jopijs 0.0.1

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 (455) hide show
  1. package/README.md +134 -0
  2. package/bin/jopib.js +4 -0
  3. package/bin/jopin.js +4 -0
  4. package/dist/@bundler/_logs.d.ts +2 -0
  5. package/dist/@bundler/_logs.js +4 -0
  6. package/dist/@bundler/_logs.js.map +1 -0
  7. package/dist/@bundler/core.d.ts +1 -0
  8. package/dist/@bundler/core.js +34 -0
  9. package/dist/@bundler/core.js.map +1 -0
  10. package/dist/@bundler/esbuild.d.ts +6 -0
  11. package/dist/@bundler/esbuild.js +111 -0
  12. package/dist/@bundler/esbuild.js.map +1 -0
  13. package/dist/@bundler/index.d.ts +2 -0
  14. package/dist/@bundler/index.js +3 -0
  15. package/dist/@bundler/index.js.map +1 -0
  16. package/dist/@bundler/plugins.d.ts +13 -0
  17. package/dist/@bundler/plugins.js +145 -0
  18. package/dist/@bundler/plugins.js.map +1 -0
  19. package/dist/@bundler/tailwind.d.ts +3 -0
  20. package/dist/@bundler/tailwind.js +119 -0
  21. package/dist/@bundler/tailwind.js.map +1 -0
  22. package/dist/@core/PageComponent.d.ts +6 -0
  23. package/dist/@core/PageComponent.js +10 -0
  24. package/dist/@core/PageComponent.js.map +1 -0
  25. package/dist/@core/_logs.d.ts +7 -0
  26. package/dist/@core/_logs.js +9 -0
  27. package/dist/@core/_logs.js.map +1 -0
  28. package/dist/@core/automaticStartStop.d.ts +25 -0
  29. package/dist/@core/automaticStartStop.js +106 -0
  30. package/dist/@core/automaticStartStop.js.map +1 -0
  31. package/dist/@core/browserCacheControl.d.ts +21 -0
  32. package/dist/@core/browserCacheControl.js +17 -0
  33. package/dist/@core/browserCacheControl.js.map +1 -0
  34. package/dist/@core/bundler/bundler.d.ts +22 -0
  35. package/dist/@core/bundler/bundler.js +90 -0
  36. package/dist/@core/bundler/bundler.js.map +1 -0
  37. package/dist/@core/bundler/common.d.ts +2 -0
  38. package/dist/@core/bundler/common.js +17 -0
  39. package/dist/@core/bundler/common.js.map +1 -0
  40. package/dist/@core/bundler/config.d.ts +20 -0
  41. package/dist/@core/bundler/config.js +11 -0
  42. package/dist/@core/bundler/config.js.map +1 -0
  43. package/dist/@core/bundler/extraContent.d.ts +6 -0
  44. package/dist/@core/bundler/extraContent.js +27 -0
  45. package/dist/@core/bundler/extraContent.js.map +1 -0
  46. package/dist/@core/bundler/pagesGenerator.d.ts +1 -0
  47. package/dist/@core/bundler/pagesGenerator.js +173 -0
  48. package/dist/@core/bundler/pagesGenerator.js.map +1 -0
  49. package/dist/@core/bundler/server.d.ts +8 -0
  50. package/dist/@core/bundler/server.js +35 -0
  51. package/dist/@core/bundler/server.js.map +1 -0
  52. package/dist/@core/caches/InMemoryCache.d.ts +35 -0
  53. package/dist/@core/caches/InMemoryCache.js +353 -0
  54. package/dist/@core/caches/InMemoryCache.js.map +1 -0
  55. package/dist/@core/caches/SimpleFileCache.d.ts +18 -0
  56. package/dist/@core/caches/SimpleFileCache.js +171 -0
  57. package/dist/@core/caches/SimpleFileCache.js.map +1 -0
  58. package/dist/@core/caches/cache.d.ts +36 -0
  59. package/dist/@core/caches/cache.js +33 -0
  60. package/dist/@core/caches/cache.js.map +1 -0
  61. package/dist/@core/caches/webSiteMirrorCache.d.ts +16 -0
  62. package/dist/@core/caches/webSiteMirrorCache.js +81 -0
  63. package/dist/@core/caches/webSiteMirrorCache.js.map +1 -0
  64. package/dist/@core/gzip.d.ts +4 -0
  65. package/dist/@core/gzip.js +31 -0
  66. package/dist/@core/gzip.js.map +1 -0
  67. package/dist/@core/index.d.ts +18 -0
  68. package/dist/@core/index.js +19 -0
  69. package/dist/@core/index.js.map +1 -0
  70. package/dist/@core/internalTools.d.ts +14 -0
  71. package/dist/@core/internalTools.js +79 -0
  72. package/dist/@core/internalTools.js.map +1 -0
  73. package/dist/@core/jQuery.d.ts +5 -0
  74. package/dist/@core/jQuery.js +18 -0
  75. package/dist/@core/jQuery.js.map +1 -0
  76. package/dist/@core/jopiEasy.d.ts +287 -0
  77. package/dist/@core/jopiEasy.js +983 -0
  78. package/dist/@core/jopiEasy.js.map +1 -0
  79. package/dist/@core/jopiRequest.d.ts +269 -0
  80. package/dist/@core/jopiRequest.js +1060 -0
  81. package/dist/@core/jopiRequest.js.map +1 -0
  82. package/dist/@core/jopiServer.d.ts +77 -0
  83. package/dist/@core/jopiServer.js +100 -0
  84. package/dist/@core/jopiServer.js.map +1 -0
  85. package/dist/@core/jopiWebSite.d.ts +319 -0
  86. package/dist/@core/jopiWebSite.js +653 -0
  87. package/dist/@core/jopiWebSite.js.map +1 -0
  88. package/dist/@core/letsEncrypt.d.ts +25 -0
  89. package/dist/@core/letsEncrypt.js +189 -0
  90. package/dist/@core/letsEncrypt.js.map +1 -0
  91. package/dist/@core/linker.d.ts +4 -0
  92. package/dist/@core/linker.js +37 -0
  93. package/dist/@core/linker.js.map +1 -0
  94. package/dist/@core/loadBalancing.d.ts +18 -0
  95. package/dist/@core/loadBalancing.js +126 -0
  96. package/dist/@core/loadBalancing.js.map +1 -0
  97. package/dist/@core/middlewares/CorsMiddleware.d.ts +5 -0
  98. package/dist/@core/middlewares/CorsMiddleware.js +15 -0
  99. package/dist/@core/middlewares/CorsMiddleware.js.map +1 -0
  100. package/dist/@core/middlewares/DdosProtection.d.ts +42 -0
  101. package/dist/@core/middlewares/DdosProtection.js +62 -0
  102. package/dist/@core/middlewares/DdosProtection.js.map +1 -0
  103. package/dist/@core/middlewares/index.d.ts +10 -0
  104. package/dist/@core/middlewares/index.js +14 -0
  105. package/dist/@core/middlewares/index.js.map +1 -0
  106. package/dist/@core/publicTools.d.ts +8 -0
  107. package/dist/@core/publicTools.js +11 -0
  108. package/dist/@core/publicTools.js.map +1 -0
  109. package/dist/@core/routeConfig.d.ts +48 -0
  110. package/dist/@core/routeConfig.js +102 -0
  111. package/dist/@core/routeConfig.js.map +1 -0
  112. package/dist/@core/searchParamFilter.d.ts +70 -0
  113. package/dist/@core/searchParamFilter.js +212 -0
  114. package/dist/@core/searchParamFilter.js.map +1 -0
  115. package/dist/@core/serverFetch.d.ts +122 -0
  116. package/dist/@core/serverFetch.js +312 -0
  117. package/dist/@core/serverFetch.js.map +1 -0
  118. package/dist/@core/serverImpl/server_bunjs.d.ts +29 -0
  119. package/dist/@core/serverImpl/server_bunjs.js +180 -0
  120. package/dist/@core/serverImpl/server_bunjs.js.map +1 -0
  121. package/dist/@core/serverImpl/server_nodejs.d.ts +22 -0
  122. package/dist/@core/serverImpl/server_nodejs.js +235 -0
  123. package/dist/@core/serverImpl/server_nodejs.js.map +1 -0
  124. package/dist/@core/serverInstanceBuilder.d.ts +17 -0
  125. package/dist/@core/serverInstanceBuilder.js +14 -0
  126. package/dist/@core/serverInstanceBuilder.js.map +1 -0
  127. package/dist/@core/userStores.d.ts +13 -0
  128. package/dist/@core/userStores.js +20 -0
  129. package/dist/@core/userStores.js.map +1 -0
  130. package/dist/@crawler/common.d.ts +203 -0
  131. package/dist/@crawler/common.js +68 -0
  132. package/dist/@crawler/common.js.map +1 -0
  133. package/dist/@crawler/core.d.ts +87 -0
  134. package/dist/@crawler/core.js +586 -0
  135. package/dist/@crawler/core.js.map +1 -0
  136. package/dist/@crawler/directFileCache.d.ts +10 -0
  137. package/dist/@crawler/directFileCache.js +60 -0
  138. package/dist/@crawler/directFileCache.js.map +1 -0
  139. package/dist/@crawler/index.d.ts +4 -0
  140. package/dist/@crawler/index.js +5 -0
  141. package/dist/@crawler/index.js.map +1 -0
  142. package/dist/@crawler/urlMapping.d.ts +21 -0
  143. package/dist/@crawler/urlMapping.js +52 -0
  144. package/dist/@crawler/urlMapping.js.map +1 -0
  145. package/dist/@crawler/utils.d.ts +6 -0
  146. package/dist/@crawler/utils.js +13 -0
  147. package/dist/@crawler/utils.js.map +1 -0
  148. package/dist/@generated/index.d.ts +29 -0
  149. package/dist/@generated/index.js +93 -0
  150. package/dist/@generated/index.js.map +1 -0
  151. package/dist/@linker/arobaseTypes.d.ts +50 -0
  152. package/dist/@linker/arobaseTypes.js +235 -0
  153. package/dist/@linker/arobaseTypes.js.map +1 -0
  154. package/dist/@linker/changeProof.d.ts +5 -0
  155. package/dist/@linker/changeProof.js +50 -0
  156. package/dist/@linker/changeProof.js.map +1 -0
  157. package/dist/@linker/config.d.ts +2 -0
  158. package/dist/@linker/config.js +40 -0
  159. package/dist/@linker/config.js.map +1 -0
  160. package/dist/@linker/engine.d.ts +156 -0
  161. package/dist/@linker/engine.js +665 -0
  162. package/dist/@linker/engine.js.map +1 -0
  163. package/dist/@linker/index.d.ts +3 -0
  164. package/dist/@linker/index.js +4 -0
  165. package/dist/@linker/index.js.map +1 -0
  166. package/dist/@linker/install.d.ts +4 -0
  167. package/dist/@linker/install.js +42 -0
  168. package/dist/@linker/install.js.map +1 -0
  169. package/dist/@linker/modInstaller.d.ts +10 -0
  170. package/dist/@linker/modInstaller.js +38 -0
  171. package/dist/@linker/modInstaller.js.map +1 -0
  172. package/dist/@linker/typeEvents.d.ts +9 -0
  173. package/dist/@linker/typeEvents.js +48 -0
  174. package/dist/@linker/typeEvents.js.map +1 -0
  175. package/dist/@linker/typeRoutes.d.ts +23 -0
  176. package/dist/@linker/typeRoutes.js +197 -0
  177. package/dist/@linker/typeRoutes.js.map +1 -0
  178. package/dist/@linker/typeUiComposite.d.ts +6 -0
  179. package/dist/@linker/typeUiComposite.js +16 -0
  180. package/dist/@linker/typeUiComposite.js.map +1 -0
  181. package/dist/@loader/index.d.ts +1 -0
  182. package/dist/@loader/index.js +17 -0
  183. package/dist/@loader/index.js.map +1 -0
  184. package/dist/@loader/nodeJsLoader.d.ts +2 -0
  185. package/dist/@loader/nodeJsLoader.js +19 -0
  186. package/dist/@loader/nodeJsLoader.js.map +1 -0
  187. package/dist/@loader-client/index.d.ts +33 -0
  188. package/dist/@loader-client/index.js +98 -0
  189. package/dist/@loader-client/index.js.map +1 -0
  190. package/dist/@loader-tools/bunJsLoader.d.ts +1 -0
  191. package/dist/@loader-tools/bunJsLoader.js +28 -0
  192. package/dist/@loader-tools/bunJsLoader.js.map +1 -0
  193. package/dist/@loader-tools/config.d.ts +49 -0
  194. package/dist/@loader-tools/config.js +79 -0
  195. package/dist/@loader-tools/config.js.map +1 -0
  196. package/dist/@loader-tools/cssModuleCompiler.d.ts +5 -0
  197. package/dist/@loader-tools/cssModuleCompiler.js +65 -0
  198. package/dist/@loader-tools/cssModuleCompiler.js.map +1 -0
  199. package/dist/@loader-tools/esBuildPlugin.d.ts +5 -0
  200. package/dist/@loader-tools/esBuildPlugin.js +113 -0
  201. package/dist/@loader-tools/esBuildPlugin.js.map +1 -0
  202. package/dist/@loader-tools/index.d.ts +9 -0
  203. package/dist/@loader-tools/index.js +10 -0
  204. package/dist/@loader-tools/index.js.map +1 -0
  205. package/dist/@loader-tools/jopin.d.ts +10 -0
  206. package/dist/@loader-tools/jopin.js +331 -0
  207. package/dist/@loader-tools/jopin.js.map +1 -0
  208. package/dist/@loader-tools/nodeJsLoader.d.ts +13 -0
  209. package/dist/@loader-tools/nodeJsLoader.js +101 -0
  210. package/dist/@loader-tools/nodeJsLoader.js.map +1 -0
  211. package/dist/@loader-tools/nodeJsResolver.d.ts +2 -0
  212. package/dist/@loader-tools/nodeJsResolver.js +117 -0
  213. package/dist/@loader-tools/nodeJsResolver.js.map +1 -0
  214. package/dist/@loader-tools/rules.d.ts +3 -0
  215. package/dist/@loader-tools/rules.js +33 -0
  216. package/dist/@loader-tools/rules.js.map +1 -0
  217. package/dist/@loader-tools/sourceChangesWatcher.d.ts +39 -0
  218. package/dist/@loader-tools/sourceChangesWatcher.js +182 -0
  219. package/dist/@loader-tools/sourceChangesWatcher.js.map +1 -0
  220. package/dist/@loader-tools/tools.d.ts +21 -0
  221. package/dist/@loader-tools/tools.js +113 -0
  222. package/dist/@loader-tools/tools.js.map +1 -0
  223. package/dist/@loader-tools/transform.d.ts +5 -0
  224. package/dist/@loader-tools/transform.js +114 -0
  225. package/dist/@loader-tools/transform.js.map +1 -0
  226. package/dist/@loader-tools/virtualUrl.d.ts +9 -0
  227. package/dist/@loader-tools/virtualUrl.js +40 -0
  228. package/dist/@loader-tools/virtualUrl.js.map +1 -0
  229. package/dist/@tool-jopinb/index.d.ts +1 -0
  230. package/dist/@tool-jopinb/index.js +9 -0
  231. package/dist/@tool-jopinb/index.js.map +1 -0
  232. package/dist/@ui/components.d.ts +27 -0
  233. package/dist/@ui/components.js +42 -0
  234. package/dist/@ui/components.js.map +1 -0
  235. package/dist/@ui/cssModules.d.ts +11 -0
  236. package/dist/@ui/cssModules.js +17 -0
  237. package/dist/@ui/cssModules.js.map +1 -0
  238. package/dist/@ui/hooks.d.ts +31 -0
  239. package/dist/@ui/hooks.js +71 -0
  240. package/dist/@ui/hooks.js.map +1 -0
  241. package/dist/@ui/index.d.ts +9 -0
  242. package/dist/@ui/index.js +11 -0
  243. package/dist/@ui/index.js.map +1 -0
  244. package/dist/@ui/internal.d.ts +4 -0
  245. package/dist/@ui/internal.js +10 -0
  246. package/dist/@ui/internal.js.map +1 -0
  247. package/dist/@ui/modules.d.ts +41 -0
  248. package/dist/@ui/modules.js +74 -0
  249. package/dist/@ui/modules.js.map +1 -0
  250. package/dist/@ui/objectRegistry.d.ts +12 -0
  251. package/dist/@ui/objectRegistry.js +35 -0
  252. package/dist/@ui/objectRegistry.js.map +1 -0
  253. package/dist/@ui/pageController.d.ts +61 -0
  254. package/dist/@ui/pageController.js +166 -0
  255. package/dist/@ui/pageController.js.map +1 -0
  256. package/dist/@ui/tools.d.ts +24 -0
  257. package/dist/@ui/tools.js +78 -0
  258. package/dist/@ui/tools.js.map +1 -0
  259. package/dist/@uikit/core/UiKitModule.d.ts +6 -0
  260. package/dist/@uikit/core/UiKitModule.js +18 -0
  261. package/dist/@uikit/core/UiKitModule.js.map +1 -0
  262. package/dist/@uikit/core/index.d.ts +1 -0
  263. package/dist/@uikit/core/index.js +4 -0
  264. package/dist/@uikit/core/index.js.map +1 -0
  265. package/dist/@uikit/core/jBundler_ifBrowser.d.ts +1 -0
  266. package/dist/@uikit/core/jBundler_ifBrowser.js +5 -0
  267. package/dist/@uikit/core/jBundler_ifBrowser.js.map +1 -0
  268. package/dist/@uikit/core/jBundler_ifServer.d.ts +6 -0
  269. package/dist/@uikit/core/jBundler_ifServer.js +17 -0
  270. package/dist/@uikit/core/jBundler_ifServer.js.map +1 -0
  271. package/dist/@uikit/forms/components.d.ts +18 -0
  272. package/dist/@uikit/forms/components.js +69 -0
  273. package/dist/@uikit/forms/components.js.map +1 -0
  274. package/dist/@uikit/forms/hooks.d.ts +4 -0
  275. package/dist/@uikit/forms/hooks.js +31 -0
  276. package/dist/@uikit/forms/hooks.js.map +1 -0
  277. package/dist/@uikit/forms/index.d.ts +3 -0
  278. package/dist/@uikit/forms/index.js +4 -0
  279. package/dist/@uikit/forms/index.js.map +1 -0
  280. package/dist/@uikit/forms/interfaces.d.ts +101 -0
  281. package/dist/@uikit/forms/interfaces.js +4 -0
  282. package/dist/@uikit/forms/interfaces.js.map +1 -0
  283. package/dist/@uikit/forms/private.d.ts +40 -0
  284. package/dist/@uikit/forms/private.js +307 -0
  285. package/dist/@uikit/forms/private.js.map +1 -0
  286. package/dist/@uikit/helpers/hooks.d.ts +33 -0
  287. package/dist/@uikit/helpers/hooks.js +107 -0
  288. package/dist/@uikit/helpers/hooks.js.map +1 -0
  289. package/dist/@uikit/helpers/index.d.ts +2 -0
  290. package/dist/@uikit/helpers/index.js +3 -0
  291. package/dist/@uikit/helpers/index.js.map +1 -0
  292. package/dist/@uikit/helpers/tools.d.ts +8 -0
  293. package/dist/@uikit/helpers/tools.js +35 -0
  294. package/dist/@uikit/helpers/tools.js.map +1 -0
  295. package/dist/@uikit/index.d.ts +10 -0
  296. package/dist/@uikit/index.js +13 -0
  297. package/dist/@uikit/index.js.map +1 -0
  298. package/dist/@uikit/menu/core.d.ts +21 -0
  299. package/dist/@uikit/menu/core.js +191 -0
  300. package/dist/@uikit/menu/core.js.map +1 -0
  301. package/dist/@uikit/menu/hooks.d.ts +5 -0
  302. package/dist/@uikit/menu/hooks.js +24 -0
  303. package/dist/@uikit/menu/hooks.js.map +1 -0
  304. package/dist/@uikit/menu/index.d.ts +3 -0
  305. package/dist/@uikit/menu/index.js +4 -0
  306. package/dist/@uikit/menu/index.js.map +1 -0
  307. package/dist/@uikit/menu/interfaces.d.ts +20 -0
  308. package/dist/@uikit/menu/interfaces.js +8 -0
  309. package/dist/@uikit/menu/interfaces.js.map +1 -0
  310. package/dist/@uikit/menu/internal.d.ts +40 -0
  311. package/dist/@uikit/menu/internal.js +191 -0
  312. package/dist/@uikit/menu/internal.js.map +1 -0
  313. package/dist/@uikit/reactRouter/hooks/jBundler_ifBrowser.d.ts +18 -0
  314. package/dist/@uikit/reactRouter/hooks/jBundler_ifBrowser.js +40 -0
  315. package/dist/@uikit/reactRouter/hooks/jBundler_ifBrowser.js.map +1 -0
  316. package/dist/@uikit/reactRouter/hooks/jBundler_ifServer.d.ts +28 -0
  317. package/dist/@uikit/reactRouter/hooks/jBundler_ifServer.js +35 -0
  318. package/dist/@uikit/reactRouter/hooks/jBundler_ifServer.js.map +1 -0
  319. package/dist/@uikit/reactRouter/index.d.ts +1 -0
  320. package/dist/@uikit/reactRouter/index.js +2 -0
  321. package/dist/@uikit/reactRouter/index.js.map +1 -0
  322. package/dist/@uikit/users/hooks.d.ts +10 -0
  323. package/dist/@uikit/users/hooks.js +39 -0
  324. package/dist/@uikit/users/hooks.js.map +1 -0
  325. package/dist/@uikit/users/index.d.ts +1 -0
  326. package/dist/@uikit/users/index.js +2 -0
  327. package/dist/@uikit/users/index.js.map +1 -0
  328. package/dist/@uikit/variants/index.d.ts +7 -0
  329. package/dist/@uikit/variants/index.js +16 -0
  330. package/dist/@uikit/variants/index.js.map +1 -0
  331. package/package.json +115 -0
  332. package/src/@bundler/README.md +1 -0
  333. package/src/@bundler/_logs.ts +4 -0
  334. package/src/@bundler/core.ts +41 -0
  335. package/src/@bundler/esbuild.ts +141 -0
  336. package/src/@bundler/index.ts +3 -0
  337. package/src/@bundler/plugins.ts +163 -0
  338. package/src/@bundler/tailwind.ts +139 -0
  339. package/src/@core/PageComponent.tsx +25 -0
  340. package/src/@core/README.md +1 -0
  341. package/src/@core/_logs.ts +11 -0
  342. package/src/@core/automaticStartStop.ts +132 -0
  343. package/src/@core/browserCacheControl.ts +40 -0
  344. package/src/@core/bundler/bundler.ts +128 -0
  345. package/src/@core/bundler/common.ts +18 -0
  346. package/src/@core/bundler/config.ts +37 -0
  347. package/src/@core/bundler/extraContent.ts +30 -0
  348. package/src/@core/bundler/pagesGenerator.ts +194 -0
  349. package/src/@core/bundler/server.ts +41 -0
  350. package/src/@core/caches/InMemoryCache.ts +483 -0
  351. package/src/@core/caches/SimpleFileCache.ts +205 -0
  352. package/src/@core/caches/cache.ts +77 -0
  353. package/src/@core/caches/webSiteMirrorCache.ts +90 -0
  354. package/src/@core/cheerio.d.ts +332 -0
  355. package/src/@core/gzip.ts +33 -0
  356. package/src/@core/index.ts +23 -0
  357. package/src/@core/internalTools.ts +95 -0
  358. package/src/@core/jQuery.ts +22 -0
  359. package/src/@core/jopiEasy.ts +1285 -0
  360. package/src/@core/jopiRequest.tsx +1279 -0
  361. package/src/@core/jopiServer.ts +188 -0
  362. package/src/@core/jopiWebSite.tsx +1027 -0
  363. package/src/@core/letsEncrypt.ts +243 -0
  364. package/src/@core/linker.ts +42 -0
  365. package/src/@core/loadBalancing.ts +152 -0
  366. package/src/@core/middlewares/CorsMiddleware.ts +23 -0
  367. package/src/@core/middlewares/DdosProtection.ts +120 -0
  368. package/src/@core/middlewares/index.ts +16 -0
  369. package/src/@core/publicTools.ts +14 -0
  370. package/src/@core/routeConfig.ts +104 -0
  371. package/src/@core/searchParamFilter.ts +315 -0
  372. package/src/@core/serverFetch.ts +454 -0
  373. package/src/@core/serverImpl/server_bunjs.tsx +230 -0
  374. package/src/@core/serverImpl/server_nodejs.ts +312 -0
  375. package/src/@core/serverInstanceBuilder.ts +36 -0
  376. package/src/@core/userStores.ts +34 -0
  377. package/src/@crawler/README.md +1 -0
  378. package/src/@crawler/common.ts +275 -0
  379. package/src/@crawler/core.ts +678 -0
  380. package/src/@crawler/directFileCache.ts +69 -0
  381. package/src/@crawler/index.ts +4 -0
  382. package/src/@crawler/urlMapping.ts +67 -0
  383. package/src/@crawler/utils.ts +13 -0
  384. package/src/@generated/index.ts +134 -0
  385. package/src/@linker/arobaseTypes.ts +325 -0
  386. package/src/@linker/changeProof.ts +53 -0
  387. package/src/@linker/config.ts +46 -0
  388. package/src/@linker/engine.ts +865 -0
  389. package/src/@linker/index.ts +3 -0
  390. package/src/@linker/install.ts +39 -0
  391. package/src/@linker/modInstaller.ts +50 -0
  392. package/src/@linker/typeEvents.ts +58 -0
  393. package/src/@linker/typeRoutes.ts +255 -0
  394. package/src/@linker/typeUiComposite.ts +19 -0
  395. package/src/@loader/README.md +4 -0
  396. package/src/@loader/bunHtmlCompiler.mjs +27 -0
  397. package/src/@loader/index.ts +19 -0
  398. package/src/@loader/loader.mjs +7 -0
  399. package/src/@loader/nodeJsLoader.ts +21 -0
  400. package/src/@loader-client/README.md +2 -0
  401. package/src/@loader-client/index.ts +108 -0
  402. package/src/@loader-tools/README.md +6 -0
  403. package/src/@loader-tools/bunJsLoader.ts +33 -0
  404. package/src/@loader-tools/config.ts +142 -0
  405. package/src/@loader-tools/cssModuleCompiler.ts +76 -0
  406. package/src/@loader-tools/esBuildPlugin.ts +142 -0
  407. package/src/@loader-tools/index.ts +11 -0
  408. package/src/@loader-tools/jopin.ts +403 -0
  409. package/src/@loader-tools/nodeJsLoader.ts +119 -0
  410. package/src/@loader-tools/nodeJsResolver.ts +140 -0
  411. package/src/@loader-tools/rules.ts +45 -0
  412. package/src/@loader-tools/sourceChangesWatcher.ts +225 -0
  413. package/src/@loader-tools/tools.ts +139 -0
  414. package/src/@loader-tools/transform.ts +138 -0
  415. package/src/@loader-tools/virtualUrl.ts +57 -0
  416. package/src/@loader-types/README.md +15 -0
  417. package/src/@loader-types/extensions.d.ts +65 -0
  418. package/src/@tool-jopinb/README.md +1 -0
  419. package/src/@tool-jopinb/index.ts +11 -0
  420. package/src/@ui/README.md +3 -0
  421. package/src/@ui/components.tsx +49 -0
  422. package/src/@ui/cssModules.tsx +27 -0
  423. package/src/@ui/hooks.tsx +95 -0
  424. package/src/@ui/index.ts +13 -0
  425. package/src/@ui/internal.ts +12 -0
  426. package/src/@ui/modules.ts +100 -0
  427. package/src/@ui/objectRegistry.ts +50 -0
  428. package/src/@ui/pageController.ts +203 -0
  429. package/src/@ui/tools.ts +116 -0
  430. package/src/@uikit/README.md +1 -0
  431. package/src/@uikit/core/UiKitModule.ts +20 -0
  432. package/src/@uikit/core/index.ts +4 -0
  433. package/src/@uikit/core/jBundler_ifBrowser.ts +4 -0
  434. package/src/@uikit/core/jBundler_ifServer.ts +18 -0
  435. package/src/@uikit/forms/components.tsx +97 -0
  436. package/src/@uikit/forms/hooks.ts +38 -0
  437. package/src/@uikit/forms/index.ts +3 -0
  438. package/src/@uikit/forms/interfaces.ts +159 -0
  439. package/src/@uikit/forms/private.ts +365 -0
  440. package/src/@uikit/helpers/hooks.ts +133 -0
  441. package/src/@uikit/helpers/index.ts +2 -0
  442. package/src/@uikit/helpers/tools.ts +39 -0
  443. package/src/@uikit/index.ts +15 -0
  444. package/src/@uikit/menu/core.ts +227 -0
  445. package/src/@uikit/menu/hooks.ts +28 -0
  446. package/src/@uikit/menu/index.ts +3 -0
  447. package/src/@uikit/menu/interfaces.ts +26 -0
  448. package/src/@uikit/menu/internal.ts +218 -0
  449. package/src/@uikit/package.json +15 -0
  450. package/src/@uikit/reactRouter/hooks/jBundler_ifBrowser.tsx +59 -0
  451. package/src/@uikit/reactRouter/hooks/jBundler_ifServer.tsx +51 -0
  452. package/src/@uikit/reactRouter/index.ts +1 -0
  453. package/src/@uikit/users/hooks.ts +50 -0
  454. package/src/@uikit/users/index.ts +1 -0
  455. package/src/@uikit/variants/index.tsx +20 -0
@@ -0,0 +1,1279 @@
1
+ // noinspection JSUnusedGlobalSymbols
2
+
3
+ import type {CoreServer, ServerSocketAddress} from "./jopiServer.ts";
4
+ import {ServerFetch} from "./serverFetch.ts";
5
+ import React, {type ReactNode} from "react";
6
+ import {PageController_ExposePrivate} from "jopijs/ui";
7
+ import * as ReactServer from "react-dom/server";
8
+ import * as cheerio from "cheerio";
9
+ import type {SearchParamFilterFunction} from "./searchParamFilter.ts";
10
+ import * as jk_schema from "jopi-toolkit/jk_schema";
11
+ import * as jk_what from "jopi-toolkit/jk_what";
12
+ import * as jk_fs from "jopi-toolkit/jk_fs";
13
+ import Page from "./PageComponent.tsx";
14
+
15
+ import {initCheerio} from "./jQuery.ts";
16
+ import {type CacheEntry, type PageCache} from "./caches/cache.ts";
17
+ import {
18
+ type AuthResult,
19
+ type CookieOptions, SBPE_DirectSendThisResponseException,
20
+ type HttpMethod, type JopiRouteHandler, type LoginPassword, SBPE_NotAuthorizedException,
21
+ type RequestBody,
22
+ type ResponseModifier, type ServeFileOptions, type TestCookieValue, type TextModifier, type UserInfos,
23
+ type WebSite,
24
+ WebSiteImpl,
25
+ type WebSiteRouteInfos
26
+ } from "./jopiWebSite.tsx";
27
+
28
+ import {parseCookies} from "./internalTools.ts";
29
+ import * as jk_term from "jopi-toolkit/jk_term";
30
+ import {isNodeJS} from "jopi-toolkit/jk_what";
31
+ import {isSinglePageMode} from "jopijs/loader-client";
32
+ import {createBundleForPage} from "./bundler/bundler.ts";
33
+ import {type BrowserCacheValidationInfos, type ReqReturnFileParams} from "./browserCacheControl.ts";
34
+ import {WebSiteMirrorCache} from "./caches/webSiteMirrorCache.ts";
35
+
36
+ export class JopiRequest {
37
+ public cache: PageCache;
38
+
39
+ public readonly mainCache: PageCache;
40
+ private cookies?: { [name: string]: string };
41
+ private _headers: Headers;
42
+
43
+ constructor(public readonly webSite: WebSite,
44
+ private _urlInfos: URL|undefined,
45
+ public coreRequest: Request,
46
+ public readonly coreServer: CoreServer,
47
+ public readonly routeInfos: WebSiteRouteInfos)
48
+ {
49
+ this.cache = (webSite as WebSiteImpl).mainCache;
50
+ this.mainCache = this.cache;
51
+ this._headers = this.coreRequest.headers;
52
+ }
53
+
54
+ //region Properties
55
+
56
+ private _customData?: any;
57
+
58
+ get urlInfos(): URL {
59
+ if (!this._urlInfos) {
60
+ this._urlInfos = new URL(this.coreRequest.url);
61
+ this._urlInfos.hash = "";
62
+ }
63
+
64
+ return this._urlInfos;
65
+ }
66
+
67
+ get customData(): any {
68
+ if (!this._customData) this._customData = {};
69
+ return this._customData;
70
+ }
71
+
72
+ /**
73
+ * Return the verb used for the request (GET, POST, PUT, DELETE, ...)
74
+ */
75
+ get method(): HttpMethod {
76
+ return this.coreRequest.method as HttpMethod;
77
+ }
78
+
79
+ /**
80
+ * Return the content type of the request.
81
+ */
82
+ get reqContentType(): string | null {
83
+ return this.coreRequest.headers.get("content-type");
84
+ }
85
+
86
+ get url(): string {
87
+ return this.coreRequest.url;
88
+ }
89
+
90
+ get body(): RequestBody {
91
+ return this.coreRequest.body;
92
+ }
93
+
94
+ get headers(): Headers {
95
+ return this._headers;
96
+ }
97
+
98
+ set headers(value: Headers) {
99
+ this._headers = value;
100
+ }
101
+
102
+ /**
103
+ * The part of the url.
104
+ * if : https://mywebsite/product-name/list
105
+ * and route http://mywebsite/:product/list
106
+ * then urlParts contains {product: "product-name"}
107
+ */
108
+ urlParts?: any;
109
+
110
+ /**
111
+ * Returns the url search params.
112
+ * For "https://my-site/?sort=asc&filter=jopi", it returns {sort: "asc", filter: "jopi"}.
113
+ */
114
+ get urlSearchParams(): any {
115
+ const sp = this.urlInfos.searchParams;
116
+ if (!sp.size) return {};
117
+
118
+ const res: any = {};
119
+ sp.forEach((value, key) => res[key] = value);
120
+ return res;
121
+ }
122
+
123
+ /**
124
+ * Returns information on the caller IP.
125
+ */
126
+ get requestIP(): ServerSocketAddress | null {
127
+ return this.coreServer.requestIP(this.coreRequest);
128
+ }
129
+
130
+ get isFromLocalhost() {
131
+ const ip = this.requestIP;
132
+ if (!ip) return false;
133
+
134
+ const address = ip.address;
135
+
136
+ switch (address) {
137
+ case "::1":
138
+ case "127.0.0.1":
139
+ case "::ffff:127.0.0.1":
140
+ return true;
141
+ }
142
+
143
+ return false;
144
+ }
145
+
146
+ //endregion
147
+
148
+ //region Body transforming
149
+
150
+ /**
151
+ * Returns all the data about the request.
152
+ * It's concat all data source.
153
+ *
154
+ * - The url parts.
155
+ * - The search param (query string).
156
+ * - The POST/PUT data if available.
157
+ */
158
+ async req_getData<T = any>(options?: {ignoreUrl?: boolean, dataSchema?: jk_schema.Schema}): Promise<T> {
159
+ let res: any = {};
160
+
161
+ if (!(options && options.ignoreUrl)) {
162
+ const searchParams = this.urlInfos.searchParams;
163
+
164
+ if (searchParams.size) {
165
+ searchParams.forEach((value, key) => res[key] = value);
166
+ }
167
+
168
+ if (this.urlParts) {
169
+ res = {...res, ...this.urlParts};
170
+ }
171
+ }
172
+
173
+ if (this.req_isBodyJson) {
174
+ try {
175
+ const asJson = await this.req_bodyAsJson();
176
+ if (asJson) res = {...res, ...asJson};
177
+ } catch {
178
+ // If JSON is invalid.
179
+ }
180
+ } else if (this.req_isBodyXFormUrlEncoded) {
181
+ try {
182
+ let data = await this.req_bodyAsText();
183
+ new URLSearchParams(data).forEach((value, key) => res[key] = value);
184
+ } catch {
185
+ // If invalid.
186
+ }
187
+ } else if (this.req_isBodyFormData) {
188
+ try {
189
+ const asFormData = await this.req_bodyAsFormData();
190
+ asFormData.forEach((value, key) => res[key] = value);
191
+ } catch {
192
+ // If FormData is invalid.
193
+ }
194
+ }
195
+
196
+ if (options && options.dataSchema) {
197
+ this.tool_validateDataSchema(res, options.dataSchema);
198
+ }
199
+
200
+ return res as T;
201
+ }
202
+
203
+ /**
204
+ * Get the request body and decode it properly.
205
+ */
206
+ async req_getBodyData<T = any>(options?: {dataSchema?: jk_schema.Schema}): Promise<T> {
207
+ let res: any = {};
208
+
209
+ if (this.req_isBodyJson) {
210
+ try {
211
+ const asJson = await this.req_bodyAsJson();
212
+ if (asJson) res = {...res, ...asJson};
213
+ } catch {
214
+ // If JSON is invalid.
215
+ }
216
+ } else if (this.req_isBodyXFormUrlEncoded) {
217
+ try {
218
+ let data = await this.req_bodyAsText();
219
+ new URLSearchParams(data).forEach((value, key) => res[key] = value);
220
+ } catch {
221
+ // If invalid.
222
+ }
223
+ } else if (this.req_isBodyFormData) {
224
+ try {
225
+ const asFormData = await this.req_bodyAsFormData();
226
+ asFormData.forEach((value, key) => res[key] = value);
227
+ } catch {
228
+ // If FormData is invalid.
229
+ }
230
+ }
231
+
232
+ if (options && options.dataSchema) {
233
+ this.tool_validateDataSchema(res, options.dataSchema);
234
+ }
235
+
236
+ return res as T;
237
+ }
238
+
239
+ /**
240
+ * Returns all the data about the request, organized by category.
241
+ */
242
+ async req_getDataInfos<T = any>(): Promise<T> {
243
+ let res: any = {};
244
+
245
+ const searchParams = this.urlInfos.searchParams;
246
+
247
+ if (searchParams.size) {
248
+ const t: any = res.searchParams = {};
249
+ searchParams.forEach((value, key) => t[key] = value);
250
+ }
251
+
252
+ if (this.urlParts) {
253
+ res.urlParts = {...this.urlParts};
254
+ }
255
+
256
+ if (this.req_isBodyJson) {
257
+ try {
258
+ res.body = await this.req_bodyAsJson();
259
+ } catch {
260
+ // If JSON is invalid.
261
+ }
262
+ } else if (this.req_isBodyFormData) {
263
+ try {
264
+ const t: any = res.formData = {};
265
+ const asFormData = await this.req_bodyAsFormData();
266
+ asFormData.forEach((value, key) => t[key] = value);
267
+ } catch {
268
+ // If FormData is invalid.
269
+ }
270
+ } else if (this.req_isBodyXFormUrlEncoded) {
271
+ try {
272
+ let data = await this.req_bodyAsText();
273
+ const t: any = res.formUrlEncoded = {};
274
+ new URLSearchParams(data).forEach((value, key) => t[key] = value);
275
+ } catch {
276
+ // If invalid.
277
+ }
278
+ }
279
+
280
+ return res;
281
+ }
282
+
283
+ /**
284
+ * https://developer.mozilla.org/en-US/docs/Web/API/Request/bodyUsed
285
+ */
286
+ get req_isBodyUsed(): boolean {
287
+ return this.coreRequest.bodyUsed;
288
+ }
289
+
290
+ get req_isBodyJson(): boolean {
291
+ const ct = this.reqContentType;
292
+ if (ct === null) return false;
293
+ return ct.startsWith("application/json");
294
+ }
295
+
296
+ get req_isBodyFormData(): boolean {
297
+ const ct = this.reqContentType;
298
+ if (ct === null) return false;
299
+ return ct.startsWith("multipart/form-data");
300
+ }
301
+
302
+ get req_isBodyXFormUrlEncoded(): boolean {
303
+ const ct = this.reqContentType;
304
+ if (ct === null) return false;
305
+ return ct.startsWith("application/x-www-form-urlencoded");
306
+ }
307
+
308
+ req_bodyAsText(): Promise<string> {
309
+ return this.coreRequest.text();
310
+ }
311
+
312
+ /**
313
+ * Validate the data Schema.
314
+ * If invalid, throw a special exception allowing
315
+ * to directly send a response to the caller.
316
+ */
317
+ tool_validateDataSchema(data: any, schema: jk_schema.Schema) {
318
+ let error = jk_schema.validateSchema(data, schema);
319
+
320
+ if (error) {
321
+ throw new SBPE_DirectSendThisResponseException(() => {
322
+ return this.res_returnError400_BadRequest("Invalid data")
323
+ });
324
+ }
325
+ }
326
+
327
+ async req_bodyAsJson<T = any>(dataSchema?: jk_schema.Schema): Promise<T> {
328
+ if (dataSchema) {
329
+ const data = await this.req_bodyAsJson();
330
+ this.tool_validateDataSchema(data, dataSchema);
331
+ return data;
332
+ }
333
+
334
+ return await this.coreRequest.json() as Promise<T>;
335
+ }
336
+
337
+ /**
338
+ * https://developer.mozilla.org/en-US/docs/Web/API/Request/arrayBuffer
339
+ */
340
+ req_bodyAsArrayBuffer(): Promise<ArrayBuffer> {
341
+ return this.coreRequest.arrayBuffer();
342
+ }
343
+
344
+ /**
345
+ * https://developer.mozilla.org/en-US/docs/Web/API/Request/blob
346
+ */
347
+ req_bodyAsBlob(): Promise<Blob> {
348
+ return this.coreRequest.blob();
349
+ }
350
+
351
+ /**
352
+ * https://developer.mozilla.org/en-US/docs/Web/API/Request/bytes
353
+ */
354
+ req_bodyAsBytes(): Promise<Uint8Array> {
355
+ return this.coreRequest.bytes();
356
+ }
357
+
358
+ /**
359
+ * https://developer.mozilla.org/en-US/docs/Web/API/Request/formData
360
+ */
361
+ req_bodyAsFormData(): Promise<FormData> {
362
+ return this.coreRequest.formData();
363
+ }
364
+
365
+ //endregion
366
+
367
+ //region Request timeout
368
+
369
+ /**
370
+ * When DDOS protection is enabled, the request has a timeout of 60 seconds.
371
+ * Here it'd allow you to extend this time for a request you knew was slow.
372
+ */
373
+ req_extendTimeout_sec(sec: number) {
374
+ this.coreServer.timeout(this.coreRequest, sec);
375
+ }
376
+
377
+ //endregion
378
+
379
+ //region Response helpers
380
+
381
+ res_redirectResponse(permanent: boolean = false, url?: string | URL): Response {
382
+ if (!url) url = this.urlInfos;
383
+ return new Response(null, {status: permanent ? 301 : 302, headers: {"location": url.toString()}});
384
+ }
385
+
386
+ res_textResponse(text: string, statusCode: number = 200) {
387
+ return new Response(text, {status: statusCode, headers: {"content-type": "text/plain;charset=utf-8"}});
388
+ }
389
+
390
+ res_returnResultMessage(isOk: true, message?: string): Response {
391
+ return this.res_jsonResponse({isOk, message});
392
+ }
393
+
394
+ res_htmlResponse(html: string, statusCode: number = 200): Response {
395
+ return new Response(html, {status: statusCode, headers: {"content-type": "text/html;charset=utf-8"}});
396
+ }
397
+
398
+ res_jsonResponse(json: any, statusCode: number = 200): Response {
399
+ return new Response(JSON.stringify(json), {
400
+ status: statusCode,
401
+ headers: {"content-type": "application/json;charset=utf-8"}
402
+ });
403
+ }
404
+
405
+ res_jsonStringResponse(json: string, statusCode: number = 200): Response {
406
+ return new Response(json, {status: statusCode, headers: {"content-type": "application/json;charset=utf-8"}});
407
+ }
408
+
409
+ res_returnError404_NotFound(): Promise<Response> {
410
+ return this.webSite.return404(this);
411
+ }
412
+
413
+ res_returnError500_ServerError(error?: any | string): Promise<Response> {
414
+ return this.webSite.return500(this, error);
415
+ }
416
+
417
+ res_returnError401_Unauthorized(error?: Error | string): Promise<Response> {
418
+ return this.webSite.return401(this, error);
419
+ }
420
+
421
+ res_returnError400_BadRequest(error?: Error | string): Promise<Response> {
422
+ return Promise.resolve(new Response(error ? error.toString() : "Bad request", {status: 400}));
423
+ }
424
+
425
+ //endregion
426
+
427
+ //region Fetch / Proxy
428
+
429
+ proxy_directProxyToServer(): Promise<Response> {
430
+ return (this.webSite as WebSiteImpl).loadBalancer.directProxy(this);
431
+ }
432
+
433
+ proxy_proxyRequestTo(server: ServerFetch<any>): Promise<Response> {
434
+ return server.directProxy(this);
435
+ }
436
+
437
+ proxy_directProxyWith(server: ServerFetch<any>): Promise<Response> {
438
+ return server.directProxy(this);
439
+ }
440
+
441
+ proxy_fetchServer(headers?: Headers, method: string = "GET", url?: URL, body?: RequestBody): Promise<Response> {
442
+ if (!url) url = this.urlInfos;
443
+ return (this.webSite as WebSiteImpl).loadBalancer.fetch(method, url, body, headers);
444
+ }
445
+
446
+ //endregion
447
+
448
+ //region Cache
449
+
450
+ private _isAddedToCache = false;
451
+
452
+ /**
453
+ * Get from the cache the entry corresponding to the current url.
454
+ */
455
+ async cache_getFromCache(): Promise<Response | undefined> {
456
+ return await this.cache.getFromCache(this, this.urlInfos);
457
+ }
458
+
459
+ async cache_hasInCache(): Promise<boolean> {
460
+ return await this.cache.hasInCache(this.urlInfos);
461
+ }
462
+
463
+ cache_removeFromCache(url?: URL): Promise<void> {
464
+ // Avoid double.
465
+ //
466
+ if (!url) {
467
+ url = this.urlInfos;
468
+ url.hostname = url.hostname.toLowerCase();
469
+ url.pathname = url.pathname.toLowerCase();
470
+ }
471
+
472
+ return this.cache.removeFromCache(url || this.urlInfos);
473
+ }
474
+
475
+ cache_addToCache(response: Response) {
476
+ // Avoid adding two times in the same request.
477
+ // This is required with automatic add functionnality.
478
+ //
479
+ if (this._isAddedToCache) return;
480
+ this._isAddedToCache = false;
481
+
482
+ return this.cache.addToCache(this, this.urlInfos, response, (this.webSite as WebSiteImpl).getHeadersToCache());
483
+ }
484
+
485
+ /**
486
+ * Allow using a sub-cache.
487
+ * For example, a cache dedicated per user.
488
+ */
489
+ cache_useCache(cache: PageCache) {
490
+ this.cache = cache;
491
+ }
492
+
493
+ cache_getSubCache(name: string): PageCache {
494
+ return this.cache.createSubCache(name);
495
+ }
496
+
497
+ cache_getCacheEntryIterator(): Iterable<CacheEntry> {
498
+ return this.cache.getCacheEntryIterator();
499
+ }
500
+
501
+ //endregion
502
+
503
+ //region Test type / React on type
504
+
505
+ resValue_getContentTypeOf(response: Response): string | null {
506
+ return response.headers.get("content-type");
507
+ }
508
+
509
+ resValue_getContentTypeCategory(response: Response): ContentTypeCategory {
510
+ const contentType = response.headers.get("content-type");
511
+ if (!contentType) return ContentTypeCategory.OTHER;
512
+
513
+ if (contentType.startsWith("text/")) {
514
+ if (contentType.startsWith("html", 5)) {
515
+ return ContentTypeCategory.TEXT_HTML;
516
+ } else if (contentType.startsWith("css")) {
517
+ return ContentTypeCategory.TEXT_CSS;
518
+ } else if (contentType.startsWith("javascript", 5)) {
519
+ return ContentTypeCategory.TEXT_JAVASCRIPT;
520
+ } else if (contentType.startsWith("json")) {
521
+ return ContentTypeCategory.TEXT_JSON;
522
+ }
523
+ } else if (contentType.startsWith("image")) {
524
+ return ContentTypeCategory.IMAGE;
525
+ } else if (contentType.startsWith("application")) {
526
+ if (contentType.startsWith("x-www-form-urlencoded", 12)) {
527
+ return ContentTypeCategory.FORM_URL_ENCODED;
528
+ } else if (contentType.startsWith("json", 12)) {
529
+ return ContentTypeCategory.TEXT_JSON;
530
+ } else if (contentType.startsWith("javascript", 12)) {
531
+ return ContentTypeCategory.TEXT_JAVASCRIPT;
532
+ }
533
+ } else if (contentType.startsWith("multipart/form-data")) {
534
+ return ContentTypeCategory.FORM_MULTIPART;
535
+ }
536
+
537
+ return ContentTypeCategory.OTHER;
538
+ }
539
+
540
+ resValue_isHtml(response: Response): boolean {
541
+ const contentType = response.headers.get("content-type");
542
+ if (contentType === null) return false;
543
+ return contentType.startsWith("text/html");
544
+ }
545
+
546
+ resValue_isCss(response: Response): boolean {
547
+ const contentType = response.headers.get("content-type");
548
+ if (contentType === null) return false;
549
+ return contentType.startsWith("text/css");
550
+ }
551
+
552
+ resValue_isJavascript(response: Response): boolean {
553
+ const contentType = response.headers.get("content-type");
554
+ if (contentType === null) return false;
555
+ return contentType.startsWith("application/javascript") || contentType.startsWith("text/javascript");
556
+ }
557
+
558
+ resValue_isJson(response: Response): boolean {
559
+ const contentType = response.headers.get("content-type");
560
+ if (contentType === null) return false;
561
+ return contentType.startsWith("application/json");
562
+ }
563
+
564
+ resValue_isXFormUrlEncoded(response: Response): boolean {
565
+ const contentType = response.headers.get("content-type");
566
+ if (contentType === null) return false;
567
+ return contentType.startsWith("x-www-form-urlencoded");
568
+ }
569
+
570
+ async resValue_hookIfHtml(res: Response, ...hooks: TextModifier[]): Promise<Response> {
571
+ if (this.resValue_isHtml(res)) {
572
+ if (isNodeJS) {
573
+ let headers = new Headers(res.headers);
574
+ headers.delete("content-length");
575
+ headers.delete("content-encoding");
576
+
577
+ let newHTML = await this.resValue_applyTextModifiers(res, hooks);
578
+ return new Response(newHTML, {status: res.status, headers});
579
+ }
580
+ else {
581
+ res.headers.delete("content-length");
582
+ res.headers.delete("content-encoding");
583
+
584
+ return new Response(
585
+ await this.resValue_applyTextModifiers(res, hooks),
586
+ {status: res.status, headers: res.headers}
587
+ );
588
+ }
589
+ }
590
+
591
+ return Promise.resolve(res);
592
+ }
593
+
594
+ async resValue_hookIfCss(res: Response, ...hooks: TextModifier[]): Promise<Response> {
595
+ if (this.resValue_isCss(res)) {
596
+ return new Response(
597
+ await this.resValue_applyTextModifiers(res, hooks),
598
+ {status: res.status, headers: res.headers}
599
+ );
600
+ }
601
+
602
+ return Promise.resolve(res);
603
+ }
604
+
605
+ async resValue_hookIfJavascript(res: Response, ...hooks: TextModifier[]): Promise<Response> {
606
+ if (this.resValue_isJavascript(res)) {
607
+ return new Response(
608
+ await this.resValue_applyTextModifiers(res, hooks),
609
+ {status: res.status, headers: res.headers}
610
+ );
611
+ }
612
+
613
+ return Promise.resolve(res);
614
+ }
615
+
616
+ async resValue_applyTextModifiers(res: Response, hooks: TextModifier[]): Promise<string> {
617
+ let text = await res.text() as string;
618
+
619
+ for (const hook of hooks) {
620
+ const hRes = hook(text, this);
621
+ text = hRes instanceof Promise ? await hRes : hRes;
622
+ }
623
+
624
+ return text;
625
+ }
626
+
627
+ async resValue_executeModifiers(res: Response, hooks: ResponseModifier[]): Promise<Response> {
628
+ for (const hook of hooks) {
629
+ const hRes = hook(res, this);
630
+ res = hRes instanceof Promise ? await hRes : hRes;
631
+ }
632
+
633
+ return res;
634
+ }
635
+
636
+ async resValue_replaceWebSiteOrigin(res: Response, toReplace: string, redirectionCode?: number): Promise<Response> {
637
+ let location = res.headers.get("location");
638
+
639
+ if (location) {
640
+ const thisOrigin = this.webSite.getWelcomeUrl();
641
+ location = location.replace(toReplace, thisOrigin);
642
+ if (location.startsWith("/")) location = thisOrigin + location;
643
+ res = Response.redirect(location, redirectionCode ? redirectionCode : res.status);
644
+ } else {
645
+ // It's a helper function that allows decoding
646
+ // and re-encoding a response if his content is of type HTML.
647
+ //
648
+ res = await this.resValue_hookIfHtml(res, (html) => {
649
+ const thisOrigin = this.webSite.getWelcomeUrl();
650
+ return html.replaceAll(toReplace, thisOrigin);
651
+ });
652
+ }
653
+
654
+ return res;
655
+ }
656
+
657
+ //endregion
658
+
659
+ //region Tools
660
+
661
+ async tool_duplicateReadableStream(stream: ReadableStream | null): Promise<(ReadableStream<any> | null)[]> {
662
+ if (!stream) return [null, null];
663
+ return stream.tee();
664
+ }
665
+
666
+ async tool_duplicateRawRequest(raw: Request): Promise<[Request, Request]> {
667
+ const [str1, str2] = await this.tool_duplicateReadableStream(raw.body);
668
+
669
+ const res1 = new Request(raw.url, {
670
+ body: str1,
671
+ headers: raw.headers,
672
+ method: raw.method
673
+ });
674
+
675
+ const res2 = new Request(raw.url, {
676
+ body: str2,
677
+ headers: raw.headers,
678
+ method: raw.method
679
+ });
680
+
681
+ return [res1, res2];
682
+ }
683
+
684
+ async tool_duplicateResponse(raw: Response): Promise<[Response, Response]> {
685
+ const [str1, str2] = await this.tool_duplicateReadableStream(raw.body);
686
+
687
+ const res1 = new Response(str1, {
688
+ status: raw.status,
689
+ headers: raw.headers
690
+ });
691
+
692
+ const res2 = new Response(str2, {
693
+ status: raw.status,
694
+ headers: raw.headers
695
+ });
696
+
697
+ return [res1, res2];
698
+ }
699
+
700
+ async tool_spyRequest(handleRequest: (req: JopiRequest) => Promise<Response>): Promise<Response> {
701
+ return this.tool_spyRequestData(handleRequest, (data) => {
702
+ this.tool_printSpyRequestData(data);
703
+ });
704
+ }
705
+
706
+ async tool_printSpyRequestData(data: JopiRequestSpyData) {
707
+ const headerColor = jk_term.buildWriter(jk_term.C_RED);
708
+ const titleColor = jk_term.buildWriter(jk_term.C_ORANGE);
709
+
710
+ let resAsText = "";
711
+ //
712
+ try {
713
+ if (data.res) {
714
+ let res = data.res();
715
+ if (!res) resAsText = "[NO SET]";
716
+ else resAsText = await res.text()
717
+ } else {
718
+ resAsText = "[NO SET]";
719
+ }
720
+ } catch {
721
+ }
722
+
723
+ console.log();
724
+ console.log(headerColor(this.method, this.url));
725
+ console.log(titleColor("|- referer: "), data.reqReferer);
726
+ console.log(titleColor("|- reqContentType:"), data.reqContentType);
727
+ console.log(titleColor("|- reqData:"), data.reqData);
728
+ console.log(titleColor("|- reqCookie:"), data.reqCookies);
729
+ console.log(titleColor("|- resContentType:"), data.resContentType);
730
+ console.log(titleColor("|- resCookieSet:"), data.resCookieSet);
731
+ console.log(titleColor("|- resHeaders:"), data.resHeaders);
732
+ console.log(titleColor("|- resData:"), resAsText);
733
+ }
734
+
735
+ async tool_spyRequestData(handleRequest: JopiRouteHandler, onSpy: JopiRequestSpy): Promise<Response> {
736
+ const [bunNewReq, spyReq] = await this.tool_duplicateRawRequest(this.coreRequest);
737
+
738
+ // Required because the body is already consumed.
739
+ this.coreRequest = bunNewReq;
740
+
741
+ let res = await handleRequest(this);
742
+ const [bunNewRes, spyRes] = await this.tool_duplicateResponse(res);
743
+
744
+ // Required because the body is already consumed.
745
+ this.coreRequest = spyReq;
746
+
747
+ onSpy({
748
+ method: this.method,
749
+ res: () => spyRes,
750
+
751
+ reqUrl: this.url,
752
+ reqReferer: this.headers.get("referer"),
753
+ reqContentType: this.reqContentType,
754
+ reqData: await this.req_getDataInfos(),
755
+ resContentType: res.headers.get("content-type"),
756
+ resContentTypeCat: this.resValue_getContentTypeCategory(res),
757
+
758
+ reqCookies: this.headers.get("cookie"),
759
+ resCookieSet: spyRes.headers.getSetCookie(),
760
+
761
+ resStatus: spyRes.status,
762
+ resLocation: spyRes.headers.get("location"),
763
+ resHeaders: spyRes.headers
764
+ }, this);
765
+
766
+ return bunNewRes;
767
+ }
768
+
769
+ tool_filterSearchParams(filter?: SearchParamFilterFunction) {
770
+ if (filter) {
771
+ filter(this.urlInfos);
772
+ } else {
773
+ if (this.routeInfos.searchParamFilter) {
774
+ this.routeInfos.searchParamFilter(this.urlInfos);
775
+ }
776
+ }
777
+ }
778
+
779
+ //endregion
780
+
781
+ //region Post process
782
+
783
+ private postProcess: ((res: Response) => Response)[] | undefined;
784
+
785
+ _applyPostProcess(res: Response): Response {
786
+ if (!this.postProcess) return res;
787
+ this.postProcess.forEach(hook => res = hook(res));
788
+ return res;
789
+ }
790
+
791
+ //endregion
792
+
793
+ //region Cookies
794
+
795
+ cookie_reqHasCookie(name: string, value?: string): boolean {
796
+ if (!this.cookies) this.cookies = parseCookies(this.coreRequest.headers);
797
+ if (value) return this.cookies[name] === value;
798
+ return this.cookies[name] !== undefined;
799
+ }
800
+
801
+ cookie_getReqCookie(name: string): string | undefined {
802
+ if (!this.cookies) this.cookies = parseCookies(this.coreRequest.headers);
803
+ return this.cookies[name];
804
+ }
805
+
806
+ async cookie_hookIfResHasCookie(res: Response, name: string, testCookieValue: null | undefined | TestCookieValue, ...hooks: TextModifier[]): Promise<Response> {
807
+ const cookieValue = this.cookie_getReqCookie(name);
808
+
809
+ if (cookieValue) {
810
+ if (testCookieValue && !testCookieValue(cookieValue)) {
811
+ return Promise.resolve(res);
812
+ }
813
+
814
+ return this.res_htmlResponse(await this.resValue_applyTextModifiers(res, hooks));
815
+ }
816
+
817
+ return Promise.resolve(res);
818
+ }
819
+
820
+ cookie_addCookieToRes(cookieName: string, cookieValue: string, options?: CookieOptions) {
821
+ let cookie = `${cookieName}=${cookieValue};`;
822
+
823
+ if (options) {
824
+ if (options.maxAge) {
825
+ cookie += ` Max-Age=${options.maxAge};`;
826
+ }
827
+ }
828
+
829
+ if (!this.postProcess) this.postProcess = [];
830
+
831
+ this.postProcess.push((res: Response) => {
832
+ let current = res.headers.get("set-cookie");
833
+ if (current) cookie = current + cookie;
834
+
835
+ // With node, res.headers is immutable.
836
+ // And a Response object is also immutable.
837
+ // It's why we need to create a new response.
838
+ //
839
+ if (jk_what.isNodeJS) {
840
+ const headers = new Headers(res.headers);
841
+ headers.append("set-cookie", cookie);
842
+
843
+ res = new Response(res.body, {
844
+ headers: headers,
845
+ status: res.status
846
+ });
847
+ } else {
848
+ res.headers.append("set-cookie", cookie);
849
+ }
850
+
851
+ return res;
852
+ });
853
+ }
854
+
855
+ //endregion
856
+
857
+ //region ReactJS
858
+
859
+ /**
860
+ * Allow rendering a document fully formed from a React component.
861
+ */
862
+ react_toResponse(E: ReactNode) {
863
+ return this.res_htmlResponse(ReactServer.renderToStaticMarkup(E));
864
+ }
865
+
866
+ react_toString(element: ReactNode): string {
867
+ return ReactServer.renderToStaticMarkup(element);
868
+ }
869
+
870
+ /**
871
+ * The new render function.
872
+ * Used while refactoring the renderer.
873
+ * Used while refactoring the renderer.
874
+ */
875
+ async react_fromPage(pageKey: string, C: React.FC<any>): Promise<Response> {
876
+ try {
877
+ let bundlePath = "/_bundle/";
878
+
879
+ // When dev-mode (JOPI_DEV or JOPI_DEV_UI) then we compile the page one by one.
880
+ //
881
+ if (gIsSinglePageMode) {
882
+ await createBundleForPage(pageKey, this.routeInfos.route);
883
+ bundlePath += pageKey + "/";
884
+ }
885
+
886
+ // What we will include in our HTML.
887
+ const options = {
888
+ head: [<link key="jopi.mainBundle" rel="stylesheet" type="text/css" href={bundlePath + pageKey + ".css"} />],
889
+ bodyEnd: [<script key="jopi.mainSript" type="module" src={bundlePath + pageKey + ".js"}></script>]
890
+ };
891
+
892
+ // Allow faking the environment of the page.
893
+ const controller = new PageController_ExposePrivate<unknown>(
894
+ false, (this.webSite as WebSiteImpl).mustRemoveTrailingSlashs, options);
895
+
896
+ controller.setServerRequest(this);
897
+ (this.webSite as WebSiteImpl).executeBrowserInstall(controller);
898
+
899
+ const params = this.urlParts;
900
+ const searchParams = this.urlInfos.searchParams;
901
+
902
+ const html = ReactServer.renderToStaticMarkup(<Page controller={controller} ><C params={params} searchParams={searchParams}/></Page>);
903
+ return new Response(html, {status: 200, headers: {"content-type": "text/html;charset=utf-8"}});
904
+ }
905
+ catch (e: any) {
906
+ console.error(e);
907
+ return await this.res_returnError500_ServerError(e);
908
+ }
909
+ }
910
+
911
+ //endregion
912
+
913
+ //region JQuery
914
+
915
+ jquery_htmlToJquery(html: string) {
916
+ const res = cheerio.load(html);
917
+ initCheerio(res);
918
+ return res;
919
+ }
920
+
921
+ //endregion
922
+
923
+ //region JWT Tokens
924
+
925
+ /**
926
+ * Create a JWT token with the data.
927
+ */
928
+ user_createJwtToken(data: UserInfos): string | undefined {
929
+ return this.userJwtToken = this.webSite.createJwtToken(data);
930
+ }
931
+
932
+ /**
933
+ * Extract the JWT token from the Authorization header.
934
+ */
935
+ user_getJwtToken(): string | undefined {
936
+ if (this.userJwtToken) {
937
+ return this.userJwtToken;
938
+ }
939
+
940
+ if (this.hasNoUserInfos) {
941
+ return undefined;
942
+ }
943
+
944
+ let authHeader = this.headers.get("authorization");
945
+
946
+ if (authHeader) {
947
+ if (authHeader.startsWith("Bearer ")) {
948
+ return this.userJwtToken = authHeader.slice(7);
949
+ }
950
+ }
951
+
952
+ let authCookie = this.cookie_getReqCookie("authorization");
953
+
954
+ if (authCookie) {
955
+ if (authCookie.startsWith("jwt ")) {
956
+ return this.userJwtToken = authCookie.slice(4);
957
+ }
958
+ }
959
+
960
+ return undefined;
961
+ }
962
+
963
+ /**
964
+ * Try to sign in the user with information you provide.
965
+ * Return true if he is signed in, false otherwise.
966
+ *
967
+ * If signed in, then it automatically adds the Authorization header.
968
+ *
969
+ * @param loginInfo
970
+ * Information with things like login/password-hash/...
971
+ * Must match with you have used with webSite.setUserLoginManager.
972
+ */
973
+ async user_tryAuthWithJWT<T = LoginPassword>(loginInfo: T): Promise<AuthResult> {
974
+ const authResult = await this.webSite.tryAuthUser(loginInfo);
975
+
976
+ if (authResult.isOk) {
977
+ if (!authResult.authToken) {
978
+ authResult.authToken = this.user_createJwtToken(authResult.userInfos!);
979
+ }
980
+
981
+ // The token will be added to cookie "authorization" in the post-process step.
982
+ this.userJwtToken = authResult.authToken;
983
+ this.userInfos = authResult.userInfos!;
984
+
985
+ (this.webSite as WebSiteImpl).storeJwtToken(this);
986
+
987
+ return authResult;
988
+ }
989
+
990
+ this.userInfos = undefined;
991
+ this.userJwtToken = undefined;
992
+
993
+ return authResult;
994
+ }
995
+
996
+ /**
997
+ * Verify and decode the JWT token.
998
+ * Once done, the data is saved and can be read through req.userTokenData.
999
+ */
1000
+ private user_decodeJwtToken(): UserInfos | undefined {
1001
+ if (this.isFakingNoUsers) return undefined;
1002
+
1003
+ const token = this.user_getJwtToken();
1004
+ if (!token) return undefined;
1005
+ return this.webSite.decodeJwtToken(token);
1006
+ }
1007
+
1008
+ /**
1009
+ * Allow faking a state where there is no user connected.
1010
+ * Is mainly used by the automatic cache to generate
1011
+ * a generic anonymous page.
1012
+ */
1013
+ public user_fakeNoUsers() {
1014
+ let fakeNoUser: FakeNoUserListener|undefined;
1015
+
1016
+ if (this._onFakeNoUser) {
1017
+ fakeNoUser = this._onFakeNoUser;
1018
+ } else if ((this.webSite as WebSiteImpl).fakeNoUser) {
1019
+ fakeNoUser = (this.webSite as WebSiteImpl).fakeNoUser;
1020
+ }
1021
+
1022
+ if (fakeNoUser) {
1023
+ let r = fakeNoUser(this);
1024
+ if (r === false) return;
1025
+ }
1026
+
1027
+ this.isFakingNoUsers = true;
1028
+ }
1029
+
1030
+ /**
1031
+ * Allows setting a listener which is call when `fakeNoUsers`is called.
1032
+ * If this listener explicitly returns false, then the call to fakeNoUsers is ignored.
1033
+ */
1034
+ public user_onFakeNoUser(handler: (req: JopiRequest) => void|boolean) {
1035
+ this._onFakeNoUser = handler;
1036
+ }
1037
+
1038
+ public user_getUserInfos(): UserInfos | undefined {
1039
+ if (this.isFakingNoUsers) return undefined;
1040
+
1041
+ if (this.userInfos) return this.userInfos;
1042
+ if (this.hasNoUserInfos) return undefined;
1043
+
1044
+ const userInfos = this.user_decodeJwtToken();
1045
+
1046
+ if (userInfos) {
1047
+ this.userInfos = userInfos;
1048
+ return userInfos;
1049
+ }
1050
+
1051
+ this.hasNoUserInfos = true;
1052
+ return undefined;
1053
+ }
1054
+
1055
+ public use_requireUserInfos(): UserInfos {
1056
+ let userInfos = this.user_getUserInfos();
1057
+ if (!userInfos) throw new SBPE_NotAuthorizedException();
1058
+ return userInfos;
1059
+ }
1060
+
1061
+ private isFakingNoUsers: boolean = false;
1062
+ private _onFakeNoUser?: FakeNoUserListener;
1063
+
1064
+ private hasNoUserInfos: boolean = false;
1065
+ private userInfos?: UserInfos;
1066
+ private userJwtToken?: string;
1067
+
1068
+ //endregion
1069
+
1070
+ //region User roles
1071
+
1072
+ /**
1073
+ * Returns the roles of the user.
1074
+ */
1075
+ public role_getUserRoles(): string[] {
1076
+ const userInfos = this.user_getUserInfos();
1077
+ if (!userInfos || !userInfos.roles) return [];
1078
+ return userInfos.roles;
1079
+ }
1080
+
1081
+ /**
1082
+ * Check if the user has all these roles.
1083
+ * Return true if ok, false otherwise.
1084
+ */
1085
+ public role_userHasRoles(requiredRoles: string[]): boolean {
1086
+ const userInfos = this.user_getUserInfos();
1087
+ if (!userInfos) return false;
1088
+
1089
+ const userRoles = userInfos.roles;
1090
+ if (!userRoles) return false;
1091
+
1092
+ for (let role of requiredRoles) {
1093
+ if (!userRoles.includes(role)) return false;
1094
+ }
1095
+
1096
+ return true;
1097
+ }
1098
+
1099
+ public role_assertUserHasRoles(requiredRoles: string[]) {
1100
+ if (!this.role_userHasRoles(requiredRoles)) {
1101
+ throw new SBPE_NotAuthorizedException();
1102
+ }
1103
+ }
1104
+
1105
+ //endregion
1106
+
1107
+ //region File Serving
1108
+
1109
+ async file_returnFile(filePath: string, params?: ReqReturnFileParams): Promise<Response> {
1110
+ const res = await this.file_tryReturnFile(filePath, params);
1111
+ if (res) return res;
1112
+
1113
+ return this.res_returnError404_NotFound();
1114
+ }
1115
+
1116
+ async file_tryReturnFile(filePath: string, params?: ReqReturnFileParams): Promise<Response|undefined> {
1117
+ let cacheValidationInfos = await this.file_validateCacheHeaders(filePath);
1118
+
1119
+ // Mean that the file doesn't exist.
1120
+ if (cacheValidationInfos===undefined) return undefined;
1121
+
1122
+ // Mean that the browser cache is valid. Returns code 304.
1123
+ if (cacheValidationInfos instanceof Response) return cacheValidationInfos;
1124
+
1125
+ // Will return the file and add the browser cache headers.
1126
+ return (this.webSite as WebSiteImpl).tryReturnFile({
1127
+ req: this,
1128
+ filePath,
1129
+ contentEncoding: params?.contentEncoding,
1130
+ validationInfos: cacheValidationInfos
1131
+ });
1132
+ }
1133
+
1134
+ /**
1135
+ * Allow serving a file as a response.
1136
+ * Automatically get the file from the url and a root dir.
1137
+ */
1138
+ async file_serveFromDir(filesRootPath: string, options?: ServeFileOptions): Promise<Response> {
1139
+ options = options || gEmptyObject;
1140
+
1141
+ if (options.replaceIndexHtml !== false) {
1142
+ if (this.urlInfos.pathname.endsWith("/index.html")) {
1143
+ this.urlInfos.pathname = this.urlInfos.pathname.slice(0, -10);
1144
+ return this.res_redirectResponse(false);
1145
+ }
1146
+
1147
+ if (this.urlInfos.pathname.endsWith("/")) {
1148
+ this.urlInfos.pathname += "index.html";
1149
+ }
1150
+ }
1151
+
1152
+ const sfc = new WebSiteMirrorCache(filesRootPath);
1153
+ const fromCache = await sfc.getFromCache(this, this.urlInfos);
1154
+ if (fromCache) return fromCache;
1155
+
1156
+ if (options.onNotFound) {
1157
+ return options.onNotFound(this);
1158
+ }
1159
+
1160
+ return this.res_returnError404_NotFound();
1161
+ }
1162
+
1163
+ file_calcFileEtag(filePath: string): Promise<string|undefined> {
1164
+ return jk_fs.calcFileHash(filePath);
1165
+ }
1166
+
1167
+ file_validateCacheHeadersWith(headers: any): Response|undefined {
1168
+ let reqEtag = this.headers.get("if-none-match")
1169
+ let myEtag = headers["etag"];
1170
+
1171
+ if (reqEtag && (reqEtag===myEtag)) {
1172
+ return new Response(null, {
1173
+ status: 304,
1174
+ headers: {"etag": myEtag}
1175
+ });
1176
+ }
1177
+
1178
+ let reqLastModifiedSince = this.headers.get("if-modified-since");
1179
+ let myLastModifiedSince = headers["if-modified-since"];
1180
+
1181
+ if (myLastModifiedSince && reqLastModifiedSince) {
1182
+ const dMyLastModifiedSince = new Date(myLastModifiedSince).getTime();
1183
+ const dReqLastModifiedSince = new Date(reqLastModifiedSince).getTime();
1184
+
1185
+ if (dReqLastModifiedSince < dMyLastModifiedSince) {
1186
+ return new Response(null, {
1187
+ status: 304,
1188
+ headers: {"last-modified": myLastModifiedSince}
1189
+ });
1190
+ }
1191
+ }
1192
+ }
1193
+
1194
+ async file_validateCacheHeaders(filePath: string): Promise<BrowserCacheValidationInfos|Response|undefined> {
1195
+ let fileState = await jk_fs.getFileStat(filePath);
1196
+ if (!fileState) return undefined;
1197
+
1198
+ let lastModifiedSince = this.headers.get("if-modified-since");
1199
+
1200
+ if (lastModifiedSince) {
1201
+ const fileModifiedTime = new Date(fileState.mtimeMs).getTime();
1202
+ const clientModifiedTime = new Date(lastModifiedSince).getTime();
1203
+
1204
+ if (fileModifiedTime <= clientModifiedTime) {
1205
+ return new Response(null, {
1206
+ status: 304,
1207
+ headers: {"last-modified": new Date(fileState.mtimeMs).toUTCString()}
1208
+ });
1209
+ }
1210
+ }
1211
+
1212
+ let etag = this.headers.get("if-none-match")
1213
+ let calcEtag: string|undefined;
1214
+
1215
+ if (etag) {
1216
+ calcEtag = await jk_fs.calcFileHash(filePath);
1217
+
1218
+ if (etag === calcEtag) {
1219
+ return new Response(null, {
1220
+ status: 304,
1221
+ headers: {"etag": etag}
1222
+ });
1223
+ }
1224
+ }
1225
+
1226
+ if (!calcEtag) calcEtag = await jk_fs.calcFileHash(filePath);
1227
+ return {etag: calcEtag!, fileState: fileState!}
1228
+ }
1229
+
1230
+ //endregion
1231
+ }
1232
+
1233
+ export interface JopiRequestSpyData {
1234
+ method: string;
1235
+
1236
+ reqUrl: string;
1237
+ reqReferer: string | null;
1238
+ reqContentType: string | null;
1239
+ reqData: any;
1240
+
1241
+ // Allow avoiding printing the response content.
1242
+ res: (() => Response) | undefined | null;
1243
+
1244
+ resContentType: string | null;
1245
+ resContentTypeCat: ContentTypeCategory;
1246
+
1247
+ resStatus: number;
1248
+ resLocation: string | null;
1249
+ resHeaders: Headers | undefined | null;
1250
+
1251
+ reqCookies: string | null;
1252
+ resCookieSet: string[] | null;
1253
+ }
1254
+
1255
+
1256
+
1257
+ export type JopiRequestSpy = (data: JopiRequestSpyData, req: JopiRequest) => void;
1258
+
1259
+ export type FakeNoUserListener = (req: JopiRequest) => void|boolean;
1260
+
1261
+ export enum ContentTypeCategory {
1262
+ OTHER,
1263
+
1264
+ _TEXT_ = 10,
1265
+ TEXT_HTML = 11,
1266
+ TEXT_CSS = 12,
1267
+ TEXT_JAVASCRIPT = 13,
1268
+ TEXT_JSON = 14,
1269
+
1270
+ _FORM_ = 20,
1271
+ FORM_MULTIPART = 20,
1272
+ FORM_URL_ENCODED = 21,
1273
+
1274
+ _BINARY_ = 30,
1275
+ IMAGE
1276
+ }
1277
+
1278
+ const gEmptyObject = {};
1279
+ const gIsSinglePageMode = isSinglePageMode();