@saolabs/client 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (383) hide show
  1. package/README.md +52 -195
  2. package/dist/index.d.ts +20 -5
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +14 -3
  5. package/dist/index.js.map +1 -1
  6. package/dist/plugins.d.ts +2 -2
  7. package/dist/plugins.js +2 -2
  8. package/dist/src/core/app/Application.d.ts +25 -15
  9. package/dist/src/core/app/Application.d.ts.map +1 -1
  10. package/dist/src/core/app/Application.js +55 -18
  11. package/dist/src/core/app/Application.js.map +1 -1
  12. package/dist/src/core/bootstrap/app.d.ts +1 -1
  13. package/dist/src/core/bootstrap/app.d.ts.map +1 -1
  14. package/dist/src/core/bootstrap/app.js +77 -4
  15. package/dist/src/core/bootstrap/app.js.map +1 -1
  16. package/dist/src/core/bootstrap/bundle.d.ts +41 -0
  17. package/dist/src/core/bootstrap/bundle.d.ts.map +1 -0
  18. package/dist/src/core/bootstrap/bundle.js +98 -0
  19. package/dist/src/core/bootstrap/bundle.js.map +1 -0
  20. package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts +1 -1
  21. package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts.map +1 -1
  22. package/dist/src/core/bootstrap/providers/ApiServiceProvider.js +3 -3
  23. package/dist/src/core/bootstrap/providers/ApiServiceProvider.js.map +1 -1
  24. package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts +1 -1
  25. package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts.map +1 -1
  26. package/dist/src/core/bootstrap/providers/CoreServiceProvider.js +11 -8
  27. package/dist/src/core/bootstrap/providers/CoreServiceProvider.js.map +1 -1
  28. package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts +3 -1
  29. package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts.map +1 -1
  30. package/dist/src/core/bootstrap/providers/HelperServiceProvider.js +8 -3
  31. package/dist/src/core/bootstrap/providers/HelperServiceProvider.js.map +1 -1
  32. package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts +2 -1
  33. package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts.map +1 -1
  34. package/dist/src/core/bootstrap/providers/RouteServiceProvider.js +14 -4
  35. package/dist/src/core/bootstrap/providers/RouteServiceProvider.js.map +1 -1
  36. package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts +4 -4
  37. package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts.map +1 -1
  38. package/dist/src/core/bootstrap/providers/ServiceProvider.js +5 -5
  39. package/dist/src/core/bootstrap/providers/ServiceProvider.js.map +1 -1
  40. package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts +1 -1
  41. package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts.map +1 -1
  42. package/dist/src/core/bootstrap/providers/ViewServiceProvider.js +3 -3
  43. package/dist/src/core/bootstrap/providers/ViewServiceProvider.js.map +1 -1
  44. package/dist/src/core/bootstrap/providers/default-providers.d.ts +1 -1
  45. package/dist/src/core/bootstrap/providers/default-providers.d.ts.map +1 -1
  46. package/dist/src/core/bootstrap/providers/default-providers.js +44 -11
  47. package/dist/src/core/bootstrap/providers/default-providers.js.map +1 -1
  48. package/dist/src/core/bootstrap/providers/index.d.ts +9 -9
  49. package/dist/src/core/bootstrap/providers/index.d.ts.map +1 -1
  50. package/dist/src/core/bootstrap/providers/index.js +8 -8
  51. package/dist/src/core/bootstrap/providers/index.js.map +1 -1
  52. package/dist/src/core/bootstrap/providers/provider-order.d.ts +1 -1
  53. package/dist/src/core/bootstrap/providers/provider-order.d.ts.map +1 -1
  54. package/dist/src/core/bootstrap/ssr.d.ts +47 -0
  55. package/dist/src/core/bootstrap/ssr.d.ts.map +1 -0
  56. package/dist/src/core/bootstrap/ssr.js +129 -0
  57. package/dist/src/core/bootstrap/ssr.js.map +1 -0
  58. package/dist/src/core/cache/ViewCache.d.ts +1 -1
  59. package/dist/src/core/cache/ViewCache.d.ts.map +1 -1
  60. package/dist/src/core/cache/index.d.ts +1 -1
  61. package/dist/src/core/cache/index.d.ts.map +1 -1
  62. package/dist/src/core/cache/index.js +1 -1
  63. package/dist/src/core/cache/index.js.map +1 -1
  64. package/dist/src/core/contracts/ApplicationInterface.d.ts +15 -11
  65. package/dist/src/core/contracts/ApplicationInterface.d.ts.map +1 -1
  66. package/dist/src/core/contracts/BlockInterface.d.ts +10 -10
  67. package/dist/src/core/contracts/BlockInterface.d.ts.map +1 -1
  68. package/dist/src/core/contracts/BundleInterface.d.ts +38 -0
  69. package/dist/src/core/contracts/BundleInterface.d.ts.map +1 -0
  70. package/dist/src/core/contracts/BundleInterface.js +2 -0
  71. package/dist/src/core/contracts/BundleInterface.js.map +1 -0
  72. package/dist/src/core/contracts/ComponentInterface.d.ts +7 -6
  73. package/dist/src/core/contracts/ComponentInterface.d.ts.map +1 -1
  74. package/dist/src/core/contracts/ElementInterface.d.ts +91 -27
  75. package/dist/src/core/contracts/ElementInterface.d.ts.map +1 -1
  76. package/dist/src/core/contracts/HeadServiceInterface.d.ts +37 -0
  77. package/dist/src/core/contracts/HeadServiceInterface.d.ts.map +1 -0
  78. package/dist/src/core/contracts/HeadServiceInterface.js +3 -0
  79. package/dist/src/core/contracts/HeadServiceInterface.js.map +1 -0
  80. package/dist/src/core/contracts/HelperInterface.d.ts +9 -1
  81. package/dist/src/core/contracts/HelperInterface.d.ts.map +1 -1
  82. package/dist/src/core/contracts/HttpServiceInterface.d.ts +3 -0
  83. package/dist/src/core/contracts/HttpServiceInterface.d.ts.map +1 -1
  84. package/dist/src/core/contracts/MarkerInterface.d.ts +29 -44
  85. package/dist/src/core/contracts/MarkerInterface.d.ts.map +1 -1
  86. package/dist/src/core/contracts/ReactiveInterface.d.ts +9 -9
  87. package/dist/src/core/contracts/ReactiveInterface.d.ts.map +1 -1
  88. package/dist/src/core/contracts/RouterInterface.d.ts +5 -2
  89. package/dist/src/core/contracts/RouterInterface.d.ts.map +1 -1
  90. package/dist/src/core/contracts/SectionInterface.d.ts +48 -0
  91. package/dist/src/core/contracts/SectionInterface.d.ts.map +1 -0
  92. package/dist/src/core/contracts/SectionInterface.js +2 -0
  93. package/dist/src/core/contracts/SectionInterface.js.map +1 -0
  94. package/dist/src/core/contracts/ServiceProviderInterface.d.ts +3 -3
  95. package/dist/src/core/contracts/ServiceProviderInterface.d.ts.map +1 -1
  96. package/dist/src/core/contracts/ViewControllerInterface.d.ts +116 -15
  97. package/dist/src/core/contracts/ViewControllerInterface.d.ts.map +1 -1
  98. package/dist/src/core/contracts/ViewInterface.d.ts +31 -5
  99. package/dist/src/core/contracts/ViewInterface.d.ts.map +1 -1
  100. package/dist/src/core/contracts/ViewManagerInterface.d.ts +47 -5
  101. package/dist/src/core/contracts/ViewManagerInterface.d.ts.map +1 -1
  102. package/dist/src/core/contracts/ViewStateInterface.d.ts +39 -2
  103. package/dist/src/core/contracts/ViewStateInterface.d.ts.map +1 -1
  104. package/dist/src/core/contracts/common.d.ts +3 -3
  105. package/dist/src/core/contracts/common.js +3 -3
  106. package/dist/src/core/contracts/utils.d.ts +19 -19
  107. package/dist/src/core/contracts/utils.d.ts.map +1 -1
  108. package/dist/src/core/contracts/views.d.ts +1 -22
  109. package/dist/src/core/contracts/views.d.ts.map +1 -1
  110. package/dist/src/core/devtools/hook.d.ts +66 -0
  111. package/dist/src/core/devtools/hook.d.ts.map +1 -0
  112. package/dist/src/core/devtools/hook.js +114 -0
  113. package/dist/src/core/devtools/hook.js.map +1 -0
  114. package/dist/src/core/devtools/index.d.ts +34 -0
  115. package/dist/src/core/devtools/index.d.ts.map +1 -0
  116. package/dist/src/core/devtools/index.js +33 -0
  117. package/dist/src/core/devtools/index.js.map +1 -0
  118. package/dist/src/core/devtools/inspector.d.ts +31 -0
  119. package/dist/src/core/devtools/inspector.d.ts.map +1 -0
  120. package/dist/src/core/devtools/inspector.js +249 -0
  121. package/dist/src/core/devtools/inspector.js.map +1 -0
  122. package/dist/src/core/elements/Block.d.ts +9 -7
  123. package/dist/src/core/elements/Block.d.ts.map +1 -1
  124. package/dist/src/core/elements/Block.js +33 -9
  125. package/dist/src/core/elements/Block.js.map +1 -1
  126. package/dist/src/core/elements/BlockOutlet.d.ts +19 -8
  127. package/dist/src/core/elements/BlockOutlet.d.ts.map +1 -1
  128. package/dist/src/core/elements/BlockOutlet.js +57 -13
  129. package/dist/src/core/elements/BlockOutlet.js.map +1 -1
  130. package/dist/src/core/elements/Component.d.ts +83 -11
  131. package/dist/src/core/elements/Component.d.ts.map +1 -1
  132. package/dist/src/core/elements/Component.js +334 -13
  133. package/dist/src/core/elements/Component.js.map +1 -1
  134. package/dist/src/core/elements/ForeachSlotCache.d.ts +95 -0
  135. package/dist/src/core/elements/ForeachSlotCache.d.ts.map +1 -0
  136. package/dist/src/core/elements/ForeachSlotCache.js +149 -0
  137. package/dist/src/core/elements/ForeachSlotCache.js.map +1 -0
  138. package/dist/src/core/elements/Fragment.d.ts +19 -13
  139. package/dist/src/core/elements/Fragment.d.ts.map +1 -1
  140. package/dist/src/core/elements/Fragment.js +43 -20
  141. package/dist/src/core/elements/Fragment.js.map +1 -1
  142. package/dist/src/core/elements/Html.d.ts +89 -12
  143. package/dist/src/core/elements/Html.d.ts.map +1 -1
  144. package/dist/src/core/elements/Html.js +707 -106
  145. package/dist/src/core/elements/Html.js.map +1 -1
  146. package/dist/src/core/elements/Output.d.ts +34 -10
  147. package/dist/src/core/elements/Output.d.ts.map +1 -1
  148. package/dist/src/core/elements/Output.js +141 -26
  149. package/dist/src/core/elements/Output.js.map +1 -1
  150. package/dist/src/core/elements/Reactive.d.ts +85 -14
  151. package/dist/src/core/elements/Reactive.d.ts.map +1 -1
  152. package/dist/src/core/elements/Reactive.js +315 -41
  153. package/dist/src/core/elements/Reactive.js.map +1 -1
  154. package/dist/src/core/elements/TextElement.d.ts +11 -7
  155. package/dist/src/core/elements/TextElement.d.ts.map +1 -1
  156. package/dist/src/core/elements/TextElement.js +18 -11
  157. package/dist/src/core/elements/TextElement.js.map +1 -1
  158. package/dist/src/core/elements/Wrapper.d.ts +24 -13
  159. package/dist/src/core/elements/Wrapper.d.ts.map +1 -1
  160. package/dist/src/core/elements/Wrapper.js +65 -8
  161. package/dist/src/core/elements/Wrapper.js.map +1 -1
  162. package/dist/src/core/elements/Yield.d.ts +10 -9
  163. package/dist/src/core/elements/Yield.d.ts.map +1 -1
  164. package/dist/src/core/elements/Yield.js +31 -19
  165. package/dist/src/core/elements/Yield.js.map +1 -1
  166. package/dist/src/core/elements/helpers.d.ts +7 -7
  167. package/dist/src/core/elements/helpers.d.ts.map +1 -1
  168. package/dist/src/core/elements/helpers.js +4 -4
  169. package/dist/src/core/elements/index.d.ts +12 -11
  170. package/dist/src/core/elements/index.d.ts.map +1 -1
  171. package/dist/src/core/elements/index.js +12 -11
  172. package/dist/src/core/elements/index.js.map +1 -1
  173. package/dist/src/core/elements/reconcileChildren.d.ts +7 -0
  174. package/dist/src/core/elements/reconcileChildren.d.ts.map +1 -0
  175. package/dist/src/core/elements/reconcileChildren.js +74 -0
  176. package/dist/src/core/elements/reconcileChildren.js.map +1 -0
  177. package/dist/src/core/helpers/ApiClient.d.ts.map +1 -0
  178. package/dist/src/core/{hellpers → helpers}/ApiClient.js +10 -1
  179. package/dist/src/core/helpers/ApiClient.js.map +1 -0
  180. package/dist/src/core/helpers/app.d.ts +3 -0
  181. package/dist/src/core/helpers/app.d.ts.map +1 -0
  182. package/dist/src/core/{hellpers → helpers}/app.js +11 -1
  183. package/dist/src/core/helpers/app.js.map +1 -0
  184. package/dist/src/core/helpers/transition.d.ts +34 -0
  185. package/dist/src/core/helpers/transition.d.ts.map +1 -0
  186. package/dist/src/core/helpers/transition.js +118 -0
  187. package/dist/src/core/helpers/transition.js.map +1 -0
  188. package/dist/src/core/{hellpers → helpers}/utils.d.ts +2 -2
  189. package/dist/src/core/helpers/utils.d.ts.map +1 -0
  190. package/dist/src/core/{hellpers → helpers}/utils.js +4 -4
  191. package/dist/src/core/helpers/utils.js.map +1 -0
  192. package/dist/src/core/helpers/view.d.ts +39 -0
  193. package/dist/src/core/helpers/view.d.ts.map +1 -0
  194. package/dist/src/core/helpers/view.js +286 -0
  195. package/dist/src/core/helpers/view.js.map +1 -0
  196. package/dist/src/core/routers/Router.d.ts +108 -7
  197. package/dist/src/core/routers/Router.d.ts.map +1 -1
  198. package/dist/src/core/routers/Router.js +444 -68
  199. package/dist/src/core/routers/Router.js.map +1 -1
  200. package/dist/src/core/routers/index.d.ts +2 -2
  201. package/dist/src/core/routers/index.d.ts.map +1 -1
  202. package/dist/src/core/routers/index.js +1 -1
  203. package/dist/src/core/routers/index.js.map +1 -1
  204. package/dist/src/core/services/AssetManager.d.ts +112 -0
  205. package/dist/src/core/services/AssetManager.d.ts.map +1 -0
  206. package/dist/src/core/services/AssetManager.js +361 -0
  207. package/dist/src/core/services/AssetManager.js.map +1 -0
  208. package/dist/src/core/services/BlockManager.d.ts +73 -2
  209. package/dist/src/core/services/BlockManager.d.ts.map +1 -1
  210. package/dist/src/core/services/BlockManager.js +270 -48
  211. package/dist/src/core/services/BlockManager.js.map +1 -1
  212. package/dist/src/core/services/DomService.js +3 -3
  213. package/dist/src/core/services/DomService.js.map +1 -1
  214. package/dist/src/core/services/HeadService.d.ts +24 -0
  215. package/dist/src/core/services/HeadService.d.ts.map +1 -0
  216. package/dist/src/core/services/HeadService.js +120 -0
  217. package/dist/src/core/services/HeadService.js.map +1 -0
  218. package/dist/src/core/services/HelperService.d.ts +14 -3
  219. package/dist/src/core/services/HelperService.d.ts.map +1 -1
  220. package/dist/src/core/services/HelperService.js +32 -2
  221. package/dist/src/core/services/HelperService.js.map +1 -1
  222. package/dist/src/core/services/HttpService.d.ts +8 -0
  223. package/dist/src/core/services/HttpService.d.ts.map +1 -1
  224. package/dist/src/core/services/HttpService.js +121 -47
  225. package/dist/src/core/services/HttpService.js.map +1 -1
  226. package/dist/src/core/services/MarkerRegistry.d.ts +51 -8
  227. package/dist/src/core/services/MarkerRegistry.d.ts.map +1 -1
  228. package/dist/src/core/services/MarkerRegistry.js +135 -14
  229. package/dist/src/core/services/MarkerRegistry.js.map +1 -1
  230. package/dist/src/core/services/PageCache.d.ts +101 -0
  231. package/dist/src/core/services/PageCache.d.ts.map +1 -0
  232. package/dist/src/core/services/PageCache.js +184 -0
  233. package/dist/src/core/services/PageCache.js.map +1 -0
  234. package/dist/src/core/services/SectionManager.d.ts +100 -0
  235. package/dist/src/core/services/SectionManager.d.ts.map +1 -0
  236. package/dist/src/core/services/SectionManager.js +408 -0
  237. package/dist/src/core/services/SectionManager.js.map +1 -0
  238. package/dist/src/core/services/StorageService.js +1 -1
  239. package/dist/src/core/services/TranslationService.d.ts +21 -0
  240. package/dist/src/core/services/TranslationService.d.ts.map +1 -0
  241. package/dist/src/core/services/TranslationService.js +95 -0
  242. package/dist/src/core/services/TranslationService.js.map +1 -0
  243. package/dist/src/core/services/index.d.ts +11 -8
  244. package/dist/src/core/services/index.d.ts.map +1 -1
  245. package/dist/src/core/services/index.js +11 -8
  246. package/dist/src/core/services/index.js.map +1 -1
  247. package/dist/src/core/types/utils.d.ts +8 -8
  248. package/dist/src/core/types/utils.d.ts.map +1 -1
  249. package/dist/src/core/view/LoopContext.d.ts +21 -1
  250. package/dist/src/core/view/LoopContext.d.ts.map +1 -1
  251. package/dist/src/core/view/LoopContext.js +36 -0
  252. package/dist/src/core/view/LoopContext.js.map +1 -1
  253. package/dist/src/core/view/ResourceScope.d.ts +38 -0
  254. package/dist/src/core/view/ResourceScope.d.ts.map +1 -0
  255. package/dist/src/core/view/ResourceScope.js +167 -0
  256. package/dist/src/core/view/ResourceScope.js.map +1 -0
  257. package/dist/src/core/view/Section.d.ts +17 -32
  258. package/dist/src/core/view/Section.d.ts.map +1 -1
  259. package/dist/src/core/view/Section.js +14 -55
  260. package/dist/src/core/view/Section.js.map +1 -1
  261. package/dist/src/core/view/View.d.ts +43 -7
  262. package/dist/src/core/view/View.d.ts.map +1 -1
  263. package/dist/src/core/view/View.js +22 -4
  264. package/dist/src/core/view/View.js.map +1 -1
  265. package/dist/src/core/view/ViewController.d.ts +313 -40
  266. package/dist/src/core/view/ViewController.d.ts.map +1 -1
  267. package/dist/src/core/view/ViewController.js +1002 -147
  268. package/dist/src/core/view/ViewController.js.map +1 -1
  269. package/dist/src/core/view/ViewManager.d.ts +318 -27
  270. package/dist/src/core/view/ViewManager.d.ts.map +1 -1
  271. package/dist/src/core/view/ViewManager.js +1199 -258
  272. package/dist/src/core/view/ViewManager.js.map +1 -1
  273. package/dist/src/core/view/ViewState.d.ts +185 -6
  274. package/dist/src/core/view/ViewState.d.ts.map +1 -1
  275. package/dist/src/core/view/ViewState.js +851 -123
  276. package/dist/src/core/view/ViewState.js.map +1 -1
  277. package/dist/src/core/view/index.d.ts +5 -5
  278. package/dist/src/core/view/index.d.ts.map +1 -1
  279. package/dist/src/core/view/index.js +5 -5
  280. package/dist/src/core/view/index.js.map +1 -1
  281. package/dist/src/testing/index.d.ts +69 -0
  282. package/dist/src/testing/index.d.ts.map +1 -0
  283. package/dist/src/testing/index.js +140 -0
  284. package/dist/src/testing/index.js.map +1 -0
  285. package/index.ts +29 -5
  286. package/package.json +30 -21
  287. package/plugins.ts +2 -2
  288. package/src/core/app/Application.ts +72 -31
  289. package/src/core/bootstrap/app.ts +84 -6
  290. package/src/core/bootstrap/bundle.ts +101 -0
  291. package/src/core/bootstrap/providers/ApiServiceProvider.ts +4 -4
  292. package/src/core/bootstrap/providers/CoreServiceProvider.ts +14 -8
  293. package/src/core/bootstrap/providers/HelperServiceProvider.ts +8 -3
  294. package/src/core/bootstrap/providers/RouteServiceProvider.ts +15 -4
  295. package/src/core/bootstrap/providers/ServiceProvider.ts +8 -8
  296. package/src/core/bootstrap/providers/ViewServiceProvider.ts +3 -3
  297. package/src/core/bootstrap/providers/default-providers.ts +51 -16
  298. package/src/core/bootstrap/providers/index.ts +9 -9
  299. package/src/core/bootstrap/providers/provider-order.test.ts +16 -27
  300. package/src/core/bootstrap/providers/provider-order.ts +1 -1
  301. package/src/core/bootstrap/ssr.ts +141 -0
  302. package/src/core/cache/ViewCache.ts +1 -1
  303. package/src/core/cache/index.ts +1 -1
  304. package/src/core/contracts/ApplicationInterface.ts +15 -11
  305. package/src/core/contracts/BlockInterface.ts +18 -18
  306. package/src/core/contracts/BundleInterface.ts +40 -0
  307. package/src/core/contracts/ComponentInterface.ts +7 -6
  308. package/src/core/contracts/ElementInterface.ts +95 -27
  309. package/src/core/contracts/HeadServiceInterface.ts +41 -0
  310. package/src/core/contracts/HelperInterface.ts +9 -1
  311. package/src/core/contracts/HttpServiceInterface.ts +3 -0
  312. package/src/core/contracts/MarkerInterface.ts +22 -49
  313. package/src/core/contracts/ReactiveInterface.ts +9 -9
  314. package/src/core/contracts/RouterInterface.ts +5 -2
  315. package/src/core/contracts/SectionInterface.ts +49 -0
  316. package/src/core/contracts/ServiceProviderInterface.ts +3 -3
  317. package/src/core/contracts/ViewControllerInterface.ts +124 -16
  318. package/src/core/contracts/ViewInterface.ts +34 -5
  319. package/src/core/contracts/ViewManagerInterface.ts +40 -5
  320. package/src/core/contracts/ViewStateInterface.ts +39 -2
  321. package/src/core/contracts/common.ts +3 -3
  322. package/src/core/contracts/utils.ts +19 -19
  323. package/src/core/contracts/views.ts +2 -22
  324. package/src/core/devtools/hook.ts +142 -0
  325. package/src/core/devtools/index.ts +38 -0
  326. package/src/core/devtools/inspector.ts +270 -0
  327. package/src/core/elements/Block.ts +43 -21
  328. package/src/core/elements/BlockOutlet.ts +61 -21
  329. package/src/core/elements/Component.ts +352 -18
  330. package/src/core/elements/ElementManager.ts +9 -9
  331. package/src/core/elements/ForeachSlotCache.ts +170 -0
  332. package/src/core/elements/Fragment.ts +50 -32
  333. package/src/core/elements/Html.ts +696 -117
  334. package/src/core/elements/Output.ts +147 -30
  335. package/src/core/elements/Reactive.ts +329 -52
  336. package/src/core/elements/TextElement.ts +25 -16
  337. package/src/core/elements/Wrapper.ts +84 -18
  338. package/src/core/elements/Yield.ts +35 -24
  339. package/src/core/elements/helpers.ts +12 -12
  340. package/src/core/elements/index.ts +25 -24
  341. package/src/core/elements/reconcileChildren.ts +70 -0
  342. package/src/core/{hellpers → helpers}/ApiClient.ts +8 -1
  343. package/src/core/{hellpers → helpers}/app.ts +13 -2
  344. package/src/core/helpers/transition.ts +127 -0
  345. package/src/core/{hellpers → helpers}/utils.ts +4 -4
  346. package/src/core/helpers/view.ts +283 -0
  347. package/src/core/routers/Router.ts +458 -64
  348. package/src/core/routers/index.ts +2 -2
  349. package/src/core/services/AssetManager.ts +387 -0
  350. package/src/core/services/BlockManager.ts +272 -50
  351. package/src/core/services/DomService.ts +3 -3
  352. package/src/core/services/HeadService.ts +124 -0
  353. package/src/core/services/HelperService.ts +34 -4
  354. package/src/core/services/HttpService.ts +116 -55
  355. package/src/core/services/MarkerRegistry.ts +142 -16
  356. package/src/core/services/PageCache.ts +239 -0
  357. package/src/core/services/SectionManager.ts +387 -0
  358. package/src/core/services/StorageService.ts +1 -1
  359. package/src/core/services/TranslationService.ts +91 -0
  360. package/src/core/services/index.ts +11 -8
  361. package/src/core/types/utils.ts +8 -8
  362. package/src/core/view/LoopContext.ts +38 -1
  363. package/src/core/view/ResourceScope.ts +174 -0
  364. package/src/core/view/Section.ts +21 -68
  365. package/src/core/view/View.ts +57 -8
  366. package/src/core/view/ViewController.ts +1070 -186
  367. package/src/core/view/ViewManager.ts +1346 -272
  368. package/src/core/view/ViewState.ts +834 -116
  369. package/src/core/view/index.ts +5 -5
  370. package/src/testing/index.ts +184 -0
  371. package/dist/src/core/hellpers/ApiClient.d.ts.map +0 -1
  372. package/dist/src/core/hellpers/ApiClient.js.map +0 -1
  373. package/dist/src/core/hellpers/app.d.ts +0 -3
  374. package/dist/src/core/hellpers/app.d.ts.map +0 -1
  375. package/dist/src/core/hellpers/app.js.map +0 -1
  376. package/dist/src/core/hellpers/utils.d.ts.map +0 -1
  377. package/dist/src/core/hellpers/utils.js.map +0 -1
  378. package/dist/src/core/services/MarkerService.d.ts +0 -299
  379. package/dist/src/core/services/MarkerService.d.ts.map +0 -1
  380. package/dist/src/core/services/MarkerService.js +0 -723
  381. package/dist/src/core/services/MarkerService.js.map +0 -1
  382. package/src/core/services/MarkerService.ts +0 -923
  383. /package/dist/src/core/{hellpers → helpers}/ApiClient.d.ts +0 -0
@@ -20,6 +20,10 @@
20
20
  export interface HttpRequestConfig extends RequestInit {
21
21
  headers?: Record<string, string>;
22
22
  timeout?: number;
23
+ /** Default: cancel only an identical method + final URL. */
24
+ dedupe?: 'cancel-previous' | 'parallel';
25
+ /** Explicit logical identity, e.g. latest search across different queries. */
26
+ requestKey?: string;
23
27
  [key: string]: any;
24
28
  }
25
29
 
@@ -57,7 +61,8 @@ export class HttpService {
57
61
  private timeout: number = 10000;
58
62
  private defaultHeaders: Record<string, string> = {};
59
63
  private interceptors: HttpInterceptor[] = [];
60
- private pending = new Map<string, AbortController>();
64
+ private pending = new Map<string, Set<AbortController>>();
65
+ private activeRequests = new Set<AbortController>();
61
66
 
62
67
  // ─── Config ─────────────────────────────────────────────────
63
68
 
@@ -103,58 +108,84 @@ export class HttpService {
103
108
  data: any = null,
104
109
  options: HttpRequestConfig = {},
105
110
  ): Promise<HttpResponse<T>> {
106
- // Build full URL
107
- let fullUrl = url.startsWith('http') ? url : `${this.baseUrl}${url}`;
108
-
109
- // Cancel duplicate in-flight request
110
- const requestKey = `${method.toUpperCase()}:${fullUrl}`;
111
- this.pending.get(requestKey)?.abort();
112
-
113
111
  const controller = new AbortController();
114
- this.pending.set(requestKey, controller);
115
-
116
- // Build config
117
- let config: HttpRequestConfig = {
118
- method: method.toUpperCase(),
119
- headers: { ...this.defaultHeaders, ...options.headers },
120
- signal: options.signal ?? controller.signal,
121
- ...options,
112
+ this.activeRequests.add(controller);
113
+ const detach: (() => void)[] = [];
114
+ const linked = new Set<AbortSignal>();
115
+ const linkSignal = (signal?: AbortSignal | null) => {
116
+ if (!signal || signal === controller.signal || linked.has(signal)) return;
117
+ linked.add(signal);
118
+ const abort = () => controller.abort(signal.reason);
119
+ if (signal.aborted) abort();
120
+ else {
121
+ signal.addEventListener('abort', abort, { once: true });
122
+ detach.push(() => signal.removeEventListener('abort', abort));
123
+ }
122
124
  };
123
-
124
- // Apply request interceptors
125
- for (const i of this.interceptors) {
126
- if (i.request) config = await i.request(config);
127
- }
128
-
129
- // Body for POST/PUT/PATCH
130
- if (data && ['POST', 'PUT', 'PATCH'].includes(config.method!)) {
131
- const ct = (config.headers as Record<string, string>)?.['Content-Type'];
132
- if (data instanceof FormData) {
133
- config.body = data;
134
- delete (config.headers as Record<string, string>)['Content-Type'];
135
- } else if (ct === 'application/json' || !ct) {
136
- (config.headers as Record<string, string>)['Content-Type'] = 'application/json';
137
- config.body = JSON.stringify(data);
138
- } else {
139
- config.body = data;
125
+ linkSignal(options.signal);
126
+ let requestKey: string | undefined;
127
+ const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? this.timeout);
128
+ try {
129
+ let config: HttpRequestConfig = {
130
+ ...options,
131
+ method: (options.method ?? method).toUpperCase(),
132
+ headers: { ...this.defaultHeaders, ...options.headers },
133
+ signal: controller.signal,
134
+ };
135
+ if (typeof window !== 'undefined') {
136
+ const revision = (window as any).APP_CONFIGS?.view?.revision;
137
+ const headers = config.headers as Record<string, string>;
138
+ if (typeof revision === 'string' && !headers['X-Saola-View-Revision']) {
139
+ headers['X-Saola-View-Revision'] = revision;
140
+ headers['X-Sao-Response'] ??= 'json';
141
+ }
140
142
  }
141
- }
142
-
143
- // Query params for GET
144
- if (data && config.method === 'GET' && typeof data === 'object') {
145
- const urlObj = new URL(fullUrl, window.location.origin);
146
- for (const [key, value] of Object.entries(data)) {
147
- urlObj.searchParams.append(key, String(value));
143
+ controller.signal.throwIfAborted();
144
+ for (const i of this.interceptors) {
145
+ if (i.request) config = await i.request(config);
146
+ linkSignal(config.signal);
147
+ config.signal = controller.signal;
148
+ controller.signal.throwIfAborted();
148
149
  }
149
- fullUrl = urlObj.toString();
150
- }
151
-
152
- try {
153
- const timeoutId = setTimeout(() => controller.abort(), options.timeout ?? this.timeout);
150
+ config.method = (config.method ?? method).toUpperCase();
151
+ if (data !== null && ['POST', 'PUT', 'PATCH'].includes(config.method)) {
152
+ const ct = config.headers?.['Content-Type'];
153
+ if (data instanceof FormData) {
154
+ config.body = data;
155
+ if (config.headers) delete config.headers['Content-Type'];
156
+ } else if (ct === 'application/json' || !ct) {
157
+ config.headers ??= {};
158
+ config.headers['Content-Type'] = 'application/json';
159
+ config.body = JSON.stringify(data);
160
+ } else config.body = data;
161
+ }
162
+ const urlObj = this.resolveUrl(url);
163
+ if (data && config.method === 'GET' && typeof data === 'object') {
164
+ for (const [key, value] of Object.entries(data)) urlObj.searchParams.append(key, String(value));
165
+ }
166
+ const fullUrl = urlObj.toString();
167
+ requestKey = `${config.method}:${config.requestKey ?? fullUrl}`;
168
+ let group = this.pending.get(requestKey);
169
+ if (config.dedupe !== 'parallel') {
170
+ for (const previous of group ?? []) previous.abort();
171
+ }
172
+ if (!group) this.pending.set(requestKey, group = new Set());
173
+ group.add(controller);
154
174
  const raw = await fetch(fullUrl, config);
155
- clearTimeout(timeoutId);
156
-
157
175
  const responseData = await raw.json().catch(() => ({}));
176
+ controller.signal.throwIfAborted();
177
+
178
+ const viewContext = responseData?.viewContext;
179
+ const currentContext = typeof window !== 'undefined'
180
+ ? (window as any).APP_CONFIGS?.view?.systemData?.__context__
181
+ : null;
182
+ if (viewContext?.changed === true
183
+ && (!currentContext || viewContext.context === currentContext)
184
+ && typeof window !== 'undefined') {
185
+ window.dispatchEvent(new CustomEvent('saola:view-context', {
186
+ detail: viewContext,
187
+ }));
188
+ }
158
189
 
159
190
  let response: HttpResponse<T> = {
160
191
  status: raw.ok,
@@ -168,25 +199,43 @@ export class HttpService {
168
199
  if (i.response) response = await i.response(response);
169
200
  }
170
201
 
171
- this.pending.delete(requestKey);
202
+ controller.signal.throwIfAborted();
172
203
 
173
204
  if (!raw.ok) {
174
- throw Object.assign(new Error(`HTTP ${raw.status} ${raw.statusText}`), { response });
205
+ // Ưu tiên message của server (Laravel 422 trả "The email field is
206
+ // required."): caller nào cũng in `err.message`, mà "HTTP 422
207
+ // Unprocessable Content" thì người dùng không biết sai ở đâu.
208
+ // Body đầy đủ (kèm `errors` từng field) vẫn nằm ở `err.response`.
209
+ const serverMessage = typeof (responseData as any)?.message === 'string'
210
+ ? (responseData as any).message.trim()
211
+ : '';
212
+ const message = serverMessage !== ''
213
+ ? serverMessage
214
+ : `HTTP ${raw.status} ${raw.statusText}`;
215
+ throw Object.assign(new Error(message), { response });
175
216
  }
176
217
 
177
218
  return response;
178
219
  } catch (err) {
179
- this.pending.delete(requestKey);
180
220
 
181
221
  let error = err as Error;
182
222
  for (const i of this.interceptors) {
183
223
  if (i.error) error = await i.error(error);
184
224
  }
185
225
 
186
- if (error.name === 'AbortError') {
187
- throw new Error('Request cancelled');
226
+ if (controller.signal.aborted || error.name === 'AbortError') {
227
+ throw Object.assign(new Error('Request cancelled'), { name: 'AbortError', cause: error });
188
228
  }
189
229
  throw error;
230
+ } finally {
231
+ clearTimeout(timeoutId);
232
+ for (const off of detach) off();
233
+ this.activeRequests.delete(controller);
234
+ if (requestKey !== undefined) {
235
+ const group = this.pending.get(requestKey);
236
+ group?.delete(controller);
237
+ if (group?.size === 0) this.pending.delete(requestKey);
238
+ }
190
239
  }
191
240
  }
192
241
 
@@ -216,17 +265,29 @@ export class HttpService {
216
265
 
217
266
  /** Cancel all pending requests */
218
267
  cancelAll(): void {
219
- this.pending.forEach((c) => c.abort());
268
+ for (const controller of this.activeRequests) controller.abort();
220
269
  this.pending.clear();
221
270
  }
222
271
 
223
272
  /** Cancel a specific pending request */
224
273
  cancel(url: string, method: string = 'GET'): void {
225
- const key = `${method.toUpperCase()}:${url}`;
226
- this.pending.get(key)?.abort();
274
+ this.cancelKey(this.resolveUrl(url).toString(), method);
275
+ }
276
+
277
+ /** Cancel a logical requestKey (all parallel requests in the group). */
278
+ cancelKey(identity: string, method: string = 'GET'): void {
279
+ const key = `${method.toUpperCase()}:${identity}`;
280
+ for (const controller of this.pending.get(key) ?? []) controller.abort();
227
281
  this.pending.delete(key);
228
282
  }
229
283
 
284
+ private resolveUrl(url: string): URL {
285
+ const full = /^(https?:)?\/\//i.test(url) ? url : `${this.baseUrl}${url}`;
286
+ const resolved = new URL(full, typeof window !== 'undefined' ? window.location.origin : 'http://localhost');
287
+ resolved.hash = ''; // fragments are never sent to the server
288
+ return resolved;
289
+ }
290
+
230
291
  /** Destroy — cancel all + clear interceptors */
231
292
  destroy(): void {
232
293
  this.cancelAll();
@@ -18,12 +18,16 @@
18
18
  * - DevTools (future) — to inspect/debug the element tree via markers
19
19
  */
20
20
 
21
- import { MarkerRegistryRecord } from "../contracts/MarkerInterface";
21
+ import { MarkerRegistryInterface, MarkerRegistryRecord } from "../contracts/MarkerInterface.js";
22
22
 
23
23
 
24
- export class MarkerRegistryService {
24
+ export class MarkerRegistryService implements MarkerRegistryInterface {
25
25
  static class: string = 'MarkerRegistryService';
26
- /** Tag name → short abbreviation (for compact DOM comments) */
26
+ /**
27
+ * Tag name → short abbreviation (for compact DOM comments).
28
+ * PHẢI khớp 1-1 với server core/ViewStorageManager::$markerTagShortcut —
29
+ * lệch shortcut nào thì marker loại đó không claim được khi hydrate.
30
+ */
27
31
  private shortcuts: Record<string, string> = {
28
32
  view: 'v',
29
33
  component: 'c',
@@ -34,9 +38,12 @@ export class MarkerRegistryService {
34
38
  section: 's',
35
39
  fragment: 'frg',
36
40
  blockoutlet: 'bo',
41
+ output: 'o',
37
42
  for: 'fo',
38
43
  forin: 'fi',
39
44
  foreach: 'fe',
45
+ forelse: 'fls',
46
+ each: 'ea',
40
47
  while: 'wh',
41
48
  if: 'if',
42
49
  switch: 'sw',
@@ -60,6 +67,17 @@ export class MarkerRegistryService {
60
67
  /** Delimiter between tag shortcut and ID in keys */
61
68
  private delimiter: string = ':';
62
69
 
70
+ /**
71
+ * Saola marker prefix — format chuẩn (RUNTIME_CONTRACT.md §5.1):
72
+ * open: <!--s:{type}:{id}-s-->
73
+ * close: <!--s:{type}:{id}-e-->
74
+ * Phải khớp server (core ViewStorageManager) để hydration claim đúng.
75
+ */
76
+ private prefix: string = 's';
77
+ /** Hậu tố đánh dấu open/close marker */
78
+ private openSuffix: string = '-s';
79
+ private closeSuffix: string = '-e';
80
+
63
81
  /** Auto-increment counter for generating unique IDs */
64
82
  private counter: number = 0;
65
83
 
@@ -165,6 +183,7 @@ export class MarkerRegistryService {
165
183
  clear(): void {
166
184
  this.records.clear();
167
185
  this.counter = 0;
186
+ this.index = null;
168
187
  }
169
188
 
170
189
  /** Total number of registered markers */
@@ -172,41 +191,148 @@ export class MarkerRegistryService {
172
191
  return this.records.size;
173
192
  }
174
193
 
194
+ // ─── Marker Index (O(1) claim khi hydrate) ──────────────────
195
+
196
+ /**
197
+ * Index text-comment → Comment node, dựng 1 lần cho mỗi lượt hydrate.
198
+ *
199
+ * Trước đây mỗi element (Reactive/Output/Component/Wrapper/BlockOutlet/Block)
200
+ * tự tạo TreeWalker quét toàn bộ comment trong parent để tìm đúng 2 chuỗi →
201
+ * O(số element × số comment). Với 400 row × 5 cột (~4000 marker) đo được
202
+ * ~210ms chỉ để claim. Index 1 lượt rồi Map.get đưa về O(N) tổng, ~1.5ms.
203
+ *
204
+ * null = chưa dựng (lazy — CSR không bao giờ chạm tới).
205
+ */
206
+ private index: Map<string, Comment> | null = null;
207
+
208
+ /** Đã dựng lại index vì miss trong lượt microtask hiện tại chưa? */
209
+ private rebuiltOnMiss: boolean = false;
210
+
211
+ /** Bỏ index — gọi khi có HTML server MỚI vào DOM (đầu mỗi lượt hydrate). */
212
+ invalidateIndex(): void {
213
+ this.index = null;
214
+ }
215
+
216
+ private buildIndex(): Map<string, Comment> {
217
+ const idx = new Map<string, Comment>();
218
+ const root = document.body ?? document.documentElement;
219
+ if (root) {
220
+ const head = this.prefix + this.delimiter; // "s:"
221
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_COMMENT);
222
+ let node: Comment | null;
223
+ while ((node = walker.nextNode() as Comment | null)) {
224
+ const value = (node.nodeValue ?? '').trim();
225
+ // Giữ node ĐẦU TIÊN theo document order — đúng thứ tự mà
226
+ // TreeWalker scan cũ trả về.
227
+ if (value.startsWith(head) && !idx.has(value)) idx.set(value, node);
228
+ }
229
+ }
230
+ this.index = idx;
231
+ return idx;
232
+ }
233
+
234
+ /**
235
+ * Claim cặp marker SSR `<!--s:{tag}:{id}-s-->` … `<!--s:{tag}:{id}-e-->`.
236
+ *
237
+ * @param scope Nếu truyền, cặp tìm được PHẢI nằm trong scope — không thì
238
+ * rơi về scan tuyến tính trong scope (giữ nguyên ngữ nghĩa cũ).
239
+ * @returns null khi server không render vùng này (partial hydration).
240
+ */
241
+ claim(tag: string, id: string, scope?: Element | null): { open: Comment; close: Comment } | null {
242
+ const openText = this.openComment(tag, id);
243
+ const closeText = this.closeComment(tag, id);
244
+
245
+ let idx = this.index ?? this.buildIndex();
246
+ let open = idx.get(openText);
247
+ let close = idx.get(closeText);
248
+
249
+ // Miss có HAI nguyên nhân: (a) server không render vùng này — partial
250
+ // hydration, hợp lệ; (b) index cũ vì HTML server mới vào DOM sau lần
251
+ // dựng. Không phân biệt được trong O(1), nên dựng lại TỐI ĐA MỘT LẦN
252
+ // mỗi lượt microtask: burst hydrate đồng bộ chỉ tốn thêm 1 lượt quét,
253
+ // còn 499 miss thật sau đó vẫn O(1) (không quay lại O(N²)).
254
+ // Node rời DOM cũng là dấu hiệu index cũ → xử lý cùng nhánh.
255
+ const stale = (open && !open.isConnected) || (close && !close.isConnected);
256
+ if (stale || ((!open || !close) && !this.rebuiltOnMiss)) {
257
+ this.rebuiltOnMiss = true;
258
+ queueMicrotask(() => { this.rebuiltOnMiss = false; });
259
+ idx = this.buildIndex();
260
+ open = idx.get(openText);
261
+ close = idx.get(closeText);
262
+ }
263
+
264
+ if (!open || !close || !open.isConnected || !close.isConnected) return null;
265
+
266
+ if (scope && !(scope.contains(open) && scope.contains(close))) {
267
+ return this.scanForPair(scope, openText, closeText);
268
+ }
269
+ return { open, close };
270
+ }
271
+
272
+ /** Fallback hiếm: quét tuyến tính trong scope (ngữ nghĩa scan cũ). */
273
+ private scanForPair(scope: Node, openText: string, closeText: string): { open: Comment; close: Comment } | null {
274
+ const walker = document.createTreeWalker(scope, NodeFilter.SHOW_COMMENT);
275
+ let openNode: Comment | null = null;
276
+ let node: Comment | null;
277
+ while ((node = walker.nextNode() as Comment | null)) {
278
+ const value = (node.nodeValue ?? '').trim();
279
+ if (!openNode && value === openText) { openNode = node; continue; }
280
+ if (openNode && value === closeText) return { open: openNode, close: node };
281
+ }
282
+ return null;
283
+ }
284
+
175
285
  // ─── Comment Node Helpers ───────────────────────────────────
176
286
 
177
287
  /**
178
288
  * Create a comment string for a marker (open).
179
- * e.g. 'r:abc123' for <!--r:abc123-->
289
+ * Format chuẩn: 's:r:abc123-s' cho <!--s:r:abc123-s-->
180
290
  */
181
291
  openComment(tag: string, id?: string): string {
182
- return `${this.shortcut(tag)}${id ? this.delimiter + id : ''}`;
292
+ const body = id ? `${this.shortcut(tag)}${this.delimiter}${id}` : this.shortcut(tag);
293
+ return `${this.prefix}${this.delimiter}${body}${this.openSuffix}`;
183
294
  }
184
295
 
185
296
  /**
186
297
  * Create a comment string for a closing marker.
187
- * e.g. '/r:abc123' for <!--/r:abc123-->
298
+ * Format chuẩn: 's:r:abc123-e' cho <!--s:r:abc123-e-->
188
299
  */
189
300
  closeComment(tag: string, id?: string): string {
190
- return `/${this.shortcut(tag)}${id ? this.delimiter + id : ''}`;
301
+ const body = id ? `${this.shortcut(tag)}${this.delimiter}${id}` : this.shortcut(tag);
302
+ return `${this.prefix}${this.delimiter}${body}${this.closeSuffix}`;
191
303
  }
192
304
 
193
305
  /**
194
- * Parse a comment node's text to extract tag and id.
195
- * ' r:abc123 ' → { tag: 'reactive', id: 'abc123', isClose: false }
196
- * ' /r:abc123 ' → { tag: 'reactive', id: 'abc123', isClose: true }
306
+ * Parse a comment node's text to extract tag and id (format chuẩn §5.1).
307
+ * 's:r:abc123-s' → { tag: 'reactive', id: 'abc123', isClose: false }
308
+ * 's:r:abc123-e' → { tag: 'reactive', id: 'abc123', isClose: true }
197
309
  */
198
310
  parseComment(text: string): { tag: string; id: string; isClose: boolean } | null {
199
311
  const trimmed = text.trim();
200
312
  if (!trimmed) return null;
201
313
 
202
- const isClose = trimmed.startsWith('/');
203
- const content = isClose ? trimmed.slice(1) : trimmed;
204
- const delimIdx = content.indexOf(this.delimiter);
314
+ const head = this.prefix + this.delimiter; // "s:"
315
+ if (!trimmed.startsWith(head)) return null;
316
+
317
+ let body = trimmed.slice(head.length); // "r:abc123-s"
318
+ let isClose: boolean;
319
+ if (body.endsWith(this.openSuffix)) {
320
+ isClose = false;
321
+ body = body.slice(0, -this.openSuffix.length);
322
+ } else if (body.endsWith(this.closeSuffix)) {
323
+ isClose = true;
324
+ body = body.slice(0, -this.closeSuffix.length);
325
+ } else {
326
+ return null;
327
+ }
205
328
 
206
- if (delimIdx === -1) return null;
329
+ const delimIdx = body.indexOf(this.delimiter);
330
+ if (delimIdx === -1) {
331
+ return { tag: this.fullTag(body), id: '', isClose };
332
+ }
207
333
 
208
- const shortTag = content.slice(0, delimIdx);
209
- const id = content.slice(delimIdx + 1);
334
+ const shortTag = body.slice(0, delimIdx);
335
+ const id = body.slice(delimIdx + 1);
210
336
 
211
337
  return {
212
338
  tag: this.fullTag(shortTag),