nodefony 7.0.2 → 10.0.0-alpha.2

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 (903) hide show
  1. package/.ai/symbols.json +36135 -0
  2. package/LICENSE +544 -0
  3. package/README.md +316 -694
  4. package/bin/nodefony +2183 -85
  5. package/dist/client/Container.js +367 -0
  6. package/dist/client/Event.js +198 -0
  7. package/dist/client/Service.js +344 -0
  8. package/dist/client/Tools.js +162 -0
  9. package/dist/client/client/ClientKernel.js +276 -0
  10. package/dist/client/client/angular/index.js +352 -0
  11. package/dist/client/client/announce.js +212 -0
  12. package/dist/client/client/debugbar/DebugBar.js +1624 -0
  13. package/dist/client/client/debugbar/format.js +103 -0
  14. package/dist/client/client/debugbar/hmr.js +37 -0
  15. package/dist/client/client/debugbar/index.js +29 -0
  16. package/dist/client/client/debugbar/model.js +133 -0
  17. package/dist/client/client/debugbar/network.js +173 -0
  18. package/dist/client/client/debugbar/profile.js +114 -0
  19. package/dist/client/client/index.js +34 -0
  20. package/dist/client/client/react/index.js +268 -0
  21. package/dist/client/client/realtime/AdaptiveRate.js +240 -0
  22. package/dist/client/client/realtime/BrowserWsTransport.js +57 -0
  23. package/dist/client/client/realtime/RealtimeClient.js +898 -0
  24. package/dist/client/client/realtime/localEvents.js +56 -0
  25. package/dist/client/client/realtime/notice.js +125 -0
  26. package/dist/client/client/realtime/observe.js +280 -0
  27. package/dist/client/client/roles/index.js +3 -0
  28. package/dist/client/client/roles/registry.js +97 -0
  29. package/dist/client/client/roles/roles.js +91 -0
  30. package/dist/client/client/shim/events.js +88 -0
  31. package/dist/client/client/shim/util.js +20 -0
  32. package/dist/client/client/svelte/index.js +319 -0
  33. package/dist/client/client/syslog/context.js +122 -0
  34. package/dist/client/client/syslog/errors.js +49 -0
  35. package/dist/client/client/syslog/uplink.js +106 -0
  36. package/dist/client/client/vue/index.js +321 -0
  37. package/dist/client/colors.js +63 -0
  38. package/dist/client/debugbar.standalone.js +4274 -0
  39. package/dist/client/realtime/IRealtimeTransport.js +32 -0
  40. package/dist/client/realtime/JsonRpcPeer.js +251 -0
  41. package/dist/client/realtime/channelRate.js +39 -0
  42. package/dist/client/realtime/platformChannels.js +155 -0
  43. package/dist/client/syslog/Pdu.js +224 -0
  44. package/dist/client/syslog/Syslog.js +1159 -0
  45. package/dist/client/syslog/drivers/pduFlow.js +106 -0
  46. package/dist/client/syslog/drivers/pduProtocol.js +28 -0
  47. package/dist/client/syslog/logColor.js +80 -0
  48. package/dist/client/types/src/Container.d.ts +221 -0
  49. package/dist/client/types/src/Event.d.ts +171 -0
  50. package/dist/client/types/src/FileClass.d.ts +184 -0
  51. package/dist/client/types/src/Service.d.ts +165 -0
  52. package/dist/client/types/src/Tools.d.ts +116 -0
  53. package/dist/client/types/src/client/ClientKernel.d.ts +90 -0
  54. package/dist/client/types/src/client/IClientKernel.d.ts +195 -0
  55. package/dist/client/types/src/client/angular/index.d.ts +273 -0
  56. package/dist/client/types/src/client/announce.d.ts +74 -0
  57. package/dist/client/types/src/client/debugbar/DebugBar.d.ts +279 -0
  58. package/dist/client/types/src/client/debugbar/format.d.ts +48 -0
  59. package/dist/client/types/src/client/debugbar/hmr.d.ts +16 -0
  60. package/dist/client/types/src/client/debugbar/index.d.ts +30 -0
  61. package/dist/client/types/src/client/debugbar/model.d.ts +158 -0
  62. package/dist/client/types/src/client/debugbar/network.d.ts +55 -0
  63. package/dist/client/types/src/client/debugbar/profile.d.ts +99 -0
  64. package/dist/client/types/src/client/index.d.ts +47 -0
  65. package/dist/client/types/src/client/react/index.d.ts +207 -0
  66. package/dist/client/types/src/client/realtime/AdaptiveRate.d.ts +138 -0
  67. package/dist/client/types/src/client/realtime/BrowserWsTransport.d.ts +27 -0
  68. package/dist/client/types/src/client/realtime/RealtimeClient.d.ts +573 -0
  69. package/dist/client/types/src/client/realtime/localEvents.d.ts +53 -0
  70. package/dist/client/types/src/client/realtime/notice.d.ts +70 -0
  71. package/dist/client/types/src/client/realtime/observe.d.ts +284 -0
  72. package/dist/client/types/src/client/roles/index.d.ts +15 -0
  73. package/dist/client/types/src/client/roles/registry.d.ts +72 -0
  74. package/dist/client/types/src/client/roles/roles.d.ts +68 -0
  75. package/dist/client/types/src/client/shim/cli-color.d.ts +2 -0
  76. package/dist/client/types/src/client/shim/events.d.ts +39 -0
  77. package/dist/client/types/src/client/shim/util.d.ts +14 -0
  78. package/dist/client/types/src/client/svelte/index.d.ts +216 -0
  79. package/dist/client/types/src/client/syslog/context.d.ts +76 -0
  80. package/dist/client/types/src/client/syslog/errors.d.ts +46 -0
  81. package/dist/client/types/src/client/syslog/uplink.d.ts +89 -0
  82. package/dist/client/types/src/client/transport/websocket.d.ts +5 -0
  83. package/dist/client/types/src/client/vue/index.d.ts +234 -0
  84. package/dist/client/types/src/colors.d.ts +32 -0
  85. package/dist/client/types/src/config/infra.d.ts +171 -0
  86. package/dist/client/types/src/kernel/bootReport.d.ts +112 -0
  87. package/dist/client/types/src/kernel/readinessRegistry.d.ts +104 -0
  88. package/dist/client/types/src/realtime/IRealtimeSocket.d.ts +142 -0
  89. package/dist/client/types/src/realtime/IRealtimeTransport.d.ts +50 -0
  90. package/dist/client/types/src/realtime/JsonRpcPeer.d.ts +251 -0
  91. package/dist/client/types/src/realtime/RealtimeEventMap.d.ts +269 -0
  92. package/dist/client/types/src/realtime/channelRate.d.ts +55 -0
  93. package/dist/client/types/src/realtime/platformChannels.d.ts +147 -0
  94. package/dist/client/types/src/syslog/Pdu.d.ts +134 -0
  95. package/dist/client/types/src/syslog/Syslog.d.ts +482 -0
  96. package/dist/client/types/src/syslog/drivers/ILogDriver.d.ts +177 -0
  97. package/dist/client/types/src/syslog/drivers/pduFlow.d.ts +53 -0
  98. package/dist/client/types/src/syslog/drivers/pduProtocol.d.ts +20 -0
  99. package/dist/client/types/src/syslog/logColor.d.ts +59 -0
  100. package/dist/client/types/src/types/ICliKernel.d.ts +28 -0
  101. package/dist/client/types/src/types/ICommand.d.ts +14 -0
  102. package/dist/client/types/src/types/IContainer.d.ts +35 -0
  103. package/dist/client/types/src/types/IKernel.d.ts +138 -0
  104. package/dist/client/types/src/types/IMcpTool.d.ts +157 -0
  105. package/dist/client/types/src/types/IModule.d.ts +73 -0
  106. package/dist/client/types/src/types/IPage.d.ts +119 -0
  107. package/dist/client/types/src/types/IService.d.ts +62 -0
  108. package/dist/client/types/src/types/ISyslog.d.ts +58 -0
  109. package/dist/client/types/src/types/ITransport.d.ts +5 -0
  110. package/dist/client/types/src/types/globals.d.ts +47 -0
  111. package/dist/node/Cli.js +750 -0
  112. package/dist/node/Container.js +367 -0
  113. package/dist/node/Error.js +309 -0
  114. package/dist/node/Event.js +198 -0
  115. package/dist/node/FileClass.js +308 -0
  116. package/dist/node/Nodefony.js +78 -0
  117. package/dist/node/Service.js +344 -0
  118. package/dist/node/Tools.js +205 -0
  119. package/dist/node/bin/resolveLocalCli.js +10 -0
  120. package/dist/node/bundler/index.js +103 -0
  121. package/dist/node/cli/agentTargets.js +461 -0
  122. package/dist/node/cli/aiMcp.js +553 -0
  123. package/dist/node/cli/aiMcpReport.js +129 -0
  124. package/dist/node/cli/aiSync.js +323 -0
  125. package/dist/node/cli/aiSyncReport.js +142 -0
  126. package/dist/node/cli/card.js +186 -0
  127. package/dist/node/cli/cardReport.js +110 -0
  128. package/dist/node/cli/completion.js +415 -0
  129. package/dist/node/cli/create.js +805 -0
  130. package/dist/node/cli/env.js +332 -0
  131. package/dist/node/cli/envReport.js +159 -0
  132. package/dist/node/cli/execPortable.js +60 -0
  133. package/dist/node/cli/gitHooks.js +191 -0
  134. package/dist/node/cli/gitHooksReport.js +119 -0
  135. package/dist/node/cli/globalFlags.js +67 -0
  136. package/dist/node/cli/helpReport.js +288 -0
  137. package/dist/node/cli/manPage.js +165 -0
  138. package/dist/node/cli/nodefonyBin.js +61 -0
  139. package/dist/node/cli/outdated.js +144 -0
  140. package/dist/node/cli/progress.js +555 -0
  141. package/dist/node/cli/projectRoot.js +32 -0
  142. package/dist/node/cli/promptPassword.js +44 -0
  143. package/dist/node/cli/prompts.js +86 -0
  144. package/dist/node/cli/scaffold/destination.js +92 -0
  145. package/dist/node/cli/scaffold/engine.js +2156 -0
  146. package/dist/node/cli/scaffold/entityFields.js +589 -0
  147. package/dist/node/cli/scaffold/format.js +243 -0
  148. package/dist/node/cli/scaffold/interactive.js +124 -0
  149. package/dist/node/cli/scaffold/moduleLayout.js +38 -0
  150. package/dist/node/cli/scaffold/reservedEntities.js +74 -0
  151. package/dist/node/cli/scaffold/spec.js +704 -0
  152. package/dist/node/cli/scaffold/steps.js +40 -0
  153. package/dist/node/cli/scaffold/userContractSource.js +133 -0
  154. package/dist/node/cli/scaffold/versions.js +57 -0
  155. package/dist/node/cli/scaffold/writer.js +172 -0
  156. package/dist/node/cli/startMenu.js +496 -0
  157. package/dist/node/cli/symbols.js +205 -0
  158. package/dist/node/cli/sysexits.js +34 -0
  159. package/dist/node/cli/tableReport.js +184 -0
  160. package/dist/node/cli/usageReport.js +144 -0
  161. package/dist/node/colors.js +63 -0
  162. package/dist/node/command/Builder.js +153 -0
  163. package/dist/node/command/Command.js +348 -0
  164. package/dist/node/config/configProvenance.js +101 -0
  165. package/dist/node/config/defaults.js +39 -0
  166. package/dist/node/config/defineConfig.js +129 -0
  167. package/dist/node/config/defineEnv.js +264 -0
  168. package/dist/node/config/envExample.js +41 -0
  169. package/dist/node/config/envOverride.js +423 -0
  170. package/dist/node/config/infra.js +163 -0
  171. package/dist/node/config/reactivity.js +23 -0
  172. package/dist/node/config/reservedEnv.js +118 -0
  173. package/dist/node/config/schema.js +134 -0
  174. package/dist/node/config/use.js +24 -0
  175. package/dist/node/finder/File.js +49 -0
  176. package/dist/node/finder/FileResult.js +86 -0
  177. package/dist/node/finder/Finder.js +201 -0
  178. package/dist/node/finder/Result.js +45 -0
  179. package/dist/node/index.js +121 -0
  180. package/dist/node/kernel/BootConfigurationError.js +54 -0
  181. package/dist/node/kernel/CliKernel.js +630 -0
  182. package/dist/node/kernel/Kernel.js +2198 -0
  183. package/dist/node/kernel/Module.js +453 -0
  184. package/dist/node/kernel/adminPlane/adminCaller.js +98 -0
  185. package/dist/node/kernel/adminPlane/adminRbac.js +51 -0
  186. package/dist/node/kernel/adminPlane/catalog.js +158 -0
  187. package/dist/node/kernel/adminPlane/executeAdmin.js +78 -0
  188. package/dist/node/kernel/checks/deep.js +335 -0
  189. package/dist/node/kernel/checks/freshness.js +139 -0
  190. package/dist/node/kernel/checks/gating.js +231 -0
  191. package/dist/node/kernel/checks/guards.js +232 -0
  192. package/dist/node/kernel/checks/lastBoot.js +136 -0
  193. package/dist/node/kernel/checks/live.js +330 -0
  194. package/dist/node/kernel/checks/packageDeps.js +292 -0
  195. package/dist/node/kernel/checks/readiness.js +153 -0
  196. package/dist/node/kernel/checks/renderReport.js +780 -0
  197. package/dist/node/kernel/checks/report.js +421 -0
  198. package/dist/node/kernel/checks/runDoctor.js +731 -0
  199. package/dist/node/kernel/checks/surface.js +364 -0
  200. package/dist/node/kernel/checks/walk.js +100 -0
  201. package/dist/node/kernel/checks/wiring.js +450 -0
  202. package/dist/node/kernel/commands/AiMcpCommand.js +65 -0
  203. package/dist/node/kernel/commands/AiSyncCommand.js +48 -0
  204. package/dist/node/kernel/commands/BuildCommand.js +49 -0
  205. package/dist/node/kernel/commands/CardCommand.js +50 -0
  206. package/dist/node/kernel/commands/ClusterCommand.js +58 -0
  207. package/dist/node/kernel/commands/CompletionCommand.js +31 -0
  208. package/dist/node/kernel/commands/CreateCommand.js +45 -0
  209. package/dist/node/kernel/commands/DevCommand.js +99 -0
  210. package/dist/node/kernel/commands/DoctorCommand.js +115 -0
  211. package/dist/node/kernel/commands/EnvCommand.js +52 -0
  212. package/dist/node/kernel/commands/GitHooksCommand.js +47 -0
  213. package/dist/node/kernel/commands/InspectCommand.js +153 -0
  214. package/dist/node/kernel/commands/InstallCommand.js +20 -0
  215. package/dist/node/kernel/commands/MenuCommand.js +261 -0
  216. package/dist/node/kernel/commands/OutdatedCommand.js +164 -0
  217. package/dist/node/kernel/commands/ProdCommand.js +60 -0
  218. package/dist/node/kernel/commands/StatusCommand.js +30 -0
  219. package/dist/node/kernel/commands/StopCommand.js +35 -0
  220. package/dist/node/kernel/commands/SymbolsCommand.js +45 -0
  221. package/dist/node/kernel/commands/runtimeLauncher.js +95 -0
  222. package/dist/node/kernel/decorators/kernelDecorator.js +110 -0
  223. package/dist/node/kernel/injector/injector.js +137 -0
  224. package/dist/node/kernel/injector/serviceOrder.js +106 -0
  225. package/dist/node/kernel/inspect/adminSubjects.js +194 -0
  226. package/dist/node/kernel/inspect/docOutline.js +86 -0
  227. package/dist/node/kernel/lifecycleTags.js +76 -0
  228. package/dist/node/kernel/moduleConfig.js +75 -0
  229. package/dist/node/kernel/moduleGating.js +68 -0
  230. package/dist/node/kernel/readinessRegistry.js +98 -0
  231. package/dist/node/kernel/resolveModuleEntry.js +61 -0
  232. package/dist/node/mcp/caller.js +40 -0
  233. package/dist/node/mcp/guard.js +51 -0
  234. package/dist/node/mcp/protocol.js +157 -0
  235. package/dist/node/mcp/server.js +216 -0
  236. package/dist/node/mcp/tools.js +861 -0
  237. package/dist/node/oauth/authorizationServer.js +221 -0
  238. package/dist/node/oauth/protectedResource.js +293 -0
  239. package/dist/node/package.js +4 -0
  240. package/dist/node/realtime/IRealtimeTransport.js +32 -0
  241. package/dist/node/realtime/JsonRpcPeer.js +251 -0
  242. package/dist/node/realtime/RealtimeEventMap.js +15 -0
  243. package/dist/node/realtime/channelRate.js +39 -0
  244. package/dist/node/realtime/platformChannels.js +155 -0
  245. package/dist/node/runtime/GcScheduler.js +92 -0
  246. package/dist/node/runtime/RequestContext.js +76 -0
  247. package/dist/node/runtime/bearer.js +84 -0
  248. package/dist/node/runtime/engineEnvironment.js +79 -0
  249. package/dist/node/runtime/loadEnv.js +105 -0
  250. package/dist/node/runtime/pageFacets.js +85 -0
  251. package/dist/node/runtime/pageFilters.js +103 -0
  252. package/dist/node/runtime/pageGuard.js +84 -0
  253. package/dist/node/runtime/pageQuery.js +160 -0
  254. package/dist/node/runtime/pageSort.js +114 -0
  255. package/dist/node/runtime/redact.js +43 -0
  256. package/dist/node/runtime/withTimeout.js +71 -0
  257. package/dist/node/service/cluster/ClusterManager.js +202 -0
  258. package/dist/node/service/cluster/ClusterProbeAggregator.js +120 -0
  259. package/dist/node/service/cluster/ClusterRelay.js +74 -0
  260. package/dist/node/service/cluster/clusterMaster.js +65 -0
  261. package/dist/node/service/cluster/clusterMessage.js +70 -0
  262. package/dist/node/service/cluster/cpuQuota.js +59 -0
  263. package/dist/node/service/cluster/instanceProbe.js +52 -0
  264. package/dist/node/service/cluster/podEnvironment.js +48 -0
  265. package/dist/node/service/cluster/processProbe.js +70 -0
  266. package/dist/node/service/cluster/richProcessProbe.js +115 -0
  267. package/dist/node/service/cluster/topology.js +71 -0
  268. package/dist/node/service/dev/BootReporter.js +490 -0
  269. package/dist/node/service/dev/DevSupervisor.js +1004 -0
  270. package/dist/node/service/dev/bootVerdict.js +158 -0
  271. package/dist/node/service/dev/detachedStart.js +372 -0
  272. package/dist/node/service/dev/devProcess.js +1137 -0
  273. package/dist/node/service/dev/devProjects.js +138 -0
  274. package/dist/node/service/dev/devStatusReport.js +395 -0
  275. package/dist/node/service/dev/devStop.js +263 -0
  276. package/dist/node/service/fetchService.js +35 -0
  277. package/dist/node/service/gitService.js +62 -0
  278. package/dist/node/syslog/Pdu.js +224 -0
  279. package/dist/node/syslog/Syslog.js +1159 -0
  280. package/dist/node/syslog/drivers/ClusterFileLogDriver.js +114 -0
  281. package/dist/node/syslog/drivers/FileLogDriver.js +127 -0
  282. package/dist/node/syslog/drivers/ILogDriver.js +26 -0
  283. package/dist/node/syslog/drivers/LokiLogDriver.js +144 -0
  284. package/dist/node/syslog/drivers/MemoryLogDriver.js +33 -0
  285. package/dist/node/syslog/drivers/OpenSearchLogDriver.js +159 -0
  286. package/dist/node/syslog/drivers/builtinLogDrivers.js +114 -0
  287. package/dist/node/syslog/drivers/filterPdus.js +71 -0
  288. package/dist/node/syslog/drivers/logDriverRegistry.js +83 -0
  289. package/dist/node/syslog/drivers/opensearchShared.js +17 -0
  290. package/dist/node/syslog/drivers/pduFlow.js +106 -0
  291. package/dist/node/syslog/drivers/pduProtocol.js +28 -0
  292. package/dist/node/syslog/httpFetch.js +43 -0
  293. package/dist/node/syslog/logColor.js +80 -0
  294. package/dist/node/syslog/sinks/FileSink.js +142 -0
  295. package/dist/node/syslog/transports/BatchingHttpTransport.js +98 -0
  296. package/dist/node/syslog/transports/ConsoleTransport.js +14 -0
  297. package/dist/node/syslog/transports/FileTransport.js +17 -0
  298. package/dist/node/syslog/transports/HttpTransport.js +44 -0
  299. package/dist/node/syslog/transports/LokiTransport.js +80 -0
  300. package/dist/node/syslog/transports/OpenSearchTransport.js +53 -0
  301. package/dist/node/syslog/transports/SyslogTransport.js +13 -0
  302. package/dist/node/syslog/transports/index.js +8 -0
  303. package/dist/node/testing/index.js +190 -0
  304. package/dist/types/Cli.d.ts +177 -0
  305. package/dist/types/Container.d.ts +221 -0
  306. package/dist/types/Error.d.ts +162 -0
  307. package/dist/types/Event.d.ts +171 -0
  308. package/dist/types/FileClass.d.ts +184 -0
  309. package/dist/types/Nodefony.d.ts +66 -0
  310. package/dist/types/Service.d.ts +165 -0
  311. package/dist/types/Tools.d.ts +116 -0
  312. package/dist/types/bin/resolveLocalCli.d.ts +49 -0
  313. package/dist/types/bundler/index.d.ts +66 -0
  314. package/dist/types/cli/agentTargets.d.ts +378 -0
  315. package/dist/types/cli/aiMcp.d.ts +239 -0
  316. package/dist/types/cli/aiMcpReport.d.ts +113 -0
  317. package/dist/types/cli/aiSync.d.ts +68 -0
  318. package/dist/types/cli/aiSyncReport.d.ts +149 -0
  319. package/dist/types/cli/card.d.ts +49 -0
  320. package/dist/types/cli/cardReport.d.ts +127 -0
  321. package/dist/types/cli/completion.d.ts +154 -0
  322. package/dist/types/cli/create.d.ts +177 -0
  323. package/dist/types/cli/env.d.ts +103 -0
  324. package/dist/types/cli/envReport.d.ts +153 -0
  325. package/dist/types/cli/execPortable.d.ts +54 -0
  326. package/dist/types/cli/gitHooks.d.ts +48 -0
  327. package/dist/types/cli/gitHooksReport.d.ts +101 -0
  328. package/dist/types/cli/globalFlags.d.ts +48 -0
  329. package/dist/types/cli/helpReport.d.ts +110 -0
  330. package/dist/types/cli/manPage.d.ts +21 -0
  331. package/dist/types/cli/nodefonyBin.d.ts +45 -0
  332. package/dist/types/cli/outdated.d.ts +119 -0
  333. package/dist/types/cli/progress.d.ts +349 -0
  334. package/dist/types/cli/projectRoot.d.ts +19 -0
  335. package/dist/types/cli/promptPassword.d.ts +22 -0
  336. package/dist/types/cli/prompts.d.ts +102 -0
  337. package/dist/types/cli/scaffold/destination.d.ts +70 -0
  338. package/dist/types/cli/scaffold/engine.d.ts +545 -0
  339. package/dist/types/cli/scaffold/entityFields.d.ts +238 -0
  340. package/dist/types/cli/scaffold/format.d.ts +78 -0
  341. package/dist/types/cli/scaffold/interactive.d.ts +11 -0
  342. package/dist/types/cli/scaffold/moduleLayout.d.ts +69 -0
  343. package/dist/types/cli/scaffold/reservedEntities.d.ts +49 -0
  344. package/dist/types/cli/scaffold/spec.d.ts +159 -0
  345. package/dist/types/cli/scaffold/steps.d.ts +24 -0
  346. package/dist/types/cli/scaffold/userContractSource.d.ts +86 -0
  347. package/dist/types/cli/scaffold/versions.d.ts +14 -0
  348. package/dist/types/cli/scaffold/writer.d.ts +100 -0
  349. package/dist/types/cli/startMenu.d.ts +195 -0
  350. package/dist/types/cli/symbols.d.ts +84 -0
  351. package/dist/types/cli/sysexits.d.ts +31 -0
  352. package/dist/types/cli/tableReport.d.ts +44 -0
  353. package/dist/types/cli/usageReport.d.ts +108 -0
  354. package/dist/types/client/ClientKernel.d.ts +90 -0
  355. package/dist/types/client/IClientKernel.d.ts +195 -0
  356. package/dist/types/client/angular/index.d.ts +273 -0
  357. package/dist/types/client/announce.d.ts +74 -0
  358. package/dist/types/client/debugbar/DebugBar.d.ts +279 -0
  359. package/dist/types/client/debugbar/format.d.ts +48 -0
  360. package/dist/types/client/debugbar/hmr.d.ts +16 -0
  361. package/dist/types/client/debugbar/index.d.ts +30 -0
  362. package/dist/types/client/debugbar/model.d.ts +158 -0
  363. package/dist/types/client/debugbar/network.d.ts +55 -0
  364. package/dist/types/client/debugbar/profile.d.ts +99 -0
  365. package/dist/types/client/index.d.ts +47 -0
  366. package/dist/types/client/react/index.d.ts +207 -0
  367. package/dist/types/client/realtime/AdaptiveRate.d.ts +138 -0
  368. package/dist/types/client/realtime/BrowserWsTransport.d.ts +27 -0
  369. package/dist/types/client/realtime/RealtimeClient.d.ts +573 -0
  370. package/dist/types/client/realtime/localEvents.d.ts +53 -0
  371. package/dist/types/client/realtime/notice.d.ts +70 -0
  372. package/dist/types/client/realtime/observe.d.ts +284 -0
  373. package/dist/types/client/roles/index.d.ts +15 -0
  374. package/dist/types/client/roles/registry.d.ts +72 -0
  375. package/dist/types/client/roles/roles.d.ts +68 -0
  376. package/dist/types/client/shim/cli-color.d.ts +2 -0
  377. package/dist/types/client/shim/events.d.ts +39 -0
  378. package/dist/types/client/shim/util.d.ts +14 -0
  379. package/dist/types/client/svelte/index.d.ts +216 -0
  380. package/dist/types/client/syslog/context.d.ts +76 -0
  381. package/dist/types/client/syslog/errors.d.ts +46 -0
  382. package/dist/types/client/syslog/uplink.d.ts +89 -0
  383. package/dist/types/client/transport/websocket.d.ts +5 -0
  384. package/dist/types/client/vue/index.d.ts +234 -0
  385. package/dist/types/colors.d.ts +32 -0
  386. package/dist/types/command/Builder.d.ts +47 -0
  387. package/dist/types/command/Command.d.ts +268 -0
  388. package/dist/types/config/configMeta.d.ts +53 -0
  389. package/dist/types/config/configProvenance.d.ts +58 -0
  390. package/dist/types/config/defaults.d.ts +33 -0
  391. package/dist/types/config/defineConfig.d.ts +70 -0
  392. package/dist/types/config/defineEnv.d.ts +170 -0
  393. package/dist/types/config/envExample.d.ts +23 -0
  394. package/dist/types/config/envOverride.d.ts +236 -0
  395. package/dist/types/config/index.d.ts +23 -0
  396. package/dist/types/config/infra.d.ts +171 -0
  397. package/dist/types/config/reactivity.d.ts +36 -0
  398. package/dist/types/config/reservedEnv.d.ts +145 -0
  399. package/dist/types/config/schema.d.ts +113 -0
  400. package/dist/types/config/types.d.ts +374 -0
  401. package/dist/types/config/use.d.ts +80 -0
  402. package/dist/types/finder/File.d.ts +26 -0
  403. package/dist/types/finder/FileResult.d.ts +14 -0
  404. package/dist/types/finder/Finder.d.ts +51 -0
  405. package/dist/types/finder/Result.d.ts +10 -0
  406. package/dist/types/index.d.ts +205 -0
  407. package/dist/types/kernel/BootConfigurationError.d.ts +50 -0
  408. package/dist/types/kernel/CliKernel.d.ts +289 -0
  409. package/dist/types/kernel/Kernel.d.ts +1255 -0
  410. package/dist/types/kernel/Module.d.ts +326 -0
  411. package/dist/types/kernel/adminPlane/adminCaller.d.ts +105 -0
  412. package/dist/types/kernel/adminPlane/adminRbac.d.ts +44 -0
  413. package/dist/types/kernel/adminPlane/catalog.d.ts +122 -0
  414. package/dist/types/kernel/adminPlane/executeAdmin.d.ts +99 -0
  415. package/dist/types/kernel/bootReport.d.ts +112 -0
  416. package/dist/types/kernel/checks/deep.d.ts +122 -0
  417. package/dist/types/kernel/checks/freshness.d.ts +49 -0
  418. package/dist/types/kernel/checks/gating.d.ts +134 -0
  419. package/dist/types/kernel/checks/guards.d.ts +57 -0
  420. package/dist/types/kernel/checks/lastBoot.d.ts +189 -0
  421. package/dist/types/kernel/checks/live.d.ts +110 -0
  422. package/dist/types/kernel/checks/packageDeps.d.ts +44 -0
  423. package/dist/types/kernel/checks/readiness.d.ts +97 -0
  424. package/dist/types/kernel/checks/renderReport.d.ts +107 -0
  425. package/dist/types/kernel/checks/report.d.ts +414 -0
  426. package/dist/types/kernel/checks/runDoctor.d.ts +361 -0
  427. package/dist/types/kernel/checks/surface.d.ts +148 -0
  428. package/dist/types/kernel/checks/walk.d.ts +45 -0
  429. package/dist/types/kernel/checks/wiring.d.ts +54 -0
  430. package/dist/types/kernel/commands/AiMcpCommand.d.ts +39 -0
  431. package/dist/types/kernel/commands/AiSyncCommand.d.ts +28 -0
  432. package/dist/types/kernel/commands/BuildCommand.d.ts +13 -0
  433. package/dist/types/kernel/commands/CardCommand.d.ts +34 -0
  434. package/dist/types/kernel/commands/ClusterCommand.d.ts +29 -0
  435. package/dist/types/kernel/commands/CompletionCommand.d.ts +17 -0
  436. package/dist/types/kernel/commands/CreateCommand.d.ts +16 -0
  437. package/dist/types/kernel/commands/DevCommand.d.ts +40 -0
  438. package/dist/types/kernel/commands/DoctorCommand.d.ts +55 -0
  439. package/dist/types/kernel/commands/EnvCommand.d.ts +35 -0
  440. package/dist/types/kernel/commands/GitHooksCommand.d.ts +27 -0
  441. package/dist/types/kernel/commands/InspectCommand.d.ts +59 -0
  442. package/dist/types/kernel/commands/InstallCommand.d.ts +7 -0
  443. package/dist/types/kernel/commands/MenuCommand.d.ts +58 -0
  444. package/dist/types/kernel/commands/OutdatedCommand.d.ts +47 -0
  445. package/dist/types/kernel/commands/ProdCommand.d.ts +29 -0
  446. package/dist/types/kernel/commands/StatusCommand.d.ts +17 -0
  447. package/dist/types/kernel/commands/StopCommand.d.ts +17 -0
  448. package/dist/types/kernel/commands/SymbolsCommand.d.ts +29 -0
  449. package/dist/types/kernel/commands/runtimeLauncher.d.ts +76 -0
  450. package/dist/types/kernel/decorators/kernelDecorator.d.ts +76 -0
  451. package/dist/types/kernel/injector/injector.d.ts +47 -0
  452. package/dist/types/kernel/injector/serviceOrder.d.ts +29 -0
  453. package/dist/types/kernel/inspect/adminSubjects.d.ts +169 -0
  454. package/dist/types/kernel/inspect/docOutline.d.ts +53 -0
  455. package/dist/types/kernel/lifecycleTags.d.ts +79 -0
  456. package/dist/types/kernel/moduleConfig.d.ts +35 -0
  457. package/dist/types/kernel/moduleGating.d.ts +78 -0
  458. package/dist/types/kernel/readinessRegistry.d.ts +104 -0
  459. package/dist/types/kernel/resolveModuleEntry.d.ts +46 -0
  460. package/dist/types/mcp/caller.d.ts +55 -0
  461. package/dist/types/mcp/guard.d.ts +66 -0
  462. package/dist/types/mcp/protocol.d.ts +163 -0
  463. package/dist/types/mcp/server.d.ts +70 -0
  464. package/dist/types/mcp/tools.d.ts +209 -0
  465. package/dist/types/oauth/authorizationServer.d.ts +204 -0
  466. package/dist/types/oauth/protectedResource.d.ts +339 -0
  467. package/dist/types/realtime/IRealtimeSocket.d.ts +142 -0
  468. package/dist/types/realtime/IRealtimeTransport.d.ts +50 -0
  469. package/dist/types/realtime/JsonRpcPeer.d.ts +251 -0
  470. package/dist/types/realtime/RealtimeEventMap.d.ts +269 -0
  471. package/dist/types/realtime/channelRate.d.ts +55 -0
  472. package/dist/types/realtime/platformChannels.d.ts +147 -0
  473. package/dist/types/runtime/GcScheduler.d.ts +96 -0
  474. package/dist/types/runtime/RequestContext.d.ts +152 -0
  475. package/dist/types/runtime/bearer.d.ts +88 -0
  476. package/dist/types/runtime/engineEnvironment.d.ts +115 -0
  477. package/dist/types/runtime/loadEnv.d.ts +86 -0
  478. package/dist/types/runtime/pageFacets.d.ts +104 -0
  479. package/dist/types/runtime/pageFilters.d.ts +139 -0
  480. package/dist/types/runtime/pageGuard.d.ts +78 -0
  481. package/dist/types/runtime/pageQuery.d.ts +138 -0
  482. package/dist/types/runtime/pageSort.d.ts +86 -0
  483. package/dist/types/runtime/redact.d.ts +27 -0
  484. package/dist/types/runtime/withTimeout.d.ts +51 -0
  485. package/dist/types/service/cluster/ClusterManager.d.ts +112 -0
  486. package/dist/types/service/cluster/ClusterProbeAggregator.d.ts +68 -0
  487. package/dist/types/service/cluster/ClusterRelay.d.ts +57 -0
  488. package/dist/types/service/cluster/clusterMaster.d.ts +40 -0
  489. package/dist/types/service/cluster/clusterMessage.d.ts +94 -0
  490. package/dist/types/service/cluster/cpuQuota.d.ts +37 -0
  491. package/dist/types/service/cluster/instanceProbe.d.ts +82 -0
  492. package/dist/types/service/cluster/podEnvironment.d.ts +35 -0
  493. package/dist/types/service/cluster/processProbe.d.ts +53 -0
  494. package/dist/types/service/cluster/richProcessProbe.d.ts +77 -0
  495. package/dist/types/service/cluster/topology.d.ts +64 -0
  496. package/dist/types/service/dev/BootReporter.d.ts +67 -0
  497. package/dist/types/service/dev/DevSupervisor.d.ts +117 -0
  498. package/dist/types/service/dev/bootVerdict.d.ts +99 -0
  499. package/dist/types/service/dev/detachedStart.d.ts +164 -0
  500. package/dist/types/service/dev/devProcess.d.ts +638 -0
  501. package/dist/types/service/dev/devProjects.d.ts +121 -0
  502. package/dist/types/service/dev/devStatusReport.d.ts +141 -0
  503. package/dist/types/service/dev/devStop.d.ts +57 -0
  504. package/dist/types/service/fetchService.d.ts +29 -0
  505. package/dist/types/service/gitService.d.ts +24 -0
  506. package/dist/types/syslog/Pdu.d.ts +134 -0
  507. package/dist/types/syslog/Syslog.d.ts +482 -0
  508. package/dist/types/syslog/drivers/ClusterFileLogDriver.d.ts +40 -0
  509. package/dist/types/syslog/drivers/FileLogDriver.d.ts +55 -0
  510. package/dist/types/syslog/drivers/ILogDriver.d.ts +177 -0
  511. package/dist/types/syslog/drivers/LokiLogDriver.d.ts +44 -0
  512. package/dist/types/syslog/drivers/MemoryLogDriver.d.ts +20 -0
  513. package/dist/types/syslog/drivers/OpenSearchLogDriver.d.ts +40 -0
  514. package/dist/types/syslog/drivers/builtinLogDrivers.d.ts +43 -0
  515. package/dist/types/syslog/drivers/filterPdus.d.ts +21 -0
  516. package/dist/types/syslog/drivers/logDriverRegistry.d.ts +100 -0
  517. package/dist/types/syslog/drivers/opensearchShared.d.ts +11 -0
  518. package/dist/types/syslog/drivers/pduFlow.d.ts +53 -0
  519. package/dist/types/syslog/drivers/pduProtocol.d.ts +20 -0
  520. package/dist/types/syslog/httpFetch.d.ts +48 -0
  521. package/dist/types/syslog/logColor.d.ts +59 -0
  522. package/dist/types/syslog/sinks/FileSink.d.ts +78 -0
  523. package/dist/types/syslog/transports/BatchingHttpTransport.d.ts +59 -0
  524. package/dist/types/syslog/transports/ConsoleTransport.d.ts +8 -0
  525. package/dist/types/syslog/transports/FileTransport.d.ts +13 -0
  526. package/dist/types/syslog/transports/HttpTransport.d.ts +15 -0
  527. package/dist/types/syslog/transports/LokiTransport.d.ts +45 -0
  528. package/dist/types/syslog/transports/OpenSearchTransport.d.ts +40 -0
  529. package/dist/types/syslog/transports/SyslogTransport.d.ts +9 -0
  530. package/dist/types/syslog/transports/index.d.ts +12 -0
  531. package/dist/types/testing/index.d.ts +197 -0
  532. package/dist/types/types/IAdminApi.d.ts +246 -0
  533. package/dist/types/types/ICliKernel.d.ts +28 -0
  534. package/dist/types/types/ICommand.d.ts +14 -0
  535. package/dist/types/types/IContainer.d.ts +35 -0
  536. package/dist/types/types/IIdempotencyStore.d.ts +159 -0
  537. package/dist/types/types/IKernel.d.ts +138 -0
  538. package/dist/types/types/IMcpTool.d.ts +157 -0
  539. package/dist/types/types/IModule.d.ts +73 -0
  540. package/dist/types/types/IModuleManifest.d.ts +49 -0
  541. package/dist/types/types/IPage.d.ts +119 -0
  542. package/dist/types/types/IService.d.ts +62 -0
  543. package/dist/types/types/ISyslog.d.ts +58 -0
  544. package/dist/types/types/ITransport.d.ts +5 -0
  545. package/dist/types/types/globals.d.ts +47 -0
  546. package/docs/angular-services.md +318 -0
  547. package/docs/catalogue.md +184 -0
  548. package/docs/cli.md +369 -0
  549. package/docs/client.md +616 -0
  550. package/docs/debugbar.md +218 -0
  551. package/docs/environnement.md +308 -0
  552. package/docs/index.md +388 -0
  553. package/docs/kernel.md +670 -0
  554. package/docs/progression.md +228 -0
  555. package/docs/react-hooks.md +686 -0
  556. package/docs/request-context.md +507 -0
  557. package/docs/service.md +590 -0
  558. package/docs/svelte-reactivite.md +293 -0
  559. package/docs/syslog.md +848 -0
  560. package/docs/testing.md +203 -0
  561. package/docs/vue-composables.md +255 -0
  562. package/man/nodefony.1 +152 -0
  563. package/package.json +163 -83
  564. package/templates/app/agents/AGENTS.md.tpl +967 -0
  565. package/templates/app/agents/POINTEUR.md.tpl +12 -0
  566. package/templates/app/base/Dockerfile.tpl +104 -0
  567. package/templates/app/base/README.md.tpl +314 -0
  568. package/templates/app/base/dockerignore.tpl +28 -0
  569. package/templates/app/base/env.ts.tpl +130 -0
  570. package/templates/app/base/gitattributes.tpl +28 -0
  571. package/templates/app/base/github/workflows/ci.yml.tpl +91 -0
  572. package/templates/app/base/gitignore.tpl +46 -0
  573. package/templates/app/base/gitlab-ci.yml.tpl +42 -0
  574. package/templates/app/base/index.ts.tpl +69 -0
  575. package/templates/app/base/nodefony.config.ts.tpl +342 -0
  576. package/templates/app/base/oxlintrc.json.tpl +127 -0
  577. package/templates/app/base/package.json.tpl +75 -0
  578. package/templates/app/base/prettierignore.tpl +24 -0
  579. package/templates/app/base/prettierrc.json.tpl +12 -0
  580. package/templates/app/base/rolldown.config.ts.tpl +8 -0
  581. package/templates/app/base/tests/config.test.ts.tpl +29 -0
  582. package/templates/app/base/tests/e2e.setup.ts.tpl +181 -0
  583. package/templates/app/base/tests/e2e.test.ts.tpl +105 -0
  584. package/templates/app/base/tests/migrations.e2e.test.ts.tpl +579 -0
  585. package/templates/app/base/tsconfig.json.tpl +47 -0
  586. package/templates/app/base/vitest.config.ts.tpl +88 -0
  587. package/templates/app/base/vitest.e2e.config.ts.tpl +39 -0
  588. package/templates/app/complete/compose.yaml.tpl +328 -0
  589. package/templates/app/complete/deploy/migrate-job.yaml.tpl +100 -0
  590. package/templates/app/complete/docker/db/init-nodefony-e2e.sql.tpl +30 -0
  591. package/templates/app/complete/docker/grafana/provisioning/datasources/loki.yaml.tpl +15 -0
  592. package/templates/app/complete/env.local.tpl +11 -0
  593. package/templates/app/complete/env.tpl +53 -0
  594. package/templates/app/complete/nodefony/entity/User.ts.tpl +69 -0
  595. package/templates/app/complete/nodefony/security/provisionUsers.ts.tpl +124 -0
  596. package/templates/app/complete/nodefony/service/AppBannerService.ts.tpl +98 -0
  597. package/templates/app/complete/nodefony/service/AppInfoService.ts.tpl +85 -0
  598. package/templates/app/frontend/angular/frontend/src/accent.css.tpl +34 -0
  599. package/templates/app/frontend/angular/frontend/src/app/app.component.ts.tpl +400 -0
  600. package/templates/app/frontend/react/frontend/src/App.tsx.tpl +421 -0
  601. package/templates/app/frontend/react/frontend/src/accent.css.tpl +30 -0
  602. package/templates/app/frontend/shared/frontend/src/brand.ts.tpl +3 -0
  603. package/templates/app/frontend/shared/frontend/src/showcase.css.tpl +224 -0
  604. package/templates/app/frontend/shared/nodefony/controllers/AppController.ts.tpl +45 -0
  605. package/templates/app/frontend/svelte/frontend/src/App.svelte.tpl +371 -0
  606. package/templates/app/frontend/svelte/frontend/src/accent.css.tpl +34 -0
  607. package/templates/app/frontend/vue/frontend/src/App.vue.tpl +367 -0
  608. package/templates/app/frontend/vue/frontend/src/accent.css.tpl +34 -0
  609. package/templates/app/home/nodefony/controllers/HomeController.ts.tpl +38 -0
  610. package/templates/command/nodefony/command/__NAME__.ts.tpl +132 -0
  611. package/templates/controller/duplex/nodefony/controllers/__NAME__.ts.tpl +188 -0
  612. package/templates/controller/example/nodefony/controllers/__NAME__.ts.tpl +348 -0
  613. package/templates/controller/hello/nodefony/controllers/__NAME__.ts.tpl +143 -0
  614. package/templates/controller/realtime/nodefony/controllers/__NAME__.ts.tpl +153 -0
  615. package/templates/controller/realtime/tests/__KEBAB__-realtime.test.ts.tpl +79 -0
  616. package/templates/controller/rest/nodefony/controllers/__NAME__.ts.tpl +159 -0
  617. package/templates/entity/base/nodefony/entity/__PASCAL__.schema.ts.tpl +31 -0
  618. package/templates/entity/base/nodefony/entity/__PASCAL__.ts.tpl +68 -0
  619. package/templates/entity/controller/nodefony/controllers/__PASCAL__Controller.ts.tpl +335 -0
  620. package/templates/entity/service/nodefony/service/__PASCAL__Service.ts.tpl +126 -0
  621. package/templates/entity/tests/tests/__KEBAB__.e2e.test.ts.tpl +358 -0
  622. package/templates/entity/tests/tests/__KEBAB__.test.ts.tpl +87 -0
  623. package/templates/front/angular/frontend/src/app/app.component.ts.tpl +30 -0
  624. package/templates/front/base/nodefony/controllers/__NAME__.ts.tpl +60 -0
  625. package/templates/front/react/frontend/src/App.tsx.tpl +26 -0
  626. package/templates/front/svelte/frontend/src/App.svelte.tpl +19 -0
  627. package/templates/front/vue/frontend/src/App.vue.tpl +23 -0
  628. package/templates/module/ai/AGENTS.md.tpl +71 -0
  629. package/templates/module/base/README.md.tpl +62 -0
  630. package/templates/module/base/docs/index.md.tpl +55 -0
  631. package/templates/module/base/index.ts.tpl +50 -0
  632. package/templates/module/base/nodefony/config/config.ts.tpl +60 -0
  633. package/templates/module/base/nodefony/config/defineModuleConfig.ts.tpl +55 -0
  634. package/templates/module/base/nodefony/src/errors/__PASCAL__Error.ts.tpl +20 -0
  635. package/templates/module/base/package.json.tpl +53 -0
  636. package/templates/module/base/rolldown.config.ts.tpl +14 -0
  637. package/templates/module/base/tests/__KEBAB__.test.ts.tpl +39 -0
  638. package/templates/module/base/tsconfig.json.tpl +42 -0
  639. package/templates/module/base/vitest.config.ts.tpl +27 -0
  640. package/templates/module/packages/CLAUDE.md.tpl +51 -0
  641. package/templates/module/packages/MEMORY.md.tpl +39 -0
  642. package/templates/module/packages/tsconfig.declarations.json.tpl +19 -0
  643. package/templates/module/packages/tsconfig.tests.json.tpl +11 -0
  644. package/templates/module/service/nodefony/interfaces/I__PASCAL__Service.ts.tpl +14 -0
  645. package/templates/module/service/nodefony/interfaces/index.ts.tpl +1 -0
  646. package/templates/module/service/nodefony/service/__PASCAL__Service.ts.tpl +116 -0
  647. package/templates/service/nodefony/interfaces/I__PASCAL__Service.ts.tpl +19 -0
  648. package/templates/service/nodefony/service/__PASCAL__Service.ts.tpl +136 -0
  649. package/templates/service/tests/__PASCAL__Service.test.ts.tpl +53 -0
  650. package/templates/shared/front-entry/angular/frontend/src/main.ts.tpl +17 -0
  651. package/templates/shared/front-entry/react/frontend/src/main.tsx.tpl +11 -0
  652. package/templates/shared/front-entry/svelte/frontend/src/main.ts.tpl +13 -0
  653. package/templates/shared/front-entry/vue/frontend/src/main.ts.tpl +12 -0
  654. package/templates/shared/front-registrar/nodefony/frontend/register__PASCAL__Entry.ts.tpl +40 -0
  655. package/templates/shared/front-shell/frontend/index.html.tpl +39 -0
  656. package/templates/shared/ng-app-tsconfig/frontend/tsconfig.app.json.tpl +18 -0
  657. package/templates/shared/svelte-shim/frontend/src/env.d.ts.tpl +12 -0
  658. package/templates/shared/vue-shim/frontend/src/env.d.ts.tpl +15 -0
  659. package/.yarnclean +0 -45
  660. package/CHANGELOG.md +0 -406
  661. package/autoloader.es6 +0 -253
  662. package/builder.es6 +0 -366
  663. package/cli/builder/bundles/bundle.js +0 -328
  664. package/cli/builder/bundles/controller.js +0 -73
  665. package/cli/builder/bundles/users-bundle/.yarnclean +0 -45
  666. package/cli/builder/bundles/users-bundle/Command/fixtureTask.js +0 -47
  667. package/cli/builder/bundles/users-bundle/Command/usersCommand.js +0 -105
  668. package/cli/builder/bundles/users-bundle/Entity/mongoose/userEntity.js +0 -168
  669. package/cli/builder/bundles/users-bundle/Entity/sequelize/userEntity.js +0 -231
  670. package/cli/builder/bundles/users-bundle/Fixtures/users.js +0 -117
  671. package/cli/builder/bundles/users-bundle/Fixtures/usersFixtures.js +0 -114
  672. package/cli/builder/bundles/users-bundle/Resources/config/config.js +0 -90
  673. package/cli/builder/bundles/users-bundle/Resources/config/routing.js +0 -17
  674. package/cli/builder/bundles/users-bundle/Resources/config/security.js +0 -96
  675. package/cli/builder/bundles/users-bundle/Resources/config/services.js +0 -7
  676. package/cli/builder/bundles/users-bundle/Resources/config/webpack/webpack.dev.config.js +0 -13
  677. package/cli/builder/bundles/users-bundle/Resources/config/webpack/webpack.prod.config.js +0 -31
  678. package/cli/builder/bundles/users-bundle/Resources/config/webpack.config.js +0 -146
  679. package/cli/builder/bundles/users-bundle/Resources/js/users.js +0 -153
  680. package/cli/builder/bundles/users-bundle/Resources/public/favicon.ico +0 -0
  681. package/cli/builder/bundles/users-bundle/Resources/public/images/users-logo.png +0 -0
  682. package/cli/builder/bundles/users-bundle/Resources/scss/awesome/font-awesome.config.js +0 -11
  683. package/cli/builder/bundles/users-bundle/Resources/scss/custom.scss +0 -23
  684. package/cli/builder/bundles/users-bundle/Resources/scss/users.scss +0 -59
  685. package/cli/builder/bundles/users-bundle/Resources/swagger/openapi/login.js +0 -235
  686. package/cli/builder/bundles/users-bundle/Resources/swagger/openapi/users.js +0 -232
  687. package/cli/builder/bundles/users-bundle/Resources/translations/login.en_en.yml +0 -14
  688. package/cli/builder/bundles/users-bundle/Resources/translations/login.fr_fr.yml +0 -14
  689. package/cli/builder/bundles/users-bundle/Resources/translations/users.en_en.yml +0 -35
  690. package/cli/builder/bundles/users-bundle/Resources/translations/users.fr_fr.yml +0 -36
  691. package/cli/builder/bundles/users-bundle/Resources/views/base.html.twig +0 -32
  692. package/cli/builder/bundles/users-bundle/Resources/views/footer.html.twig +0 -7
  693. package/cli/builder/bundles/users-bundle/Resources/views/header.html.twig +0 -90
  694. package/cli/builder/bundles/users-bundle/Resources/views/login/login.html.twig +0 -43
  695. package/cli/builder/bundles/users-bundle/Resources/views/users/createUser.html.twig +0 -296
  696. package/cli/builder/bundles/users-bundle/Resources/views/users/readUsers.html.twig +0 -94
  697. package/cli/builder/bundles/users-bundle/controller/api/graphql/graphqlController.js +0 -56
  698. package/cli/builder/bundles/users-bundle/controller/api/graphql/userResolver.js +0 -121
  699. package/cli/builder/bundles/users-bundle/controller/api/graphql/usertype.js +0 -58
  700. package/cli/builder/bundles/users-bundle/controller/api/openapi/loginApiController.js +0 -210
  701. package/cli/builder/bundles/users-bundle/controller/api/openapi/restUserController.js +0 -244
  702. package/cli/builder/bundles/users-bundle/controller/loginController.js +0 -91
  703. package/cli/builder/bundles/users-bundle/controller/usersController.js +0 -332
  704. package/cli/builder/bundles/users-bundle/package.json +0 -58
  705. package/cli/builder/bundles/users-bundle/readme.md +0 -360
  706. package/cli/builder/bundles/users-bundle/services/usersService.js +0 -411
  707. package/cli/builder/bundles/users-bundle/src/providers/mongoose/userProvider.js +0 -47
  708. package/cli/builder/bundles/users-bundle/src/providers/sequelize/userProvider.js +0 -48
  709. package/cli/builder/bundles/users-bundle/tests/loginTest.js +0 -182
  710. package/cli/builder/bundles/users-bundle/usersBundle.js +0 -26
  711. package/cli/builder/microService/microService.js +0 -261
  712. package/cli/builder/microService/skeletons/.editorconfig +0 -18
  713. package/cli/builder/microService/skeletons/.env-cmdrc.js +0 -32
  714. package/cli/builder/microService/skeletons/.jshintrc +0 -37
  715. package/cli/builder/microService/skeletons/README.md +0 -117
  716. package/cli/builder/microService/skeletons/bin/bash/hello.sh +0 -5
  717. package/cli/builder/microService/skeletons/bin/cli +0 -13
  718. package/cli/builder/microService/skeletons/bin/python/hello.py +0 -20
  719. package/cli/builder/microService/skeletons/config/config.js +0 -29
  720. package/cli/builder/microService/skeletons/config/pm2.config.js +0 -72
  721. package/cli/builder/microService/skeletons/config/webpack/webpack.config.dev.js +0 -8
  722. package/cli/builder/microService/skeletons/config/webpack/webpack.config.prod.js +0 -25
  723. package/cli/builder/microService/skeletons/config/webpack.config.js +0 -114
  724. package/cli/builder/microService/skeletons/package.json +0 -76
  725. package/cli/builder/microService/skeletons/src/browser/index.css +0 -3
  726. package/cli/builder/microService/skeletons/src/browser/index.js +0 -16
  727. package/cli/builder/microService/skeletons/src/browser/socketio.js +0 -46
  728. package/cli/builder/microService/skeletons/src/cli/cli.js +0 -224
  729. package/cli/builder/microService/skeletons/src/cli/menu.js +0 -28
  730. package/cli/builder/microService/skeletons/src/n-api/README.md +0 -1
  731. package/cli/builder/microService/skeletons/src/n-api/binding.gyp +0 -14
  732. package/cli/builder/microService/skeletons/src/n-api/hello.cc +0 -14
  733. package/cli/builder/microService/skeletons/src/n-api/hello.js +0 -23
  734. package/cli/builder/microService/skeletons/src/n-api/package.json +0 -15
  735. package/cli/builder/microService/skeletons/src/node/examples/index.js +0 -120
  736. package/cli/builder/microService/skeletons/src/node/index.js +0 -102
  737. package/cli/builder/microService/skeletons/src/node/services/markdown/markdown.js +0 -27
  738. package/cli/builder/microService/skeletons/src/node/services/servers/http.js +0 -45
  739. package/cli/builder/microService/skeletons/src/node/services/servers/https.js +0 -44
  740. package/cli/builder/microService/skeletons/src/node/services/socketio/socketio.js +0 -46
  741. package/cli/builder/microService/skeletons/src/node/services/syscall/syscall.js +0 -111
  742. package/cli/builder/microService/skeletons/src/node/services/worker/thread.js +0 -59
  743. package/cli/builder/microService/skeletons/src/node/services/worker/worker.js +0 -60
  744. package/cli/builder/microService/skeletons/src/templates/base.html +0 -12
  745. package/cli/builder/microService/skeletons/src/templates/socket.html +0 -23
  746. package/cli/builder/microService/skeletons/tests/unit/serviceTest.js +0 -30
  747. package/cli/builder/project/project.js +0 -474
  748. package/cli/builder/project/skeletons/README.md +0 -778
  749. package/cli/builder/project/skeletons/bin/dev-deploy.sh +0 -5
  750. package/cli/builder/project/skeletons/bin/generateCertificates.sh.skeleton +0 -180
  751. package/cli/builder/project/skeletons/bin/prod-deploy.sh +0 -3
  752. package/cli/builder/project/skeletons/config/config.js.skeleton +0 -178
  753. package/cli/builder/project/skeletons/config/openssl/ca/openssl.cnf.skeleton +0 -150
  754. package/cli/builder/project/skeletons/config/openssl/ca_intermediate/openssl.cnf.skeleton +0 -152
  755. package/cli/builder/project/skeletons/config/pm2.config.js.skeleton +0 -62
  756. package/cli/builder/project/skeletons/documentation.html.twig +0 -24
  757. package/cli/builder/project/skeletons/editorconfig.skeleton +0 -18
  758. package/cli/builder/project/skeletons/eslintignore.skeleton +0 -15
  759. package/cli/builder/project/skeletons/eslintrc.js.skeleton +0 -97
  760. package/cli/builder/project/skeletons/gitignore.skeleton +0 -32
  761. package/cli/builder/project/skeletons/migrations/2022.12.25T17.37.37.entity-user.js +0 -137
  762. package/cli/builder/project/skeletons/migrations/2022.12.25T17.37.38.entity-session.js +0 -97
  763. package/cli/builder/project/skeletons/migrations/2022.12.25T17.37.39.entity-requests.js +0 -115
  764. package/cli/builder/project/skeletons/migrations/2022.12.25T17.37.40.entity-jwts.js +0 -97
  765. package/cli/builder/project/skeletons/migrations/migrations.skeleton.js +0 -101
  766. package/cli/builder/project/skeletons/package.json.twig +0 -71
  767. package/cli/builder/react/reactBuilder.js +0 -224
  768. package/cli/builder/react/skeletons/app/Resources/databases/nodefony.db +0 -0
  769. package/cli/builder/react/skeletons/app/Resources/translations/messages.en_en.yml +0 -5
  770. package/cli/builder/react/skeletons/app/Resources/translations/messages.fr_fr.yml +0 -5
  771. package/cli/builder/react/skeletons/app/Resources/views/base.html.twig +0 -33
  772. package/cli/builder/react/skeletons/app/config/config.js +0 -392
  773. package/cli/builder/react/skeletons/app/config/routing.js +0 -10
  774. package/cli/builder/react/skeletons/app/config/security.js +0 -74
  775. package/cli/builder/react/skeletons/app/controller/appController.js +0 -22
  776. package/cli/builder/sandbox/sandBoxBuilder.js +0 -610
  777. package/cli/builder/sandbox/skeletons/Resources/css/entry.css +0 -48
  778. package/cli/builder/sandbox/skeletons/Resources/js/entry.js +0 -60
  779. package/cli/builder/sandbox/skeletons/Resources/public/manifest.json +0 -32
  780. package/cli/builder/sandbox/skeletons/Resources/translations/messages.en_en.yml +0 -5
  781. package/cli/builder/sandbox/skeletons/Resources/translations/messages.fr_fr.yml +0 -5
  782. package/cli/builder/sandbox/skeletons/Resources/views/base.html.twig +0 -46
  783. package/cli/builder/sandbox/skeletons/Resources/views/index.html.twig +0 -34
  784. package/cli/builder/sandbox/skeletons/bootstrap/custom.scss +0 -27
  785. package/cli/builder/sandbox/skeletons/bootstrap/entry.scss +0 -60
  786. package/cli/builder/sandbox/skeletons/workbox/templates/index.html.twig +0 -32
  787. package/cli/builder/sandbox/skeletons/workbox/workers/service-worker.js +0 -196
  788. package/cli/builder/skeletons/Resources/public/favicon.ico +0 -0
  789. package/cli/builder/skeletons/Resources/public/images/app-logo.png +0 -0
  790. package/cli/builder/skeletons/Resources/views/framework-bundle/views/401.html.twig +0 -1
  791. package/cli/builder/skeletons/Resources/views/framework-bundle/views/401.json.twig +0 -24
  792. package/cli/builder/skeletons/Resources/views/framework-bundle/views/403.html.twig +0 -1
  793. package/cli/builder/skeletons/Resources/views/framework-bundle/views/403.json.twig +0 -20
  794. package/cli/builder/skeletons/Resources/views/framework-bundle/views/404.html.twig +0 -1
  795. package/cli/builder/skeletons/Resources/views/framework-bundle/views/404.json.twig +0 -20
  796. package/cli/builder/skeletons/Resources/views/framework-bundle/views/500.html.twig +0 -1
  797. package/cli/builder/skeletons/Resources/views/framework-bundle/views/base.html.twig +0 -30
  798. package/cli/builder/skeletons/Resources/views/framework-bundle/views/exception.html.twig +0 -1
  799. package/cli/builder/skeletons/Resources/views/framework-bundle/views/exception.json.twig +0 -24
  800. package/cli/builder/skeletons/Resources/views/framework-bundle/views/footer.html.twig +0 -33
  801. package/cli/builder/skeletons/Resources/views/framework-bundle/views/header.html.twig +0 -6
  802. package/cli/builder/skeletons/Resources/views/framework-bundle/views/index.html.twig +0 -6
  803. package/cli/builder/skeletons/Resources/views/framework-bundle/views/index.json.twig +0 -1
  804. package/cli/builder/skeletons/Resources/views/framework-bundle/views/layout.html.twig +0 -125
  805. package/cli/builder/skeletons/Resources/views/framework-bundle/views/layout.json.twig +0 -5
  806. package/cli/builder/skeletons/Resources/views/framework-bundle/views/timeout.html.twig +0 -1
  807. package/cli/builder/skeletons/Resources/views/framework-bundle/views/timeout.json.twig +0 -24
  808. package/cli/builder/skeletons/app/appKernel.js +0 -133
  809. package/cli/builder/skeletons/binding/binding.cc +0 -26
  810. package/cli/builder/skeletons/binding/binding.skeleton +0 -8
  811. package/cli/builder/skeletons/bundle/bundleClass.js +0 -37
  812. package/cli/builder/skeletons/command/commandClass.js +0 -11
  813. package/cli/builder/skeletons/command/taskClass.js +0 -20
  814. package/cli/builder/skeletons/config/config.js +0 -138
  815. package/cli/builder/skeletons/config/nodefony/elastic-bundle.js +0 -37
  816. package/cli/builder/skeletons/config/nodefony/framework-bundle.js +0 -22
  817. package/cli/builder/skeletons/config/nodefony/http-bundle.js +0 -104
  818. package/cli/builder/skeletons/config/nodefony/mail-bundle.js +0 -44
  819. package/cli/builder/skeletons/config/nodefony/mongoose-bundle.js +0 -51
  820. package/cli/builder/skeletons/config/nodefony/monitoring-bundle.js +0 -34
  821. package/cli/builder/skeletons/config/nodefony/realtime-bundle.js +0 -15
  822. package/cli/builder/skeletons/config/nodefony/redis-bundle.js +0 -44
  823. package/cli/builder/skeletons/config/nodefony/security-bundle.js +0 -25
  824. package/cli/builder/skeletons/config/nodefony/sequelize-bundle.js +0 -156
  825. package/cli/builder/skeletons/config/routing.js +0 -55
  826. package/cli/builder/skeletons/config/security.js +0 -102
  827. package/cli/builder/skeletons/config/services.js +0 -1
  828. package/cli/builder/skeletons/config/webpack/webpack.dev.config.js +0 -13
  829. package/cli/builder/skeletons/config/webpack/webpack.prod.config.js +0 -20
  830. package/cli/builder/skeletons/config/webpack.config.js +0 -148
  831. package/cli/builder/skeletons/controller/controllerClass.js +0 -75
  832. package/cli/builder/skeletons/package.json.twig +0 -57
  833. package/cli/builder/skeletons/unittest/testFile.js +0 -46
  834. package/cli/builder/vue/skeletons/Resources/public/images/app-logo.png +0 -0
  835. package/cli/builder/vue/skeletons/Resources/translations/messages.en_en.yml +0 -5
  836. package/cli/builder/vue/skeletons/Resources/translations/messages.fr_fr.yml +0 -5
  837. package/cli/builder/vue/skeletons/vue.config.js +0 -129
  838. package/cli/builder/vue/vueBuilder.js +0 -410
  839. package/cli/generate/generate.es6 +0 -103
  840. package/cli/install/install.js +0 -190
  841. package/cli/sequelize/sequelize.js +0 -94
  842. package/cli/start.js +0 -702
  843. package/cli/tools/pm2.es6 +0 -112
  844. package/cli/tools/tools.es6 +0 -97
  845. package/cli.es6 +0 -925
  846. package/container.es6 +0 -272
  847. package/error.es6 +0 -293
  848. package/fileClass.es6 +0 -242
  849. package/finder/file.es6 +0 -33
  850. package/finder/fileResult.es6 +0 -138
  851. package/finder/finder2.es6 +0 -275
  852. package/finder.es6 +0 -427
  853. package/kernel/annotations/annotations.es6 +0 -286
  854. package/kernel/api/api.es6 +0 -61
  855. package/kernel/api/graphqlApi.es6 +0 -107
  856. package/kernel/api/jsonApi.es6 +0 -177
  857. package/kernel/api/openApi.es6 +0 -139
  858. package/kernel/api/schemas/openApiSchema.js +0 -394
  859. package/kernel/babylon.es6 +0 -162
  860. package/kernel/bundle.es6 +0 -1200
  861. package/kernel/cliKernel.es6 +0 -869
  862. package/kernel/command.es6 +0 -78
  863. package/kernel/commands/nodefonyCommand.es6 +0 -247
  864. package/kernel/commands/tasks/bundlesTask.es6 +0 -59
  865. package/kernel/controllers/controller.es6 +0 -791
  866. package/kernel/fixture.es6 +0 -19
  867. package/kernel/injections/injections.es6 +0 -424
  868. package/kernel/kernel.es6 +0 -1960
  869. package/kernel/kernelWatcher.es6 +0 -387
  870. package/kernel/orm/entity.es6 +0 -59
  871. package/kernel/orm/orm.es6 +0 -139
  872. package/kernel/reader.es6 +0 -290
  873. package/kernel/security/encoder.es6 +0 -17
  874. package/kernel/security/factories/factory.es6 +0 -111
  875. package/kernel/security/factories/passeportFactory.es6 +0 -61
  876. package/kernel/security/provider.es6 +0 -58
  877. package/kernel/security/providers/chainProvider.es6 +0 -99
  878. package/kernel/security/providers/memoryProvider.es6 +0 -55
  879. package/kernel/security/providers/providerManager.es6 +0 -67
  880. package/kernel/security/providers/userEntityProvider.es6 +0 -50
  881. package/kernel/security/role.es6 +0 -12
  882. package/kernel/security/secureArea.es6 +0 -431
  883. package/kernel/security/tokens/token.es6 +0 -171
  884. package/kernel/security/user.es6 +0 -106
  885. package/kernel/serviceRealTime.es6 +0 -21
  886. package/kernel/services/connectionsService.es6 +0 -93
  887. package/kernel/services/cron/cronService.es6 +0 -123
  888. package/kernel/task.es6 +0 -80
  889. package/kernel/templates.es6 +0 -22
  890. package/kernel/templating/twig.es6 +0 -218
  891. package/kernel/tests/cliTest.js +0 -59
  892. package/kernel/tests/containerTest.js +0 -232
  893. package/kernel/tests/kernelTest.js +0 -39
  894. package/nodefony.es6 +0 -1084
  895. package/notificationsCenter.es6 +0 -118
  896. package/protocol.es6 +0 -86
  897. package/protocols/bayeux.es6 +0 -174
  898. package/protocols/jsonRpc/jsonrpc.es6 +0 -56
  899. package/result.es6 +0 -99
  900. package/service.es6 +0 -288
  901. package/syslog/pdu.es6 +0 -141
  902. package/syslog/syslog.es6 +0 -754
  903. package/watcher.es6 +0 -113
@@ -0,0 +1,2198 @@
1
+ import { Nodefony } from "../Nodefony.js";
2
+ import { logColor, resolveColorEnabled, setLogColor } from "../syslog/logColor.js";
3
+ import { extend, isSubclassOf } from "../Tools.js";
4
+ import FileClass from "../FileClass.js";
5
+ import { DEFAULT_ENGINE_ENVIRONMENT, engineModeOf } from "../runtime/engineEnvironment.js";
6
+ import Syslog, { NULL_LOG_SINK } from "../syslog/Syslog.js";
7
+ import Service from "../Service.js";
8
+ import { FileSink } from "../syslog/sinks/FileSink.js";
9
+ import { getActiveLogDriver, getLogDriverFactory, listLogDriverFactories, registerLogDriver, setActiveLogDriver } from "../syslog/drivers/logDriverRegistry.js";
10
+ import { registerBuiltinLogDrivers, resolveQueryDriver } from "../syslog/drivers/builtinLogDrivers.js";
11
+ import { isInKubernetes, shouldWarnPerPodView } from "../service/cluster/podEnvironment.js";
12
+ import "../cli/sysexits.js";
13
+ import { resolveModuleEntry, toImportSpecifier } from "./resolveModuleEntry.js";
14
+ import { isUnboundedListener, readListenerTags } from "./lifecycleTags.js";
15
+ import nodefonyError from "../Error.js";
16
+ import { clearReadinessState, writeReadinessState } from "../service/dev/devProcess.js";
17
+ import { isClusterMessage } from "../service/cluster/clusterMessage.js";
18
+ import { writeLastBoot } from "./checks/lastBoot.js";
19
+ import { applyResolvedPath, closestMatch, parseNfEnvOverrides, pathLooksSecret, readResolvedPath, resolveFailureHint } from "../config/envOverride.js";
20
+ import { gateModuleManifest } from "./moduleGating.js";
21
+ import { resolveInfra } from "../config/infra.js";
22
+ import CliKernel from "./CliKernel.js";
23
+ import Injector from "./injector/injector.js";
24
+ import Module from "./Module.js";
25
+ import { defaultAppConfig } from "../config/defaults.js";
26
+ import { isConfigDescriptor, readAppEnvOverrideReport } from "../config/defineConfig.js";
27
+ import { findSetReservedKeys } from "../config/configProvenance.js";
28
+ import { TimeoutError, withTimeout } from "../runtime/withTimeout.js";
29
+ import { BootConfigurationError } from "./BootConfigurationError.js";
30
+ import { ReadinessRegistry } from "./readinessRegistry.js";
31
+ import cluster from "node:cluster";
32
+ import fs from "node:fs";
33
+ import os from "node:os";
34
+ import path from "node:path";
35
+ //#region src/kernel/Kernel.ts
36
+ const colorLogEvent = () => logColor.cyanBgBlue("EVENT KERNEL");
37
+ const SERVER_SCHEME = {
38
+ http: "http",
39
+ https: "https",
40
+ websocket: "ws",
41
+ "websocket-secure": "wss"
42
+ };
43
+ const kernelDefaultOptions = { events: { nbListeners: 60 } };
44
+ const DEFAULT_SHUTDOWN_DEADLINE = 15e3;
45
+ const SHUTDOWN_DEADLINE = Symbol("shutdown-deadline");
46
+ const SHUTDOWN_DRAIN_ERROR = Symbol("shutdown-drain-error");
47
+ /**
48
+ * Dérogation au gating `policy:"dev"` en production — posée à `1`, les modules de
49
+ * développement sont chargés malgré le mode.
50
+ *
51
+ * Existe pour éprouver un runtime de production AVEC les briques de banc (une suite
52
+ * d'intégration interroge des routes qu'un module `policy:"dev"` porte, et qui
53
+ * n'existent donc pas là-bas : tout répond 404). Réservée aux bancs et au
54
+ * diagnostic — chaque module chargé par dérogation est journalisé en WARNING, et
55
+ * la variable n'a **aucun effet** hors production.
56
+ */
57
+ const FORCE_DEV_MODULES_ENV = "NF_WITH_DEV_MODULES";
58
+ /**
59
+ * Durée de vie d'un runtime de production qui a dérogé au gating des modules dev.
60
+ *
61
+ * La variable seule ne protège de rien : elle s'oublie, dans une image, un manifeste
62
+ * de déploiement, un fichier d'environnement recopié — et le jour où elle survit à
63
+ * une mise en production, plus rien ne le dit. Un runtime en dérogation **s'arrête
64
+ * donc tout seul** : l'oubli devient un incident immédiat et lisible plutôt qu'une
65
+ * surface offerte pour des mois. Large devant toute suite de tests (les tâches
66
+ * d'intégration continue du dépôt expirent à 20 minutes), dérisoire devant la durée
67
+ * de vie d'un déploiement.
68
+ */
69
+ const FORCE_DEV_MODULES_TTL_MS = 18e5;
70
+ /**
71
+ * Plafond DUR de cette durée de vie. Un banc de charge légitime dure plus qu'une
72
+ * suite d'intégration, d'où le réglage ci-dessous — mais aucune valeur ne désarme
73
+ * la minuterie : une protection qu'on peut annuler par la même variable que celle
74
+ * qu'elle protège n'en est pas une.
75
+ */
76
+ const FORCE_DEV_MODULES_TTL_MAX_MS = 144e5;
77
+ /** Variable de réglage de la durée de vie, en MINUTES (bornée par le plafond dur). */
78
+ const FORCE_DEV_MODULES_TTL_ENV = "NF_WITH_DEV_MODULES_TTL_MIN";
79
+ /**
80
+ * Durée de vie effective d'un runtime en dérogation — fonction PURE.
81
+ *
82
+ * @param env - environnement à lire (injecté : la règle s'éprouve sans toucher au process).
83
+ * @returns la durée en millisecondes, toujours comprise entre le défaut et le plafond.
84
+ */
85
+ function resolveDevModulesTtlMs(env) {
86
+ const raw = Number.parseInt(env["NF_WITH_DEV_MODULES_TTL_MIN"] ?? "", 10);
87
+ if (!Number.isFinite(raw) || raw <= 0) return FORCE_DEV_MODULES_TTL_MS;
88
+ return Math.min(Math.max(raw * 6e4, FORCE_DEV_MODULES_TTL_MS), FORCE_DEV_MODULES_TTL_MAX_MS);
89
+ }
90
+ const Events = Object.freeze({
91
+ onInit: 1,
92
+ onPreStart: 2,
93
+ onStart: 4,
94
+ onPreRegister: 8,
95
+ onRegister: 16,
96
+ onPreBoot: 32,
97
+ onBoot: 64,
98
+ onReady: 128,
99
+ onServersReady: 256,
100
+ onPostReady: 512,
101
+ onTerminate: 1024
102
+ });
103
+ /** Profil par défaut — équivaut à l'ancien `type = "CONSOLE"` (one-shot, sans serveur). */
104
+ const CONSOLE_RUN_PROFILE = Object.freeze({
105
+ servers: false,
106
+ lifetime: "oneshot",
107
+ interactive: false
108
+ });
109
+ /**
110
+ * Combien de messages CRITIC un bilan de démarrage retient.
111
+ *
112
+ * Trois : au-delà, ce sont presque toujours des conséquences du premier, et un
113
+ * bilan qu'on ne lit pas ne vaut pas mieux qu'un bilan muet.
114
+ */
115
+ const MAX_BOOT_CRITICALS = 3;
116
+ /** Longueur d'un message retenu — au-delà, la ligne déborde tout rendu. */
117
+ const MAX_BOOT_CRITICAL_LENGTH = 200;
118
+ /**
119
+ * Orchestrateur central de Nodefony — gère le boot, les modules, le DI Container racine,
120
+ * et expose les events lifecycle auxquels services/modules se branchent.
121
+ *
122
+ * Hérite de {@link Service} → bénéficie DI, EventEmitter, Syslog. Pollue le singleton
123
+ * `Nodefony.#kernel` au constructor (`Nodefony.setKernel(this)`) — isoler les tests avec
124
+ * un mock minimal.
125
+ *
126
+ * **Lifecycle phases** (chronologiques, jamais régressives) :
127
+ * `started → preRegistered → registered → booted → ready → postReady`
128
+ *
129
+ * **Events bitmask** (`progress` = OR cumulatif) :
130
+ * - `onInit=1`, `onPreStart=2`, `onStart=4`
131
+ * - `onPreRegister=8`, `onRegister=16`
132
+ * - `onPreBoot=32`, `onBoot=64`
133
+ * - `onReady=128`, `onServersReady=256`, `onPostReady=512`
134
+ * - `onTerminate=1024`
135
+ *
136
+ * **Chaîne async** (chaque maillon appelle le suivant si `!setCommandComplete`) :
137
+ * `start() → preRegister() → boot() → onReady() → initServers()`
138
+ *
139
+ * @example
140
+ * ```ts
141
+ * import { CliKernel } from "nodefony";
142
+ * const cli = new CliKernel("development");
143
+ * await cli.start(); // → new Kernel(env, cli) → kernel.start() → boot complet
144
+ * ```
145
+ */
146
+ var Kernel = class extends Service {
147
+ Events = Events;
148
+ runProfile;
149
+ version = "1.0.0";
150
+ started = false;
151
+ booted = false;
152
+ ready = false;
153
+ postReady = false;
154
+ /**
155
+ * Composants qui RETIENNENT la mise en service (cf `readinessRegistry.ts`).
156
+ *
157
+ * `null` tant que personne ne s'inscrit — et de nouveau `null` quand le
158
+ * dernier contributeur se retire : la sonde `/readyz` est sur le chemin le
159
+ * plus chaud du framework, elle ne doit rien payer pour un mécanisme dont
160
+ * aucune application ne se sert. Lire {@link Kernel.readinessBlocked}.
161
+ */
162
+ readiness = null;
163
+ /**
164
+ * Supprime les bannières serveurs « Server Listen on… » (`showBanner`) — posé
165
+ * par le `BootReporter` animé (dev TTY non-debug) : le bloc « ✓ Prêt » liste
166
+ * déjà les URLs, ces logs feraient doublon. Reste `false` en prod/CI/`--debug`
167
+ * → bannières affichées (aucun changement hors écran de boot animé).
168
+ */
169
+ suppressBootBanners = false;
170
+ trunk = null;
171
+ _appEntry = void 0;
172
+ core = false;
173
+ command = null;
174
+ commandArgs = [];
175
+ preRegistered = false;
176
+ registered = false;
177
+ app = null;
178
+ cli = null;
179
+ /**
180
+ * Mode MOTEUR par défaut, quand RIEN ne le dit.
181
+ *
182
+ * 🔴 `development`, et c'est un choix de sûreté — pas de confort. Le défaut
183
+ * ne s'applique que si `NODE_ENV` est absent ET que la commande n'exprime
184
+ * aucune intention. Or cette information ne manque JAMAIS en production : le
185
+ * `Dockerfile` généré pose `ENV NODE_ENV=production`, tout orchestrateur pose
186
+ * la variable, et les trois lanceurs posent leur mode eux-mêmes
187
+ * (`ProdCommand`, `DevCommand`, `ClusterCommand`). Elle ne manque que sur un
188
+ * poste de développement — le défaut doit donc protéger CE cas.
189
+ *
190
+ * L'ancien défaut `production` a produit trois défauts constatés : un jeton
191
+ * émis contre la mauvaise configuration, une inspection qui montrait les
192
+ * modules de production sur une machine de développement, et un contrôle de
193
+ * banc qui comparait deux applications différentes. Aucun n'a levé d'erreur :
194
+ * deviner « production » là où l'on est manifestement en développement est
195
+ * une dégradation SILENCIEUSE, ce que ce framework s'interdit.
196
+ *
197
+ * Il était de surcroît incohérent avec lui-même : la cascade `.env` ne
198
+ * chargeait alors NI `.env.production` ni `.env.development` (le lanceur
199
+ * rendait `undefined`), si bien que l'application se déclarait en production
200
+ * sans en avoir la configuration.
201
+ */
202
+ environment = DEFAULT_ENGINE_ENVIRONMENT;
203
+ debug = false;
204
+ appEnvironment = { environment: process.env.NODE_ENV };
205
+ path = process.cwd();
206
+ typeCluster = this.clusterIsMaster() ? "master" : "worker";
207
+ pid = process.pid;
208
+ workerId = cluster.worker?.id;
209
+ worker = cluster.worker;
210
+ console = this.isConsole();
211
+ /**
212
+ * Vrai terminal disponible ? Résolu UNE fois dans le constructor (volet
213
+ * « environnement », cf {@link IKernel.isTTY}). Surchargeable via `NF_NO_TTY` (test/CI).
214
+ */
215
+ isTTY = process.env.NF_NO_TTY ? false : process.stdout?.isTTY === true;
216
+ /**
217
+ * Timer no-op ref'd gardant l'event loop vivant pendant un {@link park} `keepAlive`
218
+ * (daemon CONSOLE sans socket). `null` tant qu'aucun park alive — lazy. Nettoyé par
219
+ * {@link terminate}.
220
+ */
221
+ parkTimer = null;
222
+ /**
223
+ * Le shutdown en cours, s'il y en a un — garde de ré-entrance de
224
+ * {@link terminate}. `null` par défaut : aucune allocation tant qu'on ne
225
+ * termine pas, et un kernel ne termine qu'une fois.
226
+ */
227
+ terminating = null;
228
+ /** Minuterie d'auto-arrêt d'un runtime en dérogation de modules dev (`null` = aucune). */
229
+ devModulesStopTimer = null;
230
+ node_start = process.env.NF_START || this.options.node_start;
231
+ platform = process.platform;
232
+ projectName = "NODEFONY";
233
+ uptime = (/* @__PURE__ */ new Date()).getTime();
234
+ numberCpu = os.cpus().length;
235
+ modules = {};
236
+ tmpDir;
237
+ /**
238
+ * Répertoire des données runtime **persistées** (`<path>/var`) — base commune des
239
+ * stores fichier (passkeys, TOTP, sessions) et bases SQLite. Gitignoré comme
240
+ * `tmpDir` → garanti créé au boot. Distinct de `tmpDir` (éphémère).
241
+ */
242
+ varDir;
243
+ interfaces;
244
+ domain = "localhost";
245
+ progress = Events.onInit;
246
+ injector;
247
+ /**
248
+ * Transports d'ÉCRITURE montés par {@link initializeLog} (drivers du Log
249
+ * Backplane). `initializeLog` est ré-entrant (logger précoce dans `start()`,
250
+ * puis re-init avec la config app dans `loadApp()`) : on retire ces transports
251
+ * AVANT d'en monter de nouveaux, sinon chaque `FileTransport` (nouvelle instance
252
+ * → dédup `addTransport` par référence inopérante) s'accumule et chaque log est
253
+ * écrit N× dans le JSONL. Lazy (`null` tant qu'aucun transport monté).
254
+ */
255
+ _mountedLogTransports = null;
256
+ isDev = false;
257
+ isProd = true;
258
+ /**
259
+ * En dev, `BootReporter` (checklist animée) prend la main sur l'en-tête de boot
260
+ * (splash + banner `SERVER`). Quand `true`, `initCluster()` ne ré-imprime PAS le
261
+ * banner `logEnv()` (le reporter le place lui-même, dans le bon ordre). Dev-only,
262
+ * boot-only — 0 impact runtime/requête.
263
+ */
264
+ reporterOwnsHeader = false;
265
+ pendingModuleAddLogs = [];
266
+ /**
267
+ * Échecs de boot non fatals (fail-soft) agrégés pour le {@link IBootReport}.
268
+ * **Lazy** : reste `null` tant qu'aucun module/hook n'échoue → 0 allocation sur
269
+ * un boot nominal (règle perf core).
270
+ */
271
+ bootFailures = null;
272
+ /**
273
+ * Modules du manifeste volontairement NON chargés (gating `policy`/`when` de
274
+ * {@link resolveModuleEntries}), avec leur raison — un gating silencieux se lit
275
+ * comme un module perdu. **Lazy** : `null` si rien n'est gaté (cas nominal).
276
+ */
277
+ modulesGated = null;
278
+ /**
279
+ * Compteurs WARNING/ERROR du journal de boot, **figés** quand `postReady` passe
280
+ * true (après, le ring syslog mélange boot et runtime). `null` = boot en cours
281
+ * → {@link getBootReport} compte à la volée.
282
+ */
283
+ bootLogCounts = null;
284
+ /**
285
+ * Serveurs réellement en écoute, figés à `onPostReady`. `null` tant que le boot
286
+ * n'a pas atteint cette phase ; `[]` si profil serveur mais rien n'écoute (cas
287
+ * du garde-fou 0-serveur).
288
+ */
289
+ bootServers = null;
290
+ /** Horodatage (`Date.now()`) du début de `start()` — base de `durationMs`. */
291
+ bootStartedAt = 0;
292
+ /**
293
+ * Lignes de détail de boot par phase (canal NEUTRE) : un module pousse via
294
+ * {@link reportBootLine} ce qu'il veut voir RACONTÉ sous sa phase (ex. un
295
+ * adapter ORM : « drizzle → sqlite »). Le core reste agnostique du contenu.
296
+ * Lazy : `null` tant qu'aucune ligne (dev-only en pratique).
297
+ */
298
+ bootLines = null;
299
+ /**
300
+ * Construit le Kernel. **Side effect critique** : appelle `Nodefony.setKernel(this)` →
301
+ * écrase le singleton global. Isoler les tests avec un mock minimal pour éviter de
302
+ * polluer les autres tests qui dépendent de `Nodefony.getKernel()`.
303
+ *
304
+ * Récupère le container du CLI si présent, sinon en crée un nouveau. Initialise les
305
+ * interfaces réseau OS pour `setDomain()`. Fire `"onInit"` à la fin.
306
+ *
307
+ * @param environment - mode MOTEUR (`"development"` / `"production"`, ou leurs
308
+ * abrégés `"dev"` / `"prod"`). ⚠️ Ni `"test"` ni `"staging"` : `test` est une valeur
309
+ * de `NODE_ENV` normalisée en `runtimeEnv` (→ `ConfigContext.isTest`), et un staging
310
+ * tourne EN mode `production` — il se distingue par `APP_ENV`, pas par son moteur.
311
+ * @param cli - kernel CLI parent (fournit container, packageManager, commander). Peut être null.
312
+ * @param options - options surchargées (events.nbListeners, log, etc.).
313
+ */
314
+ constructor(environment, cli, options) {
315
+ const container = cli?.container;
316
+ const mergedOptions = extend({}, kernelDefaultOptions, options);
317
+ super("KERNEL", container, void 0, mergedOptions);
318
+ this.environment = environment;
319
+ if (process.env.NF_KERNEL_TRACE_FILE) try {
320
+ fs.appendFileSync(process.env.NF_KERNEL_TRACE_FILE, `${process.pid}:${environment}\n`);
321
+ } catch {}
322
+ this.setMaxListeners(mergedOptions.events?.nbListeners ?? 60);
323
+ Nodefony.setKernel(this);
324
+ this.kernel = this;
325
+ this.set("kernel", this);
326
+ this.runProfile = { ...CONSOLE_RUN_PROFILE };
327
+ this.cli = this.setCli(cli);
328
+ this.interfaces = this.getNetworkInterfaces();
329
+ this.injector = new Injector(this);
330
+ this.set("injector", this.injector);
331
+ this.fire("onInit", this);
332
+ }
333
+ /**
334
+ * Point d'entrée du boot — et POINT DE PASSAGE UNIQUE de l'échec de démarrage.
335
+ *
336
+ * Le déroulé vit dans {@link startBoot} ; cette enveloppe ne fait qu'une
337
+ * chose : figer le bilan (`var/last-boot.json`) quand le démarrage est
338
+ * abandonné, puis relancer l'erreur inchangée. Le bilan d'un démarrage
339
+ * ABOUTI est écrit ailleurs — au moment où il est complet (cf
340
+ * {@link captureBootServers} / `onPostReady`), pas ici.
341
+ *
342
+ * Pourquoi l'échec ICI et pas dans les `catch` du pipeline : ils sont une
343
+ * douzaine, et chacun relance. Y répartir l'écriture donnerait autant
344
+ * d'implémentations d'une même règle — dont certaines seraient oubliées au
345
+ * premier ajout de phase, sans que rien ne le signale. Un seul point ne peut
346
+ * pas diverger de lui-même.
347
+ *
348
+ * Et pourquoi RIEN n'est effacé sur un succès : une commande console
349
+ * (`inspect`, une commande de module) démarre et réussit sans jamais monter
350
+ * de serveur. Effacer ici ferait disparaître le bilan d'un échec applicatif
351
+ * au premier `nodefony inspect` lancé pour le diagnostiquer — l'outil de
352
+ * diagnostic détruirait la preuve qu'il vient chercher.
353
+ *
354
+ * @returns `this` après boot complet.
355
+ * @throws Toute exception du pipeline est loggée CRITIC puis re-throw — le
356
+ * bilan n'en absorbe aucune.
357
+ */
358
+ async start() {
359
+ try {
360
+ return await this.startBoot();
361
+ } catch (e) {
362
+ this.traceFatalBootFailure(e);
363
+ throw e;
364
+ }
365
+ }
366
+ /**
367
+ * Traduit le bitmask {@link progress} en NOM de la dernière phase atteinte.
368
+ *
369
+ * C'est l'information la plus discriminante d'une trace d'échec : elle situe
370
+ * le défaut dans le cycle de vie (« mort à `onBoot` » désigne les modules,
371
+ * « mort à `onReady` » désigne les serveurs) sans qu'on ait à lire une pile.
372
+ *
373
+ * @returns le nom de la phase la plus avancée, `"onInit"` par défaut.
374
+ */
375
+ lastReachedPhase() {
376
+ let reached = "onInit";
377
+ for (const [name, bit] of Object.entries(Events)) if (this.progress & bit) reached = name;
378
+ return reached;
379
+ }
380
+ /**
381
+ * Socle commun des deux bilans (abouti et abandonné) — ce qui est vrai dans
382
+ * les deux cas se remplit ICI, une seule fois.
383
+ *
384
+ * @param status - issue du démarrage.
385
+ * @returns le bilan, prêt à être complété par l'appelant.
386
+ */
387
+ baseLastBoot(status) {
388
+ return {
389
+ status,
390
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
391
+ profile: this.lastBootProfile(),
392
+ command: this.command?.name,
393
+ environment: this.environment ?? "unknown",
394
+ pid: process.pid,
395
+ node: process.version,
396
+ phase: this.lastReachedPhase()
397
+ };
398
+ }
399
+ /**
400
+ * QUI démarre — la distinction qui empêche `doctor` de désigner le mauvais
401
+ * coupable.
402
+ *
403
+ * Un `nodefony inspect` ou un `orm:migrate` boote en console et échoue
404
+ * souvent pour une raison qui ne concerne pas le serveur : un environnement
405
+ * absent du terminal où l'on tape. Sans ce champ, son bilan s'écrivait
406
+ * par-dessus celui du serveur, et le diagnostic envoyait chercher là où il
407
+ * n'y a rien.
408
+ *
409
+ * `cluster` est distingué de `server` parce que le maître ne sert AUCUNE
410
+ * requête lui-même : ses workers le font. Le critère se CONSTATE — la
411
+ * commande `cluster` dans le process primaire ; un worker forké, lui, porte
412
+ * `cluster.isWorker` et sert bien des requêtes, donc c'est un `server`.
413
+ *
414
+ * @returns le profil de ce démarrage.
415
+ */
416
+ lastBootProfile() {
417
+ if (!this.runProfile?.servers) return "console";
418
+ if (this.command?.name === "cluster" && cluster.isPrimary) return "cluster";
419
+ return "server";
420
+ }
421
+ /**
422
+ * Fige le bilan d'un démarrage ABOUTI.
423
+ *
424
+ * Ce n'est pas une redondance avec le journal : le bilan retient ce que le
425
+ * terminal a montré une fois puis perdu — les briques ignorées AVEC leur
426
+ * raison, celles que le gating a écartées, les comptes d'avertissements. Une
427
+ * application qui démarre amputée ne se signale plus après sa première
428
+ * seconde de vie ; c'est justement le cas que personne ne diagnostique.
429
+ *
430
+ * @param report - bilan de boot figé (serveurs capturés).
431
+ */
432
+ writeBootSummary(report) {
433
+ const entry = this.baseLastBoot("ok");
434
+ entry.durationMs = report.durationMs;
435
+ entry.healthy = report.healthy;
436
+ entry.modulesLoaded = report.modulesLoaded;
437
+ entry.warnings = report.warnings;
438
+ entry.errors = report.errors;
439
+ entry.criticals = report.criticals ?? void 0;
440
+ entry.remediation = report.remediation ?? void 0;
441
+ if (report.modulesSkipped.length) entry.bricksSkipped = report.modulesSkipped.map((f) => ({
442
+ module: f.module,
443
+ reason: f.reason,
444
+ phase: f.phase
445
+ }));
446
+ if (report.modulesGated.length) entry.bricksGated = report.modulesGated.map((g) => ({
447
+ module: g.module,
448
+ reason: g.reason
449
+ }));
450
+ if (report.serversListening.length) entry.serversListening = report.serversListening.map((s) => `${s.type}${s.port ? `:${s.port}` : ""}`);
451
+ writeLastBoot(this.path, entry);
452
+ }
453
+ /**
454
+ * Consigne le démarrage ABANDONNÉ, sans jamais pouvoir aggraver la panne.
455
+ *
456
+ * À ne pas confondre avec {@link recordBootFailure}, qui collecte les échecs
457
+ * **non fatals** (fail-soft) d'un boot qui, lui, continue. Les deux se
458
+ * complètent, et c'est pourquoi le bilan embarque le second : quand le boot
459
+ * meurt, savoir quelles briques avaient DÉJÀ été ignorées avant désigne
460
+ * souvent la cause réelle — un module optionnel tombé emporte plus loin celle
461
+ * qui en dépendait, et seul le premier incident l'explique.
462
+ *
463
+ * @param e - l'erreur qui a fait abandonner le boot.
464
+ */
465
+ traceFatalBootFailure(e) {
466
+ const err = e instanceof Error ? e : new Error(String(e));
467
+ const entry = this.baseLastBoot("failed");
468
+ entry.error = {
469
+ message: err.message,
470
+ name: err.name,
471
+ exitCode: err.exitCode,
472
+ stack: err.stack
473
+ };
474
+ if (this.bootFailures?.length) entry.bricksSkipped = this.bootFailures.map((f) => ({
475
+ module: f.module,
476
+ reason: f.reason,
477
+ phase: f.phase
478
+ }));
479
+ const { criticals } = this.countBootLogIssues();
480
+ if (criticals) entry.criticals = criticals;
481
+ writeLastBoot(this.path, entry);
482
+ }
483
+ /**
484
+ * Déroulé du boot. Fire `"onPreStart"` puis `"onStart"`, charge l'application
485
+ * (`loadApp()`), instancie services kernel (Rollup), puis enchaîne sur
486
+ * `preRegister()` → `boot()` → `onReady()` → `initServers()`.
487
+ *
488
+ * Si `command.kernelEvent` matche la phase atteinte → {@link finishOrPark} immédiat
489
+ * (la command a fini son boulot : terminate one-shot OU park daemon long-running) —
490
+ * même sémantique de sortie à TOUTES les phases d'arrêt.
491
+ *
492
+ * Appelé UNIQUEMENT par {@link start}, qui porte la trace d'échec.
493
+ *
494
+ * @returns `this` après boot complet.
495
+ * @throws Toute exception du pipeline est loggée CRITIC puis re-throw.
496
+ */
497
+ async startBoot() {
498
+ if (this.bootStartedAt === 0) this.bootStartedAt = Date.now();
499
+ this.debug = Boolean(this.cli?.commander?.opts().debug) || false;
500
+ this.trunk = await this.isTrunk();
501
+ this.initializeLog();
502
+ if (!this.trunk && this.cli) {
503
+ const hint = this.diagnoseUnbootableProject();
504
+ if (hint) {
505
+ this.log(hint, "CRITIC");
506
+ return await this.terminate(1);
507
+ }
508
+ if (!this.isTTY) {
509
+ this.log(`Pas de projet Nodefony ici (${this.path}) : aucune entrée d'app résolue (package.json \`main\`, dist/index.js ou index.js). Vérifie le répertoire de travail et le build (dist/).`, "CRITIC");
510
+ return await this.terminate(1);
511
+ }
512
+ return await this.cli.runCommandAsync("menu", ["-i"]).then(() => {
513
+ if (this.command) return this.command?.action(...this.commandArgs).then(() => {
514
+ return this;
515
+ });
516
+ return this;
517
+ }).catch((e) => {
518
+ this.log(e, "ERROR");
519
+ throw e;
520
+ });
521
+ }
522
+ const devSplash = this.environment === "development" && process.env.NF_DEV_CHILD === "1";
523
+ if (this.cli && devSplash) {
524
+ await this.cli.showAsciify(this.projectName).catch((e) => {
525
+ this.log(e, "WARNING");
526
+ });
527
+ this.printDevHeader();
528
+ this.reporterOwnsHeader = true;
529
+ }
530
+ const tmpPath = path.resolve(process.cwd(), "tmp");
531
+ fs.mkdirSync(tmpPath, { recursive: true });
532
+ this.tmpDir = new FileClass(tmpPath);
533
+ const varPath = path.resolve(this.path, "var");
534
+ fs.mkdirSync(varPath, { recursive: true });
535
+ this.varDir = new FileClass(varPath);
536
+ if (!this.started) {
537
+ await this.fireAsync("onPreStart", this).catch((e) => {
538
+ this.log(e, "CRITIC");
539
+ throw e;
540
+ });
541
+ if (this.setCommandComplete(Events.onPreStart)) return this.finishOrPark(0);
542
+ await this.loadApp().catch((e) => {
543
+ if (!e.presented) this.log(e, "CRITIC");
544
+ throw e;
545
+ });
546
+ this.domain = this.setDomain();
547
+ if (this.app) this.projectName = this.app.getModuleName();
548
+ return this.fireAsync("onStart", this).then(async () => {
549
+ this.started = true;
550
+ if (this.cli) this.runProfile = this.cli.runProfile;
551
+ if (this.setCommandComplete(Events.onStart)) return this.finishOrPark(0);
552
+ return this.preRegister();
553
+ }).catch((e) => {
554
+ if (e.message !== "(outputHelp)") this.log(e, "CRITIC");
555
+ throw e;
556
+ });
557
+ }
558
+ return this;
559
+ }
560
+ /**
561
+ * Phase pré-registration — fire `"onPreRegister"` puis `"onRegister"`. Le manifeste
562
+ * `config.modules` est consommé ici (chargement via {@link loadModulesFromManifest}).
563
+ *
564
+ * @returns `this` ou chaîne sur `boot()`.
565
+ */
566
+ async preRegister() {
567
+ await this.fireLifecycle("onPreRegister", this);
568
+ if (this.resolveRuntimeEnv(this.cli?.environment) === "development" || !this.runProfile?.servers) this.cli?.writeCompletionManifest().catch(() => {});
569
+ if (this.setCommandComplete(Events.onPreRegister)) return this.finishOrPark(0);
570
+ this.preRegistered = true;
571
+ if (this.cli) {
572
+ this.debug = Boolean(this.cli?.commander?.opts().debug) || false;
573
+ this.setEnv(this.cli.environment);
574
+ this.cli.setProcessTitle(this.projectName.toLowerCase());
575
+ }
576
+ this.setNodeEnv(this.environment);
577
+ this.initCluster();
578
+ this.applyModuleConfigOverrides();
579
+ return this.fireLifecycle("onRegister", this).then(() => {
580
+ this.registered = true;
581
+ this.warnReservedConfigKeys();
582
+ if (this.setCommandComplete(Events.onRegister)) return this.finishOrPark(0);
583
+ return this.boot().catch((e) => {
584
+ throw e;
585
+ });
586
+ }).catch((e) => {
587
+ throw e;
588
+ });
589
+ }
590
+ fixCommanderCli(version = true, debug = false) {
591
+ if (this.cli && this.cli.commander && this.cli.commander?.options.length) {
592
+ if (version) {
593
+ if (this.cli?.commander?.options.some((opt) => opt.short === "-v" || opt.long === "--version")) {
594
+ const index = this.cli.commander.options.findIndex((value) => {
595
+ if (value.flags === "-v, --version") return value;
596
+ });
597
+ if (index >= 0) this.cli.commander.options.splice(index, 1);
598
+ }
599
+ }
600
+ if (debug) {
601
+ if (this.cli?.commander?.options.some((opt) => opt.short === "-d" || opt.long === "--debug")) {
602
+ const index = this.cli.commander.options.findIndex((value) => {
603
+ if (value.flags === "-d, --debug") return value;
604
+ });
605
+ if (index >= 0) this.cli.commander.options.splice(index, 1);
606
+ }
607
+ }
608
+ }
609
+ }
610
+ /**
611
+ * Phase boot — fire `"onPreBoot"` puis `"onBoot"`. Les services kernel (router, certificats,
612
+ * sessions, http-kernel) sont créés ici via les décorateurs `@services`.
613
+ *
614
+ * @returns `this` ou chaîne sur `onReady()`.
615
+ */
616
+ async boot() {
617
+ await this.fireLifecycle("onPreBoot", this);
618
+ if (this.setCommandComplete(Events.onPreBoot)) return this.finishOrPark(0);
619
+ return this.fireLifecycle("onBoot", this).then(() => {
620
+ this.booted = true;
621
+ if (this.setCommandComplete(Events.onBoot)) return this.finishOrPark(0);
622
+ return this.onReady().catch((e) => {
623
+ throw e;
624
+ });
625
+ }).catch((e) => {
626
+ throw e;
627
+ });
628
+ }
629
+ /**
630
+ * Phase ready — fire `"onReady"`, démarre les serveurs HTTP/WS via `initServers()`, log
631
+ * memoryUsage, puis fire `"onPostReady"`. C'est ici que les Server Listen apparaissent.
632
+ *
633
+ * @returns `this` après tout le pipeline post-ready.
634
+ */
635
+ async onReady() {
636
+ return this.fireLifecycle("onReady", this).then(async () => {
637
+ this.ready = true;
638
+ if (this.setCommandComplete(Events.onReady)) return this.finishOrPark(0);
639
+ return this.initServers().then(async (servers) => {
640
+ this.captureBootServers(servers);
641
+ const report = this.getBootReport();
642
+ this.logBootVerdict(report);
643
+ this.writeBootSummary(report);
644
+ if (global && global.gc) {
645
+ this.memoryUsage("MEMORY POST READY ");
646
+ setTimeout(() => {
647
+ if (global && global.gc) global.gc();
648
+ this.memoryUsage("EXPOSE GARBADGE COLLECTOR ON START");
649
+ }, 2e4);
650
+ } else this.memoryUsage("MEMORY POST READY ");
651
+ return this.fireLifecycle("onPostReady", this).then(() => {
652
+ this.postReady = true;
653
+ this.bootLogCounts = this.countBootLogIssues();
654
+ if (!this.suppressBootBanners) servers.map((server) => {
655
+ server.showBanner();
656
+ });
657
+ if (!report.healthy && report.serversExpected) return this.terminate(69);
658
+ if (this.setCommandComplete(Events.onPostReady)) {
659
+ if (!this.runProfile.servers) return this.finishOrPark(typeof process.exitCode === "number" ? process.exitCode : 0);
660
+ this.log(`Live cycle terminate`, "DEBUG");
661
+ return this;
662
+ } else return this;
663
+ }).catch((e) => {
664
+ throw e;
665
+ });
666
+ });
667
+ }).catch((e) => {
668
+ throw e;
669
+ });
670
+ }
671
+ /**
672
+ * Démarre les serveurs HTTP/HTTPS/HTTP2/WS/WSS via le `HttpKernel` (@nodefony/http).
673
+ *
674
+ * Si le HttpKernel n'est pas dans le container (mode CONSOLE pur) → retourne tableau vide.
675
+ * Sinon → délègue à `httpKernel.initServers()`, fire `"onServersReady"` après succès.
676
+ *
677
+ * **Le profil d'exécution fait foi** : une commande qui déclare ne pas vouloir
678
+ * de serveur (`runProfile.servers === false`, le défaut console) n'en obtient
679
+ * pas, même si le `HttpKernel` est chargé. Sans cette garde, toute commande
680
+ * poussée jusqu'à `onPostReady` ouvrait les ports par effet de bord — elle
681
+ * échouait alors si un serveur tournait déjà, et refusait le service à celui
682
+ * qui tournait pendant sa brève existence. `onServersReady` n'est pas émis :
683
+ * annoncer des serveurs prêts quand il n'y en a aucun rendrait l'événement
684
+ * inexploitable pour ceux qui l'écoutent.
685
+ *
686
+ * @returns array d'instances de serveurs démarrés (ou `[]`).
687
+ */
688
+ async initServers() {
689
+ if (this.runProfile?.servers === false) return [];
690
+ const httpKernel = this.get("HttpKernel");
691
+ if (httpKernel) return await httpKernel.initServers().then(async (servers) => {
692
+ await this.fireAsync("onServersReady");
693
+ return servers;
694
+ }).catch((e) => {
695
+ throw e;
696
+ });
697
+ return [];
698
+ }
699
+ /**
700
+ * Parke le flow : retourne une Promise qui ne se résout JAMAIS (met en pause le
701
+ * pipeline async appelant). **Source UNIQUE** du « rester vivant jusqu'à un signal »
702
+ * — remplace les `new Promise(() => {})` inline disséminés (DevSupervisor parent,
703
+ * master cluster, daemon).
704
+ *
705
+ * ⚠️ Une Promise pending ne garde PAS Node en vie (le process sort dès l'event loop
706
+ * vide). Pour un daemon CONSOLE qui n'a aucun socket/handle → `keepAlive: true` ref un
707
+ * timer no-op (réveil ~tous les 12 j, coût nul). Les superviseurs ont DÉJÀ leurs propres
708
+ * handles (watchers fs du DevSupervisor / canaux IPC workers + timers de sonde du
709
+ * master) → `keepAlive` défaut `false` : aucun handle en trop qui empêcherait leur
710
+ * sortie naturelle au shutdown. Le timer alive est nettoyé par {@link terminate}.
711
+ *
712
+ * Signal handling : déjà fourni globalement par `Cli.handleSignals` → `terminate()`.
713
+ *
714
+ * @param opts.keepAlive - ref un timer no-op pour garder l'event loop vivant.
715
+ * @returns Promise jamais résolue.
716
+ */
717
+ park(opts = {}) {
718
+ if (opts.keepAlive && this.parkTimer === null) this.parkTimer = setInterval(() => {}, 1 << 30);
719
+ return new Promise(() => {});
720
+ }
721
+ /**
722
+ * Fin de cycle d'une commande ayant atteint sa phase cible : `terminate(code)` pour un
723
+ * run one-shot (build, install, batch, help), MAIS **park** pour un run long-running
724
+ * sans serveur (daemon CONSOLE : worker de queue, consumer, agent IA, cron daemon).
725
+ *
726
+ * C'est ICI que `lifetime` devient effectif : avant, chaque commande daemon inlinait
727
+ * son propre park ; désormais le Kernel décide à partir de la `lifetime` DÉCLARÉE par
728
+ * la commande (`command.lifetime`, fallback `runProfile.lifetime`) croisée avec
729
+ * `runProfile.servers` (les serveurs gardent déjà le process vivant → jamais de park).
730
+ *
731
+ * @param code - exit code si run one-shot.
732
+ * @returns Promise du terminate (one-shot) ou park (daemon, jamais résolue).
733
+ */
734
+ finishOrPark(code) {
735
+ if ((this.command?.lifetime ?? this.runProfile.lifetime) === "longrunning" && !this.runProfile.servers) {
736
+ this.log("CONSOLE long-running — park (no server, until signal)", "DEBUG");
737
+ return this.park({ keepAlive: true });
738
+ }
739
+ const failure = typeof process.exitCode === "number" && process.exitCode !== 0 ? process.exitCode : null;
740
+ return this.terminate(failure ?? code);
741
+ }
742
+ clean() {
743
+ this.removeAllListeners();
744
+ this.modules = {};
745
+ }
746
+ setCommand(command) {
747
+ this.command = command;
748
+ }
749
+ setDomain() {
750
+ if (this.options.domain == "selectAuto") return this.getFirstExternalInterface()?.address || "localhost";
751
+ else return this.options.domain || "localhost";
752
+ }
753
+ readConfig(config) {
754
+ if (!config) return this.options;
755
+ return extend(this.options, config);
756
+ }
757
+ async addService(service, module, ...args) {
758
+ return module.addService(service, module, ...args);
759
+ }
760
+ /**
761
+ * Instancie un service au niveau kernel (vs niveau module via `Module.addService`).
762
+ *
763
+ * Utilisé pour les services partagés essentiels au boot (Rollup, HttpKernel).
764
+ * Stocke directement dans le container kernel.
765
+ *
766
+ * @param ctor - constructeur du service (typiquement décoré `@injectable`).
767
+ * @param args - args additionnels après les `@inject` resolved.
768
+ * @returns instance du service prête à l'usage.
769
+ */
770
+ async addKernelService(service, ...args) {
771
+ const inst = Injector.instantiate(service, this, ...args);
772
+ if (this.get(inst.name)) this.log(`SERVICE ALREADY EXIST override old service : ${inst.name}`, "WARNING");
773
+ this.log(`SERVICE ADD : ${inst.name}`, "DEBUG");
774
+ const serviceInit = inst;
775
+ await this.guardServiceInitialize(serviceInit, this, true);
776
+ Injector.rememberContainerKey(service, inst.name);
777
+ this.set(inst.name, inst);
778
+ return this.get(inst.name);
779
+ }
780
+ async loadService(service, module = this.app, ...args) {
781
+ if (!module) throw new Error(`Applcation not ready`);
782
+ const res = await import(toImportSpecifier(service));
783
+ return this.addService(res.default, module, ...args);
784
+ }
785
+ /**
786
+ * Charge un module du manifeste. La résolution part de l'APPLICATION
787
+ * ({@link resolveModuleEntry}) et non du paquet `nodefony` : sans cela, un
788
+ * module local de l'app (workspace `modules/*`) est introuvable dès que le core
789
+ * vit hors de l'arbre `node_modules` de l'app (`--link`, monorepo, pnpm).
790
+ */
791
+ async loadModule(moduleName) {
792
+ const moduleClass = await import(resolveModuleEntry(this.path, moduleName));
793
+ return await this.addModule(moduleClass.default);
794
+ }
795
+ /**
796
+ * Résout la liste ORDONNÉE des entrées de modules à charger depuis le manifeste
797
+ * `config.modules`, AVEC leur config colocalisée (`use(name, config)`). L'ordre
798
+ * du tableau est conservé (= priorité de chargement) ; `policy`/`when` ne font
799
+ * que FILTRER, jamais réordonner.
800
+ *
801
+ * - `policy:"dev"` → ignoré en production (gain mémoire = non-chargement ; en
802
+ * ESM un module importé n'est jamais déchargé).
803
+ * - `when(config)` faux → ignoré (sa config colocalisée l'est aussi → pas de
804
+ * warning « module absent » sur un module volontairement gaté).
805
+ * - string nue → policy `optional`, toujours chargée, sans config.
806
+ *
807
+ * @returns entrées `{ name, config? }` à charger, dans l'ordre.
808
+ */
809
+ resolveModuleEntries() {
810
+ const isProd = this.resolveRuntimeEnv(this.cli?.environment) === "production";
811
+ const devModulesForced = isProd && process.env["NF_WITH_DEV_MODULES"] === "1";
812
+ const outcome = gateModuleManifest(this.options.modules, {
813
+ isProduction: isProd,
814
+ forceDevModules: devModulesForced,
815
+ config: this.options
816
+ });
817
+ this.modulesGated = null;
818
+ for (const gated of outcome.gated) this.recordModuleGated(gated.module, gated.reason);
819
+ for (const name of outcome.derogated) {
820
+ this.log(`module "${name}" (policy "dev") chargé en PRODUCTION sur ${FORCE_DEV_MODULES_ENV}=1 — dérogation explicite, à ne jamais poser sur un déploiement réel`, "WARNING", "KERNEL");
821
+ this.armDevModulesSelfStop();
822
+ }
823
+ return outcome.entries;
824
+ }
825
+ /**
826
+ * Enregistre un module volontairement non chargé (gating `policy`/`when`) avec
827
+ * sa raison — surfacé par le bilan de boot ({@link getBootReport}). Lazy : 0
828
+ * allocation si rien n'est gaté.
829
+ *
830
+ * @param module - nom d'entrée du manifeste.
831
+ * @param reason - raison lisible du non-chargement.
832
+ */
833
+ recordModuleGated(module, reason) {
834
+ (this.modulesGated ??= []).push({
835
+ module,
836
+ reason
837
+ });
838
+ this.log(`MODULE GATED : ${module} — ${reason}`, "DEBUG");
839
+ }
840
+ /**
841
+ * Arme l'auto-arrêt d'un runtime de production ayant dérogé au gating des modules
842
+ * de développement (cf {@link FORCE_DEV_MODULES_ENV}). Idempotent : une seule
843
+ * minuterie, quel que soit le nombre de modules concernés.
844
+ *
845
+ * **L'échéance est ANNONCÉE, deux fois.** Un arrêt temporisé qui surprend est pire
846
+ * que pas de garde-fou : il coupe un banc de charge au milieu d'une mesure, et le
847
+ * journal qu'on relira ensuite accusera le code. On dit donc l'échéance au
848
+ * démarrage — avec le moyen de l'allonger — puis on prévient avant de tomber, et
849
+ * l'arrêt lui-même en donne la raison. Les minuteries sont `unref` : elles
850
+ * n'empêchent jamais une sortie naturelle.
851
+ */
852
+ armDevModulesSelfStop() {
853
+ if (this.devModulesStopTimer) return;
854
+ const ttl = resolveDevModulesTtlMs(process.env);
855
+ const minutes = Math.round(ttl / 6e4);
856
+ this.log(`runtime en DÉROGATION (modules dev en production) — arrêt automatique dans ${minutes} min. Allonger : ${FORCE_DEV_MODULES_TTL_ENV}=<minutes> (plafond ${Math.round(FORCE_DEV_MODULES_TTL_MAX_MS / 6e4)} min, jamais désarmé)`, "WARNING", "KERNEL");
857
+ const notice = ttl - 3e5;
858
+ if (notice > 0) setTimeout(() => {
859
+ this.log(`arrêt automatique dans 5 min (dérogation ${FORCE_DEV_MODULES_ENV}) — termine ta mesure ou relance avec ${FORCE_DEV_MODULES_TTL_ENV} plus haut`, "WARNING", "KERNEL");
860
+ }, notice).unref();
861
+ this.devModulesStopTimer = setTimeout(() => {
862
+ this.log(`arrêt automatique : ce runtime tournait en production avec des modules "policy: dev" (${FORCE_DEV_MODULES_ENV}=1). Ce n'est pas une panne — c'est la garde qui empêche une dérogation de banc de survivre à un déploiement`, "CRITIC", "KERNEL");
863
+ this.terminate(0);
864
+ }, ttl);
865
+ this.devModulesStopTimer.unref();
866
+ }
867
+ /**
868
+ * Charge en série les modules résolus par {@link resolveModuleEntries} via
869
+ * `loadModule` (import dynamique → lazy : un module hors liste n'est jamais
870
+ * importé), puis applique la config colocalisée (`use(name, config)`) de
871
+ * chaque entrée. Branché à `onPreRegister` par {@link loadApp} si un manifeste
872
+ * `config.modules` est présent. Seul orchestrateur du chargement de modules.
873
+ */
874
+ async loadModulesFromManifest() {
875
+ for (const entry of this.resolveModuleEntries()) try {
876
+ const mod = await this.loadModule(entry.name);
877
+ if (entry.config) {
878
+ mod.options = extend(true, {}, mod.options, entry.config);
879
+ this.log(`MODULE CONFIG (use) : ${entry.name}`, "DEBUG");
880
+ }
881
+ } catch (error) {
882
+ const msg = error instanceof Error ? error.message : String(error);
883
+ this.log(`MODULE LOAD: échec non bloquant (fail-soft) de "${entry.name}" — ${msg}`, "WARNING");
884
+ this.recordBootFailure({
885
+ module: entry.name,
886
+ reason: msg,
887
+ phase: "load"
888
+ });
889
+ }
890
+ }
891
+ /**
892
+ * Instancie un module et l'enregistre dans `kernel.modules[name]`. Appelle `initialize(this)`
893
+ * sur le module si défini (équivalent constructeur async).
894
+ *
895
+ * @param Mod - constructeur du module (extends `Module`).
896
+ * @param args - arguments additionnels (après `kernel, path, options` par défaut).
897
+ * @returns instance du module enregistrée.
898
+ */
899
+ async addModule(Mod, ...args) {
900
+ const mod = new Mod(this, ...args);
901
+ this.modules[mod.name] = mod;
902
+ if (this.pendingModuleAddLogs) this.pendingModuleAddLogs.push(mod.name);
903
+ else this.log(`MODULE ADD : ${mod.name}`, "INFO");
904
+ if (mod.init) {
905
+ this.log(`MODULE INITIALIZE : ${mod.name}`, "DEBUG");
906
+ const init = mod.init.bind(mod);
907
+ await this.guardInitialize(() => init(this), mod.name, mod.constructor.critical);
908
+ }
909
+ return mod;
910
+ }
911
+ getModule(name) {
912
+ return this.modules[name];
913
+ }
914
+ getModules() {
915
+ return this.modules;
916
+ }
917
+ /**
918
+ * Applique les overrides de config inter-modules (clés `Module-<name>` /
919
+ * `module-<name>` dans les options d'un module — typiquement l'app qui
920
+ * reconfigure un autre module) en itérant TOUS les modules enregistrés.
921
+ *
922
+ * Appelé par {@link preRegister} ENTRE `onPreRegister` (tous les modules sont
923
+ * alors enregistrés via `@modules`/`loadApp`) et `onRegister` (où les modules
924
+ * valident + gèlent leur config Zod dans `onKernelRegister`). Corrige la dette
925
+ * d'ordering : l'override était auparavant posé à `onPreBoot` (après la
926
+ * validation) → silencieusement ignoré par tout module qui fige sa config tôt
927
+ * (redis, realtime…). Désormais il précède la validation, qui le voit.
928
+ *
929
+ * Ordre d'application = ordre d'insertion dans `this.modules` (app chargée en
930
+ * premier → configure les modules avant qu'ils ne se valident). Idempotent.
931
+ */
932
+ applyModuleConfigOverrides() {
933
+ for (const name in this.modules) this.modules[name].readOverrideModuleConfig();
934
+ this.applyEnvConfigOverrides();
935
+ }
936
+ /**
937
+ * Applique les overrides de config par variable d'environnement générique
938
+ * `NF__<MODULE>__<CHEMIN…>` (ADR-0006 D3) sur la config de chaque module ciblé.
939
+ *
940
+ * Résolu 1× au boot (hors hot path). La surcharge est déposée sur
941
+ * `module.options` → elle sera validée par le schéma Zod du module à
942
+ * `onKernelRegister` (fail-closed si invalide). Un module ou un chemin
943
+ * introuvable est SIGNALÉ (WARNING) sans bloquer le boot — jamais de clé
944
+ * fantôme silencieuse. Les valeurs de chemins « secrets » sont rédigées au log.
945
+ */
946
+ applyEnvConfigOverrides() {
947
+ const overrides = parseNfEnvOverrides(process.env);
948
+ if (!overrides.length) return;
949
+ /**
950
+ * Le schéma d'un module, quand il en publie un — jamais une hypothèse.
951
+ *
952
+ * `configSchema()` est une méthode de `Module`, mais rien ne garantit qu'un
953
+ * module l'ait : un paquet tiers, un module ancien ou un objet de banc
954
+ * peuvent n'être que compatibles de forme. Appeler à l'aveugle ferait tomber
955
+ * le BOOT sur une amélioration de confort — le pire échange possible.
956
+ */
957
+ const schemaDe = (m) => {
958
+ const f = m.configSchema;
959
+ return typeof f === "function" ? f.call(m) ?? void 0 : void 0;
960
+ };
961
+ for (const ov of overrides) {
962
+ const mod = this.findModuleBySegment(ov.moduleSeg);
963
+ if (!mod) {
964
+ const suggestion = closestMatch(ov.moduleSeg, this.moduleSegments());
965
+ this.log(`Override env ignoré : module "${ov.moduleSeg}" introuvable (${ov.envKey})` + (suggestion ? ` — vouliez-vous dire « ${suggestion} » ?` : ""), "WARNING");
966
+ continue;
967
+ }
968
+ if (applyResolvedPath(mod.options, ov.path, ov.value, ov.raw, schemaDe(mod))) {
969
+ const appliedValue = readResolvedPath(mod.options, ov.path);
970
+ const shown = pathLooksSecret(ov.path) ? "«***»" : JSON.stringify(appliedValue);
971
+ this.log(`Override env: ${mod.name}.${ov.path.join(".")} = ${shown}`, "INFO");
972
+ } else this.log(`Override env ignoré : chemin "${ov.path.join(".")}" inconnu sur ${mod.name} (${ov.envKey})` + resolveFailureHint(mod.options, ov.path, schemaDe(mod)), "WARNING");
973
+ }
974
+ }
975
+ /**
976
+ * Filet « clé réservée » : avertit au BOOT quand une application a posé une clé de
977
+ * config marquée `reserved` (inerte — son levier vit ailleurs) à une valeur
978
+ * non-défaut. C'est le pendant runtime du drapeau que Studio se contente de griser :
979
+ * sans lui, une clé réservée écrite reste un silence total (l'app croit régler un
980
+ * comportement, rien ne se produit, rien ne le dit). Symétrique du signalement des
981
+ * chemins env inconnus ({@link applyEnvConfigOverrides}).
982
+ *
983
+ * Générique — 0 `if` par module : le JSON Schema (`Module.configSchema()`, avec les
984
+ * flags `.meta()`) et la config résolue suffisent. Un module non migré (schéma
985
+ * `null`) est ignoré. Appelé 1× après `onRegister` (options validées) → hors hot
986
+ * path ; le message inclut la `description` du champ, qui nomme la remplaçante.
987
+ */
988
+ warnReservedConfigKeys() {
989
+ for (const name in this.modules) {
990
+ const mod = this.modules[name];
991
+ const schema = mod.configSchema();
992
+ if (!schema) continue;
993
+ const hits = findSetReservedKeys(schema, mod.options);
994
+ for (const hit of hits) this.log(`Clé de config RÉSERVÉE posée sans effet : ${mod.name}.${hit.path} est inerte.${hit.description ? " " + hit.description : ""}`, "WARNING", "CONFIG");
995
+ }
996
+ }
997
+ /**
998
+ * Résout un segment de nom de module (minuscule, issu d'un `NF__<MODULE>__…`)
999
+ * vers son instance : compare au **basename** du nom de module (après le `/`),
1000
+ * insensible à la casse (`security` → `@nodefony/security`).
1001
+ *
1002
+ * @param seg - segment module normalisé (minuscule).
1003
+ * @returns le module correspondant, ou `undefined`.
1004
+ */
1005
+ findModuleBySegment(seg) {
1006
+ for (const name in this.modules) {
1007
+ const slash = name.lastIndexOf("/");
1008
+ if ((slash >= 0 ? name.slice(slash + 1) : name).toLowerCase() === seg) return this.modules[name];
1009
+ }
1010
+ }
1011
+ /**
1012
+ * Basenames de tous les modules chargés (segment ciblable par `NF__<MODULE>__…`).
1013
+ * Sert au « did you mean » quand le segment module d'un override ne résout pas.
1014
+ *
1015
+ * @returns la liste des basenames (ex. `security`, `http`).
1016
+ */
1017
+ moduleSegments() {
1018
+ const out = [];
1019
+ for (const name in this.modules) {
1020
+ const slash = name.lastIndexOf("/");
1021
+ out.push(slash >= 0 ? name.slice(slash + 1) : name);
1022
+ }
1023
+ return out;
1024
+ }
1025
+ /**
1026
+ * Valide la config de l'app au boot via le schéma fourni par l'app, si présent.
1027
+ *
1028
+ * Convention générique (le core ne dépend PAS de Zod) : si l'entrée de l'app
1029
+ * (`dist/index.js`) exporte une fonction `validateConfig`, le Kernel l'exécute sur
1030
+ * la config résolue de l'app (`app.options`) — l'app porte le schéma + la dépendance
1031
+ * Zod. Absent → étape sautée (rétro-compatible avec une app sans schéma). L'import
1032
+ * est déjà résolu par `loadModule` (cache ESM) → coût négligeable, hors hot path.
1033
+ *
1034
+ * @throws si le validateur de l'app rejette la config (message agrégé).
1035
+ */
1036
+ async validateAppConfig() {
1037
+ const entry = this.resolveAppEntry();
1038
+ if (entry === null) return;
1039
+ (await import(toImportSpecifier(entry))).validateConfig?.(this.app?.options);
1040
+ }
1041
+ /**
1042
+ * Construit le contexte d'environnement (`ctx`) passé au descripteur
1043
+ * {@link defineConfig} d'une app moderne. `runtimeEnv` = `NODE_ENV` normalisé
1044
+ * (`dev`→`development`, conserve `test`/`production`) — granularité que le ctx
1045
+ * expose (`isTest`), DISTINCTE du collapse dev/prod de {@link resolveRuntimeEnv}
1046
+ * (gating moteur : un staging tourne « comme prod »). `appEnv` = axe déploiement
1047
+ * libre (`APP_ENV`/`NF_ENV`). `process.env` est déjà peuplé par `loadEnv`
1048
+ * (bin/nodefony) avant le boot → lecture sûre ici.
1049
+ *
1050
+ * @param env - catalogue env typé exposé par l'app (`export const env = defineEnv(…)`) ;
1051
+ * `undefined` (app legacy / pas de catalogue) → `process.env` brut.
1052
+ * @returns contexte d'environnement pour `descriptor.resolve(ctx)`.
1053
+ */
1054
+ buildConfigContext(env) {
1055
+ const raw = process.env.NODE_ENV || this.cli?.environment || "development";
1056
+ const runtimeEnv = raw === "dev" ? "development" : raw;
1057
+ const appEnv = process.env.APP_ENV || process.env.NF_ENV || runtimeEnv;
1058
+ return {
1059
+ env: env ?? process.env,
1060
+ infra: this.infra,
1061
+ appEnv,
1062
+ runtimeEnv,
1063
+ isProd: runtimeEnv === "production",
1064
+ isDev: runtimeEnv === "development",
1065
+ isTest: runtimeEnv === "test"
1066
+ };
1067
+ }
1068
+ /** Infra déclarée mémoïsés — lazy, `null` tant que non lus. */
1069
+ _infra = null;
1070
+ /**
1071
+ * Infra déclarée (`database`/`cache`/`logs`) résolus depuis
1072
+ * `process.env` (modèle « infra déclarée », Phase 0.8). Calcul 1 fois, mémoïsé —
1073
+ * consommé par le ctx `defineConfig` (`ctx.infra`) et par les briques dont
1074
+ * le store vaut `"auto"` (`resolveAutoStore`).
1075
+ *
1076
+ * @throws Error au premier accès si `NF_DATABASE_URL` a un scheme non
1077
+ * supporté (fail-loud : jamais de repli sqlite silencieux).
1078
+ */
1079
+ get infra() {
1080
+ if (this._infra === null) this._infra = resolveInfra(process.env);
1081
+ return this._infra;
1082
+ }
1083
+ /**
1084
+ * Registre des résolutions de stores — lazy (`null` tant qu'aucune brique n'a
1085
+ * résolu son store). Alloué au premier `registerStoreResolution` (au boot, une
1086
+ * fois par brique) — jamais dans le hot-path. Clé = `brick` (dernière gagne).
1087
+ */
1088
+ _storeResolutions = null;
1089
+ /**
1090
+ * Résolutions EFFECTIVES des stores de persistance capturées au boot — la
1091
+ * vérité vécue (replis annoncés inclus). Alimente `/nodefony/kernel/api/stores`
1092
+ * et l'écran Studio « Stores ». Vide si aucune brique n'a encore résolu.
1093
+ */
1094
+ get storeResolutions() {
1095
+ return this._storeResolutions ? Array.from(this._storeResolutions.values()) : [];
1096
+ }
1097
+ /**
1098
+ * Enregistre la résolution effective d'une brique (idempotent par `brick`).
1099
+ * La provenance est dérivée de `configured` : la sentinelle `"auto"` ⇒
1100
+ * `"infra"` (résolu depuis l'infra déclarée), un backend nommé ⇒ `"explicit"`.
1101
+ *
1102
+ * @param resolution - triplet brique/configuré/résolu + raison + nature
1103
+ * ({@link IStoreResolution} sans `provenance`, dérivée ici).
1104
+ */
1105
+ registerStoreResolution(resolution) {
1106
+ if (this._storeResolutions === null) this._storeResolutions = /* @__PURE__ */ new Map();
1107
+ this._storeResolutions.set(resolution.brick, {
1108
+ ...resolution,
1109
+ provenance: resolution.configured === "auto" ? "infra" : "explicit"
1110
+ });
1111
+ }
1112
+ /**
1113
+ * Résout les options brutes de l'app. App moderne (`export default defineConfig(…)`)
1114
+ * → `raw` est un descripteur {@link AppConfigDescriptor} (le symbole de marque
1115
+ * survit au spread d'options de `Service`) → résolu avec `ctx` (deep-merge des
1116
+ * défauts framework + validation Zod intégrée à `resolve`). App legacy → objet de
1117
+ * config retourné tel quel (validé séparément par son export `validateConfig`).
1118
+ *
1119
+ * @param raw - export par défaut de l'app (descripteur ou objet de config).
1120
+ * @param ctx - contexte d'environnement ({@link buildConfigContext}).
1121
+ * @returns options résolues + `wasDescriptor` (pilote le fallback de validation).
1122
+ */
1123
+ resolveAppOptions(raw, ctx) {
1124
+ if (isConfigDescriptor(raw)) {
1125
+ const options = raw.resolve(ctx);
1126
+ this.surfaceAppEnvOverrides(options);
1127
+ return {
1128
+ options,
1129
+ wasDescriptor: true
1130
+ };
1131
+ }
1132
+ return {
1133
+ options: extend(true, {}, defaultAppConfig, raw ?? {}),
1134
+ wasDescriptor: false
1135
+ };
1136
+ }
1137
+ /**
1138
+ * Émet au log le rapport des overrides `NF__APP__*` appliqués à la config app
1139
+ * (INFO par override, secret rédigé) + les warnings « did you mean » des chemins
1140
+ * non résolus. Appelé APRÈS `resolve()` : le logger kernel est prêt (le merge,
1141
+ * lui, tourne dans `resolve()` AVANT le logger → rapport différé, cf defineConfig).
1142
+ *
1143
+ * @param options - config app résolue (porte le rapport en clé non-énumérable).
1144
+ */
1145
+ surfaceAppEnvOverrides(options) {
1146
+ const report = readAppEnvOverrideReport(options);
1147
+ if (!report) return;
1148
+ for (const ov of report.applied) this.log(`Override env app: ${ov.path.join(".")} = ${ov.secret ? "«***»" : "(env)"}`, "INFO");
1149
+ for (const w of report.warnings) this.log(w, "WARNING");
1150
+ }
1151
+ /**
1152
+ * Construit + PRÉSENTE une erreur de configuration de boot de façon
1153
+ * EXCEPTIONNELLEMENT claire, et EXPLICITE sur la config PAR DÉFAUT du framework
1154
+ * (ce qui s'applique pour tout champ omis). Une config cassée n'est pas
1155
+ * récupérable (le framework ne peut pas deviner ports/modules) → fail-fast PROPRE :
1156
+ * log lisible SANS stack brute (faute de config, pas bug framework), erreur marquée
1157
+ * `presented` (le catch de boot ne re-loggue pas) + `exitCode` `EX_CONFIG`
1158
+ * (l'orchestrateur distingue « mauvaise config » d'un crash logiciel).
1159
+ *
1160
+ * @param title - titre court de l'erreur.
1161
+ * @param detail - phrase de contexte.
1162
+ * @param cause - erreur d'origine (Zod, import, fonction de config).
1163
+ * @param hints - correctifs actionnables.
1164
+ * @returns nodefonyError marquée (`exitCode`/`presented`), à `throw`.
1165
+ */
1166
+ bootConfigError(title, detail, cause, hints) {
1167
+ const causeMsg = cause instanceof Error ? cause.message : String(cause);
1168
+ const lines = [
1169
+ "",
1170
+ ` ✖ ${title}`,
1171
+ ` ${detail}`,
1172
+ ` Cause : ${causeMsg}`,
1173
+ "",
1174
+ " Configuration PAR DÉFAUT du framework (appliquée à tout champ omis) :",
1175
+ this.formatDefaults(),
1176
+ "",
1177
+ " Pour corriger :",
1178
+ ...hints.map((h) => ` → ${h}`),
1179
+ ""
1180
+ ];
1181
+ this.log(lines.join("\n"), "CRITIC");
1182
+ const err = new nodefonyError(`${title} : ${causeMsg}`);
1183
+ err.exitCode = 78;
1184
+ err.presented = true;
1185
+ return err;
1186
+ }
1187
+ /** Rendu lisible (1 ligne/clé top-level) des valeurs par défaut du framework. */
1188
+ formatDefaults() {
1189
+ return Object.entries(defaultAppConfig).map(([k, v]) => {
1190
+ return ` • ${k} = ${v !== null && typeof v === "object" ? JSON.stringify(v) : String(v)}`;
1191
+ }).join("\n");
1192
+ }
1193
+ async loadApp(config) {
1194
+ const appEntry = this.resolveAppEntry() ?? `${this.path}/dist/index.js`;
1195
+ try {
1196
+ this.app = await this.loadModule(appEntry);
1197
+ } catch (e) {
1198
+ throw this.bootConfigError("Chargement de l'application impossible", `Le point d'entrée \`${appEntry}\` n'a pas pu être importé/évalué.`, e, ["Build périmé ou absent → `npm run clean && npm run build`.", "Un fichier de config déréférence le kernel au top-level (résolu à l'import) → différer en getter/lazy."]);
1199
+ }
1200
+ this.app.isApp = true;
1201
+ const appModule = await import(toImportSpecifier(appEntry));
1202
+ const ctx = this.buildConfigContext(appModule.env);
1203
+ let wasDescriptor = false;
1204
+ try {
1205
+ const resolved = this.resolveAppOptions(this.app.options, ctx);
1206
+ this.app.options = resolved.options;
1207
+ wasDescriptor = resolved.wasDescriptor;
1208
+ } catch (e) {
1209
+ throw this.bootConfigError("Configuration de l'application invalide", "La résolution de la configuration (`defineConfig`) a échoué.", e, ["Vérifie les types/valeurs des champs signalés dans `nodefony.config.ts`.", "Une fonction `defineConfig((ctx) => …)` qui lève → corrige la logique par-env."]);
1210
+ }
1211
+ this.options = this.readConfig(extend(this.app.options, config));
1212
+ if (!wasDescriptor) try {
1213
+ await this.validateAppConfig();
1214
+ } catch (e) {
1215
+ throw this.bootConfigError("Configuration de l'application invalide", "Le schéma de validation de l'app a rejeté la configuration.", e, ["Corrige les champs signalés ci-dessus.", "Les valeurs par défaut du framework listées s'appliquent aux champs omis."]);
1216
+ }
1217
+ if (Array.isArray(this.options.modules) && this.options.modules.length) this.once("onPreRegister", async () => {
1218
+ await this.loadModulesFromManifest();
1219
+ });
1220
+ this.initializeLog();
1221
+ this.cli?.setPackageManager(this.options.packageManager);
1222
+ this.core = await this.isCore();
1223
+ this.app.package = await this.app.getPackageJson();
1224
+ this.version = this.app?.getModuleVersion();
1225
+ this.fixCommanderCli();
1226
+ this.cli?.setCommandVersion(this.version);
1227
+ await this.fireAsync("onAppLoad", this.app).catch((e) => {
1228
+ throw e;
1229
+ });
1230
+ return this.app;
1231
+ }
1232
+ /**
1233
+ * Résout le point d'entrée de l'application depuis SON `package.json`
1234
+ * (champ `main` — la source de vérité Node), fallback legacy `dist/index.js`
1235
+ * puis `index.js`. Vérifie l'EXISTENCE du fichier, sans jamais l'importer :
1236
+ * l'exécution (side effects top-level, cache ESM) et la validation du module
1237
+ * (export default = `Module`) appartiennent à {@link loadApp}, une seule
1238
+ * fois, avec son diagnostic `bootConfigError`. Mémoïsé (stable pour le boot).
1239
+ *
1240
+ * Anti faux-positif : un projet Node QUELCONQUE (Express…) a aussi un
1241
+ * `package.json` + `main` → on exige le signal d'identité Nodefony, sans
1242
+ * import : `nodefony` déclaré dans les dépendances, OU installé
1243
+ * (`node_modules/nodefony` — couvre le monorepo self-hosted où la racine ne
1244
+ * le déclare pas mais où le workspace le symlinke).
1245
+ *
1246
+ * En production (image Docker), seuls `package.json` + `dist/` +
1247
+ * `node_modules` sont déployés : la détection ne dépend d'AUCUN source.
1248
+ *
1249
+ * @returns chemin absolu de l'entrée, ou `null` (cwd hors projet Nodefony).
1250
+ */
1251
+ resolveAppEntry() {
1252
+ if (this._appEntry !== void 0) return this._appEntry;
1253
+ let pkg;
1254
+ try {
1255
+ pkg = JSON.parse(fs.readFileSync(path.resolve(this.path, "package.json"), "utf8"));
1256
+ } catch {
1257
+ this._appEntry = null;
1258
+ return null;
1259
+ }
1260
+ if (!Boolean(pkg.dependencies?.nodefony ?? pkg.devDependencies?.nodefony ?? pkg.peerDependencies?.nodefony) && !fs.existsSync(path.resolve(this.path, "node_modules", "nodefony"))) {
1261
+ this._appEntry = null;
1262
+ return null;
1263
+ }
1264
+ for (const candidate of pkg.main ? [pkg.main] : ["dist/index.js", "index.js"]) {
1265
+ const abs = path.resolve(this.path, candidate);
1266
+ if (fs.existsSync(abs)) {
1267
+ this._appEntry = abs;
1268
+ return abs;
1269
+ }
1270
+ }
1271
+ this._appEntry = null;
1272
+ return null;
1273
+ }
1274
+ async isTrunk() {
1275
+ const entry = this.resolveAppEntry();
1276
+ if (entry === null) return null;
1277
+ return entry.includes(`${path.sep}dist${path.sep}`) ? "typescript" : "javascript";
1278
+ }
1279
+ /**
1280
+ * Diagnostic fail-loud d'un PROJET Nodefony non bootable — différencie les
1281
+ * trois raisons qu'un `resolveAppEntry()` nul confond : « vraiment pas un
1282
+ * projet » (→ `null`, wizard/message générique), « dépendances non
1283
+ * installées » et « non construit ». Sans lui, un `nodefony dev` dans une
1284
+ * app fraîchement générée tombait sur le wizard de création — déroutant
1285
+ * alors qu'il ne manque que `npm install` / `npm run build` (vécu).
1286
+ *
1287
+ * @returns le message actionnable à logger CRITIC, ou `null` si ce dossier
1288
+ * n'est pas un projet Nodefony (le flux hors-projet reste inchangé)
1289
+ */
1290
+ diagnoseUnbootableProject() {
1291
+ let pkg;
1292
+ try {
1293
+ pkg = JSON.parse(fs.readFileSync(path.resolve(this.path, "package.json"), "utf8"));
1294
+ } catch {
1295
+ return null;
1296
+ }
1297
+ if (!Boolean(pkg.dependencies?.nodefony ?? pkg.devDependencies?.nodefony ?? pkg.peerDependencies?.nodefony)) return null;
1298
+ if (!fs.existsSync(path.resolve(this.path, "node_modules"))) return `Projet Nodefony détecté (${this.path}) mais dépendances NON INSTALLÉES.\nLance :\n npm install\n npm run build\npuis relance ta commande.`;
1299
+ return `Projet Nodefony détecté (${this.path}) mais NON CONSTRUIT (aucune entrée d'app : package.json \`main\`, dist/index.js ou index.js).\nLance :\n npm run build\npuis relance ta commande.`;
1300
+ }
1301
+ async isCore() {
1302
+ return false;
1303
+ }
1304
+ isModule(subclass) {
1305
+ return isSubclassOf(subclass, Module);
1306
+ }
1307
+ getEventName(event) {
1308
+ return Object.keys(Events).find((key) => Events[key] === event);
1309
+ }
1310
+ setCommandComplete(progress) {
1311
+ const index = this.getEventName(progress);
1312
+ this.progress |= Events[index];
1313
+ return this.isCommandComplete(progress);
1314
+ }
1315
+ isCommandComplete(progress) {
1316
+ const index = this.getEventName(progress);
1317
+ if (this.command) {
1318
+ const int = Events[this.command.kernelEvent];
1319
+ const res = !!(this.progress & int);
1320
+ this.log(`Ckeck Command event : ${this.getEventName(int)} Progress: ${index} : Complete : ${res}`, "DEBUG", `COMMAND ${this.command.name}`);
1321
+ return res;
1322
+ }
1323
+ return false;
1324
+ }
1325
+ initializeLog() {
1326
+ this.syslog?.removeAllListeners();
1327
+ if (this._mountedLogTransports !== null) {
1328
+ for (const t of this._mountedLogTransports) this.syslog?.removeTransport(t);
1329
+ this._mountedLogTransports = null;
1330
+ }
1331
+ if (this.options.log && !this.options.log.active) return;
1332
+ const logCfg = this.options.log;
1333
+ Syslog.setOutputBuffering(logCfg?.buffered ?? "auto");
1334
+ setLogColor(resolveColorEnabled(this.isTTY));
1335
+ const logDirAbs = path.resolve(process.cwd(), logCfg?.dir ?? "logs");
1336
+ const logDriver = logCfg?.driver ?? "stdout";
1337
+ if (logDriver === "file") {
1338
+ const logPath = logCfg?.file?.path ?? path.join(logDirAbs, `nodefony-${process.pid}.log`);
1339
+ Syslog.setLogSink(new FileSink({
1340
+ path: logPath,
1341
+ sync: logCfg?.file?.sync
1342
+ }));
1343
+ } else if (logDriver === "null") Syslog.setLogSink(NULL_LOG_SINK);
1344
+ else Syslog.setLogSink(null);
1345
+ const maxStack = logCfg?.maxStack ?? (this.environment === "development" ? 2e3 : void 0);
1346
+ if (maxStack) this.syslog?.setMaxStack(maxStack);
1347
+ const queryDriver = resolveQueryDriver(logCfg?.queryDriver, cluster.isWorker, {
1348
+ loki: logCfg?.loki?.url,
1349
+ opensearch: logCfg?.opensearch?.url
1350
+ });
1351
+ registerBuiltinLogDrivers();
1352
+ const driverCtx = {
1353
+ logCfg,
1354
+ environment: this.environment ?? "development",
1355
+ logDir: logDirAbs,
1356
+ pid: process.pid,
1357
+ getRingStack: () => this.syslog?.ringStack ?? []
1358
+ };
1359
+ const toMount = /* @__PURE__ */ new Set(["memory", queryDriver]);
1360
+ if (this.environment === "development") for (const name of listLogDriverFactories()) toMount.add(name);
1361
+ const addedWrites = /* @__PURE__ */ new Set();
1362
+ for (const name of toMount) {
1363
+ const factory = getLogDriverFactory(name);
1364
+ if (factory === void 0) continue;
1365
+ const mount = factory(driverCtx);
1366
+ if (mount === null) continue;
1367
+ registerLogDriver(mount.driver);
1368
+ if (mount.transport !== void 0) {
1369
+ const key = mount.writeKey;
1370
+ if (key === void 0 || !addedWrites.has(key)) {
1371
+ this.syslog?.addTransport(mount.transport);
1372
+ (this._mountedLogTransports ??= []).push(mount.transport);
1373
+ if (key !== void 0) addedWrites.add(key);
1374
+ }
1375
+ }
1376
+ }
1377
+ try {
1378
+ setActiveLogDriver(queryDriver);
1379
+ } catch {
1380
+ this.syslog?.log(`Log query driver "${queryDriver}" introuvable — fallback "memory".`, "WARNING", "SYSLOG");
1381
+ setActiveLogDriver("memory");
1382
+ }
1383
+ if (!this.cli && !this.debug && this.options.log?.debug) this.debug = this.options.log.debug;
1384
+ this.syslog?.setSeverityThreshold(this.environment === "production" && !this.debug ? "INFO" : null);
1385
+ const debugSpec = process.env.NF__DEBUG;
1386
+ if (debugSpec && this.syslog) {
1387
+ const { global, overrides } = Syslog.parseDebugSpec(debugSpec);
1388
+ if (global) this.syslog.setSeverityThreshold(null);
1389
+ for (const ov of overrides) this.syslog.setDebugOverride(ov.module, ov.level);
1390
+ }
1391
+ if (this.cli) return this.cli.initSyslog(this.environment, this.debug);
1392
+ else return this.initSyslog(this.environment, this.debug);
1393
+ }
1394
+ setCli(cli) {
1395
+ if (cli) {
1396
+ this.runProfile = cli.runProfile;
1397
+ this.debug = Boolean(cli?.commander?.opts().debug) || false;
1398
+ if (this.typeCluster === "worker") cli.setPid();
1399
+ this.set("cli", cli);
1400
+ return cli;
1401
+ }
1402
+ return null;
1403
+ }
1404
+ isConsole() {
1405
+ return this.runProfile ? !this.runProfile.servers : false;
1406
+ }
1407
+ setNodeEnv(environment) {
1408
+ if (environment) switch (environment) {
1409
+ case "dev":
1410
+ case "development":
1411
+ process.env.NODE_ENV = "development";
1412
+ process.env.BABEL_ENV = "development";
1413
+ this.isDev = true;
1414
+ this.isProd = false;
1415
+ break;
1416
+ default:
1417
+ process.env.NODE_ENV = "production";
1418
+ process.env.BABEL_ENV = "production";
1419
+ this.isProd = true;
1420
+ this.isDev = false;
1421
+ }
1422
+ process.env.NODE_DEBUG = this.debug ? "true" : "false";
1423
+ }
1424
+ /**
1425
+ * Résout le **mode runtime** (dev/prod) : `NODE_ENV`
1426
+ * (ambient, posé par l'orchestrateur en cloud) PRIME sur l'intention de la
1427
+ * commande locale, qui prime sur la valeur courante. Normalise en
1428
+ * `"development" | "production"`. Pur (lit `process.env`, n'écrit rien) → sûr à
1429
+ * appeler avant que `setNodeEnv` n'ait posé `NODE_ENV` (ex. gating de modules à
1430
+ * `onPreRegister`). Cf project_app_config_refonte_chantier (deux axes d'env).
1431
+ */
1432
+ resolveRuntimeEnv(fromCommand) {
1433
+ const raw = process.env.NODE_ENV || fromCommand || this.environment || "development";
1434
+ return engineModeOf(raw);
1435
+ }
1436
+ setEnv(environment) {
1437
+ const runtime = this.resolveRuntimeEnv(environment);
1438
+ this.environment = runtime;
1439
+ this.appEnvironment.environment = process.env.APP_ENV || process.env.NF_ENV || runtime;
1440
+ }
1441
+ /**
1442
+ * Header de boot dev consolidé, imprimé juste sous l'ASCII (dev-only) : nom +
1443
+ * version + environnement + meta (`cluster · platform · node · pid`). Remplace la
1444
+ * ligne « Version … » et le banner « SERVER … » bruts. Couleurs gatées TTY (logColor).
1445
+ */
1446
+ printDevHeader() {
1447
+ let version = "";
1448
+ try {
1449
+ const v = this.cli?.commander?.version();
1450
+ if (typeof v === "string") version = v;
1451
+ } catch {}
1452
+ const meta = [
1453
+ String(this.typeCluster ?? ""),
1454
+ process.platform,
1455
+ `node ${process.version}`,
1456
+ `pid ${process.pid}`,
1457
+ `tty ${this.isTTY ? "yes" : "no"}`
1458
+ ].filter(Boolean).join(" · ");
1459
+ const tag = version ? ` ${logColor.blackBright(`v${version}`)}` : "";
1460
+ const env = this.environment ? ` ${logColor.green(String(this.environment))}` : "";
1461
+ const appEnv = process.env.APP_ENV || process.env.NF_ENV;
1462
+ const deploy = appEnv && appEnv !== String(this.environment) ? ` ${logColor.blackBright("·")} ${logColor.magenta(appEnv)}` : "";
1463
+ console.log(` ${logColor.cyan("⬢")} ${logColor.cyanBold("Nodefony")}${tag}${env}${deploy}`);
1464
+ console.log(` ${logColor.blackBright(meta)}\n`);
1465
+ }
1466
+ logEnv() {
1467
+ if (this.cli) this.runProfile = this.cli.runProfile;
1468
+ const profileLabel = this.runProfile.servers ? "server" : "console";
1469
+ let txt = ` \x1b ${logColor.blue(profileLabel)} `;
1470
+ txt += ` ${logColor.magenta("Cluster")} : ${this.typeCluster} `;
1471
+ txt += ` ${logColor.magenta("Nodefony Environment")} : ${this.environment} `;
1472
+ if (this.appEnvironment) txt += ` ${logColor.magenta("App Environment")} : ${this.appEnvironment.environment} `;
1473
+ txt += ` ${logColor.magenta("Debug")} : ${this.debug}\n`;
1474
+ return txt;
1475
+ }
1476
+ clusterIsMaster() {
1477
+ return cluster.isPrimary;
1478
+ }
1479
+ /**
1480
+ * Vide le buffer des `MODULE ADD` accumulés pendant `loadApp()` et bascule
1481
+ * `addModule()` en mode log immédiat. Appelé par `initCluster()` juste
1482
+ * après l'en-tête SERVER, pour que les modules apparaissent SOUS le banner.
1483
+ */
1484
+ flushPendingModuleAddLogs() {
1485
+ if (!this.pendingModuleAddLogs) return;
1486
+ const pending = this.pendingModuleAddLogs;
1487
+ this.pendingModuleAddLogs = null;
1488
+ for (const name of pending) this.log(`MODULE ADD : ${name}`, "INFO");
1489
+ }
1490
+ initCluster() {
1491
+ this.pid = process.pid;
1492
+ if (this.console && this.cli && this.cli.commander && this.cli.commander.opts().json) return;
1493
+ if (cluster.isPrimary) {
1494
+ if (!this.reporterOwnsHeader && !this.cli?.quietBoot) console.log(this.logEnv());
1495
+ this.flushPendingModuleAddLogs();
1496
+ this.fire("onCluster", "MASTER", this, process);
1497
+ } else if (cluster.isWorker) {
1498
+ if (!this.reporterOwnsHeader && !this.cli?.quietBoot) console.log(this.logEnv());
1499
+ this.flushPendingModuleAddLogs();
1500
+ this.workerId = cluster.worker?.id;
1501
+ this.worker = cluster.worker;
1502
+ this.fire("onCluster", "WORKER", this, process);
1503
+ process.on("message", (msg) => {
1504
+ if (isClusterMessage(msg) && (msg.kind === "nf:rt" || msg.kind === "nf:probe:snap")) return;
1505
+ this.log(msg, "INFO", "IPC MESSAGE");
1506
+ this.fire("onMessage", msg);
1507
+ });
1508
+ }
1509
+ }
1510
+ fire(event, ...args) {
1511
+ this.log(`${colorLogEvent()} ${event}`, "DEBUG");
1512
+ return super.fire(event, ...args);
1513
+ }
1514
+ emit(event, ...args) {
1515
+ this.log(`${colorLogEvent()} ${event}`, "DEBUG");
1516
+ return super.emit(event, ...args);
1517
+ }
1518
+ emitAsync(event, ...args) {
1519
+ this.log(`${colorLogEvent()} ${event}`, "DEBUG");
1520
+ return super.emitAsync(event, ...args);
1521
+ }
1522
+ fireAsync(event, ...args) {
1523
+ this.log(`${colorLogEvent()} ${event}`, "DEBUG");
1524
+ return super.emitAsync(event, ...args);
1525
+ }
1526
+ /**
1527
+ * Timeout par listener appliqué au boot (résilience Phase 3). Précédence :
1528
+ * `NF_BOOT_TIMEOUT_MS` (env, orchestrateur) > défaut par env (dev 20 s,
1529
+ * prod 60 s). Large à dessein : il borne la PENDAISON infinie (ex. file Redis
1530
+ * offline qui ne rejette jamais), pas la lenteur normale d'un hook.
1531
+ */
1532
+ bootTimeoutMs() {
1533
+ const env = Number(process.env.NF_BOOT_TIMEOUT_MS);
1534
+ if (Number.isFinite(env) && env > 0) return env;
1535
+ return this.environment === "production" ? 6e4 : 2e4;
1536
+ }
1537
+ /**
1538
+ * Seuil d'alerte de lenteur d'un hook de boot (NOTICE, sans le tuer). Précédence :
1539
+ * `NF_BOOT_WARN_MS` (env) > défaut 5 s. `0` désactive la mesure.
1540
+ */
1541
+ bootWarnMs() {
1542
+ const env = Number(process.env.NF_BOOT_WARN_MS);
1543
+ if (Number.isFinite(env) && env >= 0) return env;
1544
+ return 5e3;
1545
+ }
1546
+ /**
1547
+ * Politique d'échec de boot commune (lifecycle + `initialize`). Log + verdict
1548
+ * de propagation. **Fatal** = module critique (tag `critical !== false`) ET
1549
+ * (`production` OU erreur de **configuration** {@link BootConfigurationError})
1550
+ * → on interrompt le boot (le pod crashe, l'orchestrateur le redémarre —
1551
+ * cloud-native). Sinon **fail-soft** : WARNING, le boot continue.
1552
+ *
1553
+ * **Pourquoi la config est fatale même en dev** : le fail-soft protège la DX
1554
+ * d'un module optionnel cassé — mais une configuration EXPLICITE non
1555
+ * honorable (infra déclarée injoignable, entité non portée sur le dialecte
1556
+ * demandé) ne se répare pas en continuant : le serveur « vivant » qui en
1557
+ * résulte est un piège (briques durables mortes, login impossible, cause
1558
+ * noyée dans un WARNING — vécu). Cf {@link BootConfigurationError}.
1559
+ *
1560
+ * @param error - erreur/timeout capturé.
1561
+ * @param owner - module propriétaire (tag), ou `undefined` (listener interne).
1562
+ * @param critical - criticité (tag) ; `undefined` → traité comme critique.
1563
+ * @param timedOut - `true` si l'échec est un dépassement de timeout.
1564
+ * @returns `true` si l'échec doit interrompre le boot.
1565
+ */
1566
+ isBootErrorFatal(error, owner, critical, timedOut, phase) {
1567
+ const who = owner ?? "(anonyme)";
1568
+ const configError = BootConfigurationError.is(error);
1569
+ const heldBy = this.#readinessHold();
1570
+ const fatal = configError || critical !== false && this.environment === "production" && heldBy === null;
1571
+ const msg = error instanceof Error ? error.message : String(error);
1572
+ const tag = timedOut ? " [timeout]" : "";
1573
+ this.log(`boot lifecycle: ${fatal ? configError ? "erreur de CONFIGURATION (boot interrompu)" : "échec critique" : "échec non bloquant (fail-soft)"} de "${who}"${tag} — ${msg}`, fatal ? "ERROR" : "WARNING");
1574
+ if (this.debug && error instanceof Error && error.stack) this.log(error.stack, "DEBUG");
1575
+ if (!fatal && critical !== false) this.log(`boot lifecycle: en production, cet échec de "${who}" INTERROMPRAIT le boot` + (critical === void 0 ? " — ce hook ne porte aucun tag de criticité, et un hook non tagué est traité comme CRITIQUE. S'il vient d'un module (ou d'un de ses services), le poser avec `module.hookKernel(\"<event>\", …)` plutôt que `kernel.once(…)` : il héritera du nom et de la criticité du module. `static critical = false` ne couvre QUE les hooks de classe — un hook posé à la main y échappe." : `.`), "WARNING");
1576
+ if (!fatal && heldBy !== null) this.log(`boot lifecycle: l'échec de "${who}" n'interrompt PAS le boot — la mise en service est retenue par ${heldBy}. Cet exemplaire reste vivant et ne reçoit aucun trafic (/readyz rend 503) ; il attend le geste qui lèvera la rétention.`, "WARNING");
1577
+ if (!fatal) this.recordBootFailure({
1578
+ module: who,
1579
+ reason: msg,
1580
+ phase,
1581
+ timedOut
1582
+ });
1583
+ return fatal;
1584
+ }
1585
+ /**
1586
+ * Qui retient la mise en service, en clair — ou `null` si rien ne la retient.
1587
+ *
1588
+ * Lu au moment d'un échec de boot pour décider si l'exemplaire doit mourir.
1589
+ * Ne consulte que l'état DÉJÀ calculé par le registre : aucun appel réseau,
1590
+ * aucune sonde déclenchée ici.
1591
+ *
1592
+ * @returns les contributeurs qui retiennent, séparés par « ; », ou `null`.
1593
+ */
1594
+ #readinessHold() {
1595
+ const bloquants = this.readinessReport().filter((c) => !c.ready);
1596
+ if (bloquants.length === 0) return null;
1597
+ return bloquants.map((c) => c.reason ? `${c.name} (${c.reason})` : c.name).join(" ; ");
1598
+ }
1599
+ /**
1600
+ * Enregistre un échec de boot **non fatal** (fail-soft) dans le buffer agrégé,
1601
+ * alloué paresseusement au premier échec (0 allocation si le boot est nominal).
1602
+ *
1603
+ * @param failure - module en échec + raison + étape.
1604
+ */
1605
+ recordBootFailure(failure) {
1606
+ (this.bootFailures ??= []).push(failure);
1607
+ }
1608
+ /**
1609
+ * Fige la liste des serveurs **réellement en écoute** à `onPostReady`
1610
+ * (`initServers()` ne retourne un serveur qu'une fois son `listen()` résolu).
1611
+ * Source de vérité de {@link getBootReport}.
1612
+ *
1613
+ * @param servers - instances de serveurs retournées par `initServers()`.
1614
+ */
1615
+ captureBootServers(servers) {
1616
+ if (!servers.length) {
1617
+ this.bootServers = [];
1618
+ return;
1619
+ }
1620
+ this.bootServers = servers.map((s) => {
1621
+ const type = typeof s?.type === "string" ? s.type : "server";
1622
+ const port = Number(s?.port ?? 0);
1623
+ const address = typeof s?.address === "string" ? s.address : void 0;
1624
+ const scheme = SERVER_SCHEME[type] ?? type;
1625
+ return {
1626
+ type,
1627
+ scheme,
1628
+ port,
1629
+ address,
1630
+ url: `${scheme}://${address ? address.includes(":") ? `[${address}]` : address : "127.0.0.1"}:${port}`
1631
+ };
1632
+ });
1633
+ }
1634
+ /**
1635
+ * Verdict agrégé du dernier boot (vérité unique, recalculable à la demande pour
1636
+ * l'introspection Studio/IA). `healthy=false` ⇒ un profil serveur a fini sans
1637
+ * aucun serveur en écoute (garde-fou 0-serveur). Les modules ignorés seuls
1638
+ * laissent le boot `healthy` (dégradé mais vivant).
1639
+ *
1640
+ * @returns le {@link IBootReport}.
1641
+ */
1642
+ getBootReport() {
1643
+ const measured = this.bootServers !== null;
1644
+ const serversListening = this.bootServers ?? [];
1645
+ const serversExpected = Boolean(this.runProfile?.servers);
1646
+ const modulesSkipped = this.bootFailures ?? [];
1647
+ const { warnings, errors, criticals } = this.bootLogCounts ?? this.countBootLogIssues();
1648
+ const manifestEntries = Array.isArray(this.options.modules) ? this.options.modules.length : 0;
1649
+ return {
1650
+ durationMs: this.bootStartedAt > 0 ? Date.now() - this.bootStartedAt : 0,
1651
+ modulesLoaded: Object.keys(this.modules),
1652
+ manifestEntries,
1653
+ modulesSkipped,
1654
+ modulesGated: this.modulesGated ?? [],
1655
+ warnings,
1656
+ errors,
1657
+ criticals,
1658
+ serversExpected,
1659
+ serversListening,
1660
+ healthy: !(serversExpected && measured && serversListening.length === 0),
1661
+ remediation: this.bootRemediationHint(modulesSkipped, manifestEntries, serversExpected) ?? void 0
1662
+ };
1663
+ }
1664
+ /**
1665
+ * Nombre de composants qui retiennent la mise en service — `0` = disponible.
1666
+ *
1667
+ * C'est ce que lit la sonde `/readyz`, et c'est pour elle que ce membre est un
1668
+ * ENTIER déjà calculé : une comparaison, aucune allocation, aucun `await`,
1669
+ * aucune vérification déclenchée. Une sonde qui interrogerait la base tomberait
1670
+ * avec elle ; celle-ci lit un verdict que le contributeur a posé lui-même.
1671
+ *
1672
+ * @returns le nombre de contributeurs non prêts (`0` quand personne n'est inscrit)
1673
+ */
1674
+ /**
1675
+ * Signature du dernier état publié sur le disque — `null` tant que rien ne
1676
+ * l'a été. Sert UNIQUEMENT à ne pas réécrire un fichier identique.
1677
+ */
1678
+ #readinessPublished = null;
1679
+ get readinessBlocked() {
1680
+ return this.readiness === null ? 0 : this.readiness.blocked;
1681
+ }
1682
+ /**
1683
+ * Ce processus a-t-il le droit de recevoir du trafic ?
1684
+ *
1685
+ * **La règle de mise en service, en UN seul endroit.** Elle est lue par la
1686
+ * sonde `/readyz` (que le kubelet interroge) ET par le champ `ready` du plan
1687
+ * d'administration (que lit un exploitant) : deux vérités sur la
1688
+ * disponibilité, c'en est une de trop — quelqu'un qui voit `ready: true` d'un
1689
+ * côté pendant que l'orchestrateur reçoit 503 de l'autre cherche au mauvais
1690
+ * endroit. Les deux ont divergé (`booted` contre `postReady`), et l'écart ne
1691
+ * s'est vu qu'en production, où la fin du cycle de démarrage arrive assez tard
1692
+ * pour ouvrir une fenêtre de plusieurs secondes.
1693
+ *
1694
+ * Deux conditions, et rien d'autre :
1695
+ *
1696
+ * - **le cycle de démarrage est TERMINÉ** (`postReady`) — `booted` ne suffit
1697
+ * pas : les écouteurs de `onPostReady` montent encore des choses dont une
1698
+ * requête a besoin ;
1699
+ * - **aucun composant ne retient la mise en service** — un entier déjà
1700
+ * calculé par eux ({@link Kernel.setReadiness}), jamais une vérification
1701
+ * déclenchée ici : une sonde ne doit tomber avec RIEN.
1702
+ *
1703
+ * Ce que ce getter ne connaît pas, volontairement : l'ARRÊT. Le drain
1704
+ * appartient au transport, qui bascule sa propre sonde à 503 avant même de
1705
+ * commencer à drainer — et `/livez`, lui, doit rester vert pendant ce
1706
+ * temps, sans quoi un redémarrage casserait l'arrêt gracieux en cours.
1707
+ *
1708
+ * @returns `true` si le processus peut servir du trafic.
1709
+ */
1710
+ get servable() {
1711
+ return this.postReady === true && this.readinessBlocked === 0;
1712
+ }
1713
+ /**
1714
+ * Pose le verdict de disponibilité d'un composant nommé — un schéma de base en
1715
+ * retard, un cache froid, un service tiers muet. Tant qu'un seul contributeur
1716
+ * répond `false`, `/readyz` rend **503** et l'orchestrateur n'envoie pas de
1717
+ * trafic ; le processus reste VIVANT (`/livez` inchangé), car un état externe
1718
+ * ne se répare pas en redémarrant le processus.
1719
+ *
1720
+ * Le verdict doit être DÉJÀ CALCULÉ : ce n'est pas une vérification que la
1721
+ * sonde déclenchera, c'est un booléen que le composant met à jour à son propre
1722
+ * rythme. Dès qu'il repose `true`, le processus redevient disponible tout seul
1723
+ * — sans redéploiement, ce qui est toute la raison d'être du mécanisme.
1724
+ *
1725
+ * **`blocking: false` publie l'état SANS retenir le trafic.** Un composant
1726
+ * réglé pour tolérer sa propre panne (« journalise et sers quand même ») doit
1727
+ * pouvoir le dire sans disparaître du registre : c'est sa présence, et non la
1728
+ * rétention, qui apprend au noyau qu'un état EXTERNE est en cours et qu'un
1729
+ * échec de démarrage ne doit donc pas être fatal
1730
+ * ({@link Kernel.isBootErrorFatal}). Sans cette distinction, affaiblir la
1731
+ * sonde faisait perdre la tolérance du démarrage — au moment précis où l'on
1732
+ * cherchait de l'air.
1733
+ *
1734
+ * Idempotent par nom : réenregistrer le même nom remplace son verdict.
1735
+ *
1736
+ * @param name - nom du contributeur, qui apparaîtra au journal (`"drizzle:schema"`)
1737
+ * @param ready - `false` retient la mise en service, `true` la libère
1738
+ * @param reason - ce qui retient, en clair (journalisé ; ignoré si `ready`)
1739
+ */
1740
+ setReadiness(name, ready, reason, blocking = true) {
1741
+ if (this.readiness === null) this.readiness = new ReadinessRegistry();
1742
+ if (this.readiness.set(name, ready, reason, blocking)) this.logReadinessFlip();
1743
+ this.publishReadinessState();
1744
+ }
1745
+ /**
1746
+ * Retire un contributeur du registre — sa voix ne compte plus. Geste d'un
1747
+ * module qui s'arrête ; le registre est LIBÉRÉ quand le dernier part, pour que
1748
+ * la sonde retrouve exactement le coût qu'elle avait avant toute inscription.
1749
+ *
1750
+ * @param name - nom du contributeur, tel que passé à {@link Kernel.setReadiness}
1751
+ */
1752
+ clearReadiness(name) {
1753
+ if (this.readiness === null) return;
1754
+ const flipped = this.readiness.clear(name);
1755
+ if (this.readiness.size === 0) this.readiness = null;
1756
+ if (flipped) this.logReadinessFlip();
1757
+ this.publishReadinessState();
1758
+ }
1759
+ /**
1760
+ * Restitue l'état de chaque contributeur — pour un diagnostic (journal,
1761
+ * `status`, console d'administration), jamais pour la sonde.
1762
+ *
1763
+ * @returns un contributeur par inscription, triés par nom ; vide si aucun
1764
+ */
1765
+ readinessReport() {
1766
+ return this.readiness === null ? [] : this.readiness.report();
1767
+ }
1768
+ /**
1769
+ * Publie sur le disque QUI retient la mise en service — le canal que lit
1770
+ * `nodefony status`.
1771
+ *
1772
+ * **Pourquoi un fichier plutôt que la sonde** : le détail existe déjà sur
1773
+ * `livez`, mais réservé à un appelant authentifié, et cette route est publique
1774
+ * en production. Le système de fichiers est la vraie frontière « local », il
1775
+ * répond même quand la boucle d'événements ne répond plus, et il n'ouvre
1776
+ * aucune surface publique. Détail : {@link readinessStateFile}.
1777
+ *
1778
+ * **N'écrit que si l'état a CHANGÉ.** En régime établi — un contributeur qui
1779
+ * repose le même verdict toutes les quinze secondes — ce mécanisme ne touche
1780
+ * pas le disque. La signature est une chaîne courte, composée hors du chemin
1781
+ * d'une requête : `setReadiness` n'est appelé qu'au démarrage et au rythme
1782
+ * propre de chaque contributeur.
1783
+ */
1784
+ publishReadinessState() {
1785
+ const contributors = this.readinessReport();
1786
+ const signature = contributors.map((c) => `${c.name}\u0000${c.ready ? 1 : 0}\u0000${c.reason ?? ""}`).join("");
1787
+ if (signature === this.#readinessPublished) return;
1788
+ this.#readinessPublished = signature;
1789
+ if (contributors.length === 0) {
1790
+ clearReadinessState(this.path);
1791
+ return;
1792
+ }
1793
+ writeReadinessState(this.path, contributors);
1794
+ }
1795
+ /**
1796
+ * Dit au journal ce qui vient de basculer. Appelé UNIQUEMENT quand le verdict
1797
+ * agrégé change (jamais à chaque pose de verdict identique) : un pod retenu
1798
+ * plusieurs heures ne doit pas noyer son propre journal, et un exploitant qui
1799
+ * voit un pod jamais disponible doit trouver ici le NOM de ce qui le retient —
1800
+ * le corps de la sonde, lui, reste une constante pré-allouée.
1801
+ */
1802
+ logReadinessFlip() {
1803
+ const retenant = this.readiness === null ? [] : this.readiness.report().filter((c) => !c.ready);
1804
+ if (retenant.length === 0) {
1805
+ this.log("Mise en service libérée — /readyz rend de nouveau 200", "INFO");
1806
+ return;
1807
+ }
1808
+ const details = retenant.map((c) => c.reason ? `${c.name} (${c.reason})` : c.name).join(", ");
1809
+ this.log(`Mise en service RETENUE — /readyz rend 503 : ${details}`, "CRITIC");
1810
+ }
1811
+ /**
1812
+ * Compte les WARNING (severity 4) et les ERROR-et-pire (severity 0-3 :
1813
+ * EMERGENCY/ALERT/CRITIC/ERROR) présents dans le ring buffer syslog. Pendant le
1814
+ * boot, le ring ne contient QUE des logs de boot → le compte est le « journal
1815
+ * du boot ». Borné par la capacité du ring (compte plancher, jamais gonflé).
1816
+ *
1817
+ * @returns compteurs `{ warnings, errors }`.
1818
+ */
1819
+ countBootLogIssues() {
1820
+ let warnings = 0;
1821
+ let errors = 0;
1822
+ let criticals = null;
1823
+ const ring = this.syslog?.ringStack;
1824
+ if (ring) {
1825
+ for (const pdu of ring) if (pdu.severity === 4) warnings++;
1826
+ else if (pdu.severity >= 0 && pdu.severity <= 3) {
1827
+ errors++;
1828
+ if (criticals === null) criticals = [];
1829
+ if (criticals.length < MAX_BOOT_CRITICALS) criticals.push(this.condenseBootMessage(pdu));
1830
+ }
1831
+ }
1832
+ return {
1833
+ warnings,
1834
+ errors,
1835
+ criticals
1836
+ };
1837
+ }
1838
+ /**
1839
+ * Une entrée de journal ramenée à ce qui tient dans un bilan.
1840
+ *
1841
+ * Le message brut porte parfois une pile entière : la première ligne dit ce
1842
+ * qui s'est passé, le reste appartient au journal. Le module émetteur est
1843
+ * conservé — c'est lui qui répond à « QUI a crié ? », la question qu'un
1844
+ * compte seul laissait entière.
1845
+ *
1846
+ * @param pdu - l'entrée du journal.
1847
+ * @returns une ligne, bornée.
1848
+ */
1849
+ condenseBootMessage(pdu) {
1850
+ const premiere = (String(pdu.payload ?? "").split("\n")[0] ?? "").trim();
1851
+ const message = premiere.length > MAX_BOOT_CRITICAL_LENGTH ? `${premiere.slice(0, 199)}…` : premiere;
1852
+ return `${pdu.moduleName ? `${String(pdu.moduleName)} : ` : ""}${message}`;
1853
+ }
1854
+ /**
1855
+ * Déclare une ligne de détail à afficher SOUS une phase de boot (canal NEUTRE
1856
+ * dev-only : le `BootReporter` la rend ; le core ne connaît pas son contenu).
1857
+ * Un adapter ORM/frontend/… l'appelle à son hook de phase pour que le boot
1858
+ * RACONTE ce qu'il met en place — sans coupler le core au domaine du module.
1859
+ *
1860
+ * @param phase - libellé de la phase (cf `BootReporter` PHASES, ex. « Services & ORM »).
1861
+ * @param line - ligne lisible (ex. « drizzle → sqlite (./var/app.db) »).
1862
+ */
1863
+ reportBootLine(phase, line) {
1864
+ const map = this.bootLines ??= /* @__PURE__ */ new Map();
1865
+ const lines = map.get(phase);
1866
+ if (lines) lines.push(line);
1867
+ else map.set(phase, [line]);
1868
+ }
1869
+ /**
1870
+ * REMPLACE les lignes de détail d'une phase (vs {@link reportBootLine} qui
1871
+ * AJOUTE). Pour un producteur idempotent invoqué plusieurs fois qui reconstruit
1872
+ * la liste complète à chaque appel (ex. le wiring ORM, déclenché par N drivers,
1873
+ * itère tout le registre → « dernier gagne »).
1874
+ *
1875
+ * @param phase - libellé de la phase.
1876
+ * @param lines - liste complète des lignes (remplace l'existant ; vide = efface).
1877
+ */
1878
+ setBootLines(phase, lines) {
1879
+ if (!lines.length) {
1880
+ this.bootLines?.delete(phase);
1881
+ return;
1882
+ }
1883
+ (this.bootLines ??= /* @__PURE__ */ new Map()).set(phase, [...lines]);
1884
+ }
1885
+ /**
1886
+ * Lignes de détail déclarées pour une phase de boot (vide si aucune).
1887
+ *
1888
+ * @param phase - libellé de la phase.
1889
+ * @returns lignes dans l'ordre de déclaration.
1890
+ */
1891
+ getBootLines(phase) {
1892
+ return this.bootLines?.get(phase) ?? [];
1893
+ }
1894
+ /**
1895
+ * Action corrective suggérée d'après les raisons d'échec — surfacée dans le
1896
+ * verdict (écran + log). Heuristique : un `import()` qui échoue (« Cannot find
1897
+ * package/module ») pointe presque toujours un `dist/` périmé après pull/merge.
1898
+ *
1899
+ * Le manifeste VIDE est traité en premier parce qu'il ne produit aucune raison
1900
+ * d'échec : rien n'ayant été tenté, `skipped` est vide et l'heuristique
1901
+ * suivante n'a rien à lire. C'est précisément l'état qui laissait le diagnostic
1902
+ * muet — il faut donc le nommer ici, à défaut de pouvoir le déduire.
1903
+ *
1904
+ * Le manifeste vide n'est signalé que sous un profil SERVEUR : une commande
1905
+ * batch ou un test qui boote sans manifeste est un cas nominal, et une
1906
+ * remédiation posée là serait un faux diagnostic recopié partout où le rapport
1907
+ * est lu.
1908
+ *
1909
+ * @param skipped - modules ignorés.
1910
+ * @param manifestEntries - entrées déclarées au manifeste `config.modules`.
1911
+ * @param serversExpected - le profil d'exécution attendait-il des serveurs.
1912
+ * @returns une phrase d'action, ou `null`.
1913
+ */
1914
+ bootRemediationHint(skipped, manifestEntries, serversExpected) {
1915
+ if (serversExpected && manifestEntries === 0) return "la configuration LUE ne déclare aucun module (`modules: []`) ⇒ vérifier `nodefony.config` et l'exécutable employé (`nodefony inspect config` dit la config effective et sa provenance)";
1916
+ if (skipped.some((f) => /Cannot find package|Cannot find module|ERR_MODULE_NOT_FOUND/i.test(f.reason))) return "dist périmé probable ⇒ npm run clean && npm run build";
1917
+ return null;
1918
+ }
1919
+ /**
1920
+ * Log structuré du verdict de boot — émis **toujours** (prod incluse), donc
1921
+ * indépendant du `BootReporter` (qui n'existe qu'en dev). En non-TTY/prod c'est
1922
+ * la seule trace écran ; en dev TTY animé le sink est muté → va au backplane,
1923
+ * et le `BootReporter` rend le verdict joli (✓/⚠/⛔).
1924
+ *
1925
+ * @param report - verdict à logger.
1926
+ */
1927
+ logBootVerdict(report) {
1928
+ const skipped = report.modulesSkipped;
1929
+ if (!report.healthy) {
1930
+ const reasons = skipped.map((f) => `${f.module}: ${f.reason}`).join(" · ");
1931
+ this.log(`BOOT ÉCHEC — profil serveur mais aucun serveur en écoute · manifeste : ${report.manifestEntries} module(s) déclaré(s), ${report.modulesLoaded.length} chargé(s)` + (skipped.length ? ` · ${skipped.length} module(s) en échec : ${reasons}` : "") + (report.remediation ? ` — ${report.remediation}` : ""), "CRITIC");
1932
+ return;
1933
+ }
1934
+ if (skipped.length) {
1935
+ this.log(`BOOT dégradé — ${report.modulesLoaded.length} module(s) chargé(s), ${skipped.length} en échec (fail-soft) : ` + skipped.map((f) => `${f.module} (${f.reason})`).join(" · "), "WARNING");
1936
+ return;
1937
+ }
1938
+ const urls = report.serversListening.map((s) => s.url).join(", ");
1939
+ const gated = report.modulesGated.length ? `, ${report.modulesGated.length} ignoré(s) (policy/when)` : "";
1940
+ const journal = report.warnings || report.errors ? ` · journal : ${report.errors} ERROR, ${report.warnings} WARNING` : "";
1941
+ this.log(`BOOT ok — ${report.modulesLoaded.length} module(s)${gated}, ${report.serversListening.length} serveur(s) en écoute` + (urls ? ` (${urls})` : "") + journal, "NOTICE");
1942
+ const logCfg = this.options.log;
1943
+ if (shouldWarnPerPodView(isInKubernetes(), getActiveLogDriver()?.name, Boolean(logCfg?.loki?.url || logCfg?.opensearch?.url))) this.log(`Kubernetes détecté + log.queryDriver="${getActiveLogDriver()?.name}" (vue PAR POD) : la relecture des logs ne couvre que CE pod. En multi-pod (replicas > 1), configure log.queryDriver="loki"|"opensearch" (+ URL) pour une vue globale.`, "NOTICE", "SYSLOG");
1944
+ }
1945
+ /**
1946
+ * Émet une phase de **lifecycle de boot** de façon GARDÉE (cf
1947
+ * `Event.emitAsyncGuarded`) : chaque hook de module est isolé par try/catch +
1948
+ * timeout ; un module qui throw ou se fige ne gèle/ne tue plus tout le boot. La
1949
+ * politique (propager vs fail-soft) est tranchée par {@link isBootErrorFatal}
1950
+ * d'après les tags `owner`/`critical` posés par `Module.setEvents()`.
1951
+ *
1952
+ * Remplace `fireAsync` **uniquement** dans la chaîne boot (`onPreRegister` →
1953
+ * `onPostReady`). Le hot path HTTP/WS garde `emitAsync` nu (aucun timer/alloc).
1954
+ *
1955
+ * @param event - phase lifecycle à émettre.
1956
+ * @param args - arguments passés aux hooks (typiquement `this`).
1957
+ * @returns le {@link IGuardedEmitResult} (results / errors / stopped).
1958
+ * @throws l'erreur d'un module critique en production (interrompt le boot).
1959
+ */
1960
+ async fireLifecycle(event, ...args) {
1961
+ this.log(`${colorLogEvent()} ${event} [guarded]`, "DEBUG");
1962
+ const warnMs = this.bootWarnMs();
1963
+ let fatalError = null;
1964
+ let hasFatal = false;
1965
+ const bootTimeout = this.bootTimeoutMs();
1966
+ const result = await super.emitAsyncGuarded(event, {
1967
+ timeoutMs: (listener) => isUnboundedListener(listener) ? 0 : bootTimeout,
1968
+ warnMs,
1969
+ onListenerError: (error, info) => {
1970
+ const { owner, critical, name } = readListenerTags(info.listener);
1971
+ if (this.isBootErrorFatal(error, owner ?? name, critical, info.timedOut, "lifecycle")) {
1972
+ fatalError = error;
1973
+ hasFatal = true;
1974
+ return true;
1975
+ }
1976
+ },
1977
+ onListenerSlow: (info) => {
1978
+ const { owner, name } = readListenerTags(info.listener);
1979
+ this.log(`boot lifecycle: hook "${owner ?? name ?? "(anonyme)"}" lent (${Math.round(info.durationMs)}ms ≥ ${warnMs}ms) sur ${event}`, "NOTICE");
1980
+ }
1981
+ }, ...args);
1982
+ if (hasFatal) throw fatalError instanceof Error ? fatalError : new Error(String(fatalError));
1983
+ return result;
1984
+ }
1985
+ /**
1986
+ * Initialise un service **sous garde de boot** (timeout + politique de criticité
1987
+ * via {@link guardInitialize}) si le service expose le hook `init`. Point
1988
+ * d'entrée commun à {@link addKernelService} (service kernel, `critical=true`) et
1989
+ * à {@link Module.addService} (service de module, criticité = celle du module
1990
+ * porteur), pour que les deux chemins d'init partagent la même résilience.
1991
+ *
1992
+ * @remarks Avant 2026-05-29 les services de module passaient par un `await` NU
1993
+ * (aucun timeout) : un `init` qui pend (ex. `redis` = connexion réseau)
1994
+ * gelait le boot indéfiniment. Désormais borné comme les services kernel.
1995
+ *
1996
+ * @param serviceInit - instance du service ; ne fait rien si `init` absent.
1997
+ * @param owner - propriétaire passé au hook : le {@link Module} ou ce {@link Kernel}.
1998
+ * @param critical - criticité transmise à {@link isBootErrorFatal}.
1999
+ */
2000
+ async guardServiceInitialize(serviceInit, owner, critical) {
2001
+ if (!serviceInit.init) return;
2002
+ this.log(`SERVICE INITIALIZE : ${serviceInit.name}`, "DEBUG");
2003
+ const init = serviceInit.init.bind(serviceInit);
2004
+ await this.guardInitialize(() => init(owner), serviceInit.name, critical);
2005
+ }
2006
+ /**
2007
+ * Applique la politique de criticité du boot à l'échec d'un service déclaré via
2008
+ * `@services([...])` — **construction (`new`) comprise**, pas seulement `init`.
2009
+ * Pendant symétrique de {@link guardServiceInitialize} pour le chemin décorateur.
2010
+ *
2011
+ * @remarks Le `catch` du décorateur `@services` avalait auparavant TOUT échec
2012
+ * (`log(e, "ERROR")` et rien d'autre) : il n'atteignait ni cette politique —
2013
+ * donc jamais fatal, **même en production** — ni le {@link getBootReport}, si
2014
+ * bien qu'un boot amputé d'un service critique se déclarait « UP ». Un service
2015
+ * qu'on ne peut pas CONSTRUIRE doit suivre exactement la règle de celui qu'on
2016
+ * ne peut pas INITIALISER.
2017
+ *
2018
+ * @param error - l'échec remonté par `addService`/`loadService`.
2019
+ * @param serviceName - nom lisible (pour le log + le BootReport).
2020
+ * @param critical - criticité du module porteur ({@link Module.critical}).
2021
+ * @returns `true` si l'échec est fatal → l'appelant **doit** propager.
2022
+ */
2023
+ serviceBootErrorFatal(error, serviceName, critical) {
2024
+ return this.isBootErrorFatal(error, serviceName, critical, false, "init");
2025
+ }
2026
+ /**
2027
+ * Exécute un `initialize()` (module ou service kernel) sous garde de boot :
2028
+ * borné par {@link bootTimeoutMs} et soumis à {@link isBootErrorFatal}. Un
2029
+ * `initialize` qui se fige (connexion réseau qui pend) ne gèle plus le boot.
2030
+ *
2031
+ * @param run - thunk qui lance l'`initialize` (déjà lié à son instance).
2032
+ * @param owner - nom du module/service (pour le log).
2033
+ * @param critical - criticité (module → `Module.critical` ; service kernel → `true`).
2034
+ */
2035
+ async guardInitialize(run, owner, critical) {
2036
+ try {
2037
+ await withTimeout(Promise.resolve(run()), this.bootTimeoutMs(), `initialize ${owner}`);
2038
+ } catch (error) {
2039
+ const timedOut = error instanceof TimeoutError;
2040
+ if (this.isBootErrorFatal(error, owner, critical, timedOut, "init")) throw error;
2041
+ }
2042
+ }
2043
+ checkPath(myPath) {
2044
+ if (!myPath) return null;
2045
+ if (path.isAbsolute(myPath)) return myPath;
2046
+ return path.resolve(this.path, myPath);
2047
+ }
2048
+ sendMessage(message, handle, options, callback) {
2049
+ if (process.send) return process.send({
2050
+ type: "process:msg",
2051
+ data: message
2052
+ }, handle, options, callback);
2053
+ throw new Error(`process.send not found `);
2054
+ }
2055
+ memoryUsage(message, severity = "DEBUG") {
2056
+ const { memory } = this.stats();
2057
+ for (const ele in memory) switch (ele) {
2058
+ case "rss":
2059
+ this.log(`${message || ele} ( Resident Set Size ) PID ( ${this.pid} ) : ${CliKernel.niceBytes(memory[ele])}`, severity, `MEMORY ${ele}`);
2060
+ break;
2061
+ case "heapTotal":
2062
+ this.log(`${message || ele} ( Total Size of the Heap ) PID ( ${this.pid} ) : ${CliKernel.niceBytes(memory[ele])}`, severity, `MEMORY ${ele}`);
2063
+ break;
2064
+ case "heapUsed":
2065
+ this.log(`${message || ele} ( Heap actually Used ) PID ( ${this.pid} ) : ${CliKernel.niceBytes(memory[ele])}`, severity, `MEMORY ${ele}`);
2066
+ break;
2067
+ case "external": this.log(`${message || ele} PID ( ${this.pid} ) : ${CliKernel.niceBytes(memory[ele])}`, severity, `MEMORY ${ele}`);
2068
+ }
2069
+ }
2070
+ stats() {
2071
+ return { memory: process.memoryUsage() };
2072
+ }
2073
+ interfacesFilter(filters) {
2074
+ if (filters) {
2075
+ let condition = (ele, ele2) => {
2076
+ return ele && ele2;
2077
+ };
2078
+ if (filters.condition && filters.condition === "||") condition = (ele, ele2) => {
2079
+ return ele || ele2;
2080
+ };
2081
+ const interfaces = {};
2082
+ for (const myinterface in this.interfaces) {
2083
+ interfaces[myinterface] = [];
2084
+ for (const infos of this.interfaces[myinterface]) {
2085
+ let matchType = false;
2086
+ let matchFamily = false;
2087
+ for (const filter in filters) switch (filter) {
2088
+ case "type":
2089
+ if (filters["type"] === "external") {
2090
+ if (!infos.internal) matchType = true;
2091
+ }
2092
+ if (filters["type"] === "local") {
2093
+ if (infos.internal) matchType = true;
2094
+ }
2095
+ break;
2096
+ case "family": if (filters["family"] === infos.family) matchFamily = true;
2097
+ }
2098
+ if (condition(matchType, matchFamily)) interfaces[myinterface].push(infos);
2099
+ }
2100
+ }
2101
+ return interfaces;
2102
+ }
2103
+ return this.interfaces;
2104
+ }
2105
+ getNetworkInterfaces() {
2106
+ const nets = os.networkInterfaces();
2107
+ const devices = {};
2108
+ Object.entries(nets).forEach(([name, ni]) => {
2109
+ if (!ni) return;
2110
+ devices[name] = ni;
2111
+ });
2112
+ return devices;
2113
+ }
2114
+ getNetwork() {
2115
+ return {
2116
+ external: this.interfacesFilter({ type: "external" }),
2117
+ local: this.interfacesFilter({
2118
+ type: "local",
2119
+ condition: "=="
2120
+ }),
2121
+ ipv4: this.interfacesFilter({ family: "IPv4" }),
2122
+ ipv6: this.interfacesFilter({ family: "IPv6" }),
2123
+ interfaces: this.interfaces
2124
+ };
2125
+ }
2126
+ getFirstExternalInterface(family) {
2127
+ const filter = {
2128
+ type: "external",
2129
+ family: family || "IPv4",
2130
+ condition: "&&"
2131
+ };
2132
+ const res = this.interfacesFilter(filter);
2133
+ const ele = [];
2134
+ for (const myinterface in res) for (const info of res[myinterface]) ele.push(info);
2135
+ return ele[0] || void 0;
2136
+ }
2137
+ /**
2138
+ * Shutdown propre du kernel — fire `"onTerminate"` puis `CliKernel.quit(code)` sur next tick.
2139
+ *
2140
+ * Le drain complet (`fireAsync("onTerminate")` : bascule readiness → close WS
2141
+ * 1001 → drain HTTP → cleanup services) est borné par la deadline GLOBALE
2142
+ * `shutdownDeadline` (défaut 15 s, < grace period orchestrateur) : un listener
2143
+ * qui pend (SSE ouvert, store bloqué, module tiers) force la sortie code 1 —
2144
+ * jamais un process zombie qui attend le SIGKILL externe. `0` = filet désactivé.
2145
+ *
2146
+ * @param code - exit code Unix (0 = succès, 1+ = erreur).
2147
+ * @param quiet - muselle l'AFFICHAGE (le log « terminate : N ») — jamais le
2148
+ * drain : une sortie voulue par l'utilisateur (Ctrl+C d'un menu) n'a pas à
2149
+ * ressembler à un événement système.
2150
+ * @returns Promise résolue avec `this` (ou rejected si `quit()` throw).
2151
+ */
2152
+ async terminate(code, quiet) {
2153
+ if (code === void 0) code = typeof process.exitCode === "number" ? process.exitCode : 0;
2154
+ if (this.terminating) return this.terminating;
2155
+ this.terminating = this.doTerminate(code, quiet);
2156
+ return this.terminating;
2157
+ }
2158
+ /** Le shutdown lui-même — protégé de la ré-entrance par {@link terminate}. */
2159
+ async doTerminate(code, quiet) {
2160
+ if (this.parkTimer) {
2161
+ clearInterval(this.parkTimer);
2162
+ this.parkTimer = null;
2163
+ }
2164
+ if (!quiet) this.log(`terminate : ${code}`);
2165
+ const drain = this.fireAsync("onTerminate", this, code).catch((e) => {
2166
+ this.log(e, "ERROR");
2167
+ return SHUTDOWN_DRAIN_ERROR;
2168
+ });
2169
+ const deadlineMs = this.options.shutdownDeadline ?? DEFAULT_SHUTDOWN_DEADLINE;
2170
+ let raced;
2171
+ if (deadlineMs > 0) {
2172
+ let deadlineTimer = null;
2173
+ raced = await Promise.race([drain, new Promise((resolve) => {
2174
+ deadlineTimer = setTimeout(() => resolve(SHUTDOWN_DEADLINE), deadlineMs);
2175
+ deadlineTimer.unref();
2176
+ })]);
2177
+ if (deadlineTimer !== null) clearTimeout(deadlineTimer);
2178
+ } else raced = await drain;
2179
+ if (raced === SHUTDOWN_DEADLINE) {
2180
+ this.log(`shutdown deadline exceeded (${deadlineMs} ms) — forcing exit, onTerminate listeners still pending`, "CRITIC");
2181
+ code = 1;
2182
+ } else if (raced === SHUTDOWN_DRAIN_ERROR) code = 1;
2183
+ return new Promise((resolve, reject) => {
2184
+ process.nextTick(() => {
2185
+ this.log(`NODEFONY Kernel Life Cycle Terminate CODE : ${code}`, "DEBUG");
2186
+ try {
2187
+ CliKernel.quit(code);
2188
+ return resolve(this);
2189
+ } catch (e) {
2190
+ this.log(e, "ERROR");
2191
+ return reject(e);
2192
+ }
2193
+ });
2194
+ });
2195
+ }
2196
+ };
2197
+ //#endregion
2198
+ export { CONSOLE_RUN_PROFILE, Events, FORCE_DEV_MODULES_ENV, FORCE_DEV_MODULES_TTL_ENV, FORCE_DEV_MODULES_TTL_MAX_MS, FORCE_DEV_MODULES_TTL_MS, Kernel as default, resolveDevModulesTtlMs };