@saolabs/client 1.0.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 +221 -0
  2. package/dist/index.d.ts +46 -0
  3. package/dist/index.d.ts.map +1 -0
  4. package/dist/index.js +38 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/plugins.d.ts +38 -0
  7. package/dist/plugins.d.ts.map +1 -0
  8. package/dist/plugins.js +61 -0
  9. package/dist/plugins.js.map +1 -0
  10. package/dist/src/core/app/Application.d.ts +159 -0
  11. package/dist/src/core/app/Application.d.ts.map +1 -0
  12. package/dist/src/core/app/Application.js +338 -0
  13. package/dist/src/core/app/Application.js.map +1 -0
  14. package/dist/src/core/bootstrap/app.d.ts +4 -0
  15. package/dist/src/core/bootstrap/app.d.ts.map +1 -0
  16. package/dist/src/core/bootstrap/app.js +49 -0
  17. package/dist/src/core/bootstrap/app.js.map +1 -0
  18. package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts +9 -0
  19. package/dist/src/core/bootstrap/providers/ApiServiceProvider.d.ts.map +1 -0
  20. package/dist/src/core/bootstrap/providers/ApiServiceProvider.js +18 -0
  21. package/dist/src/core/bootstrap/providers/ApiServiceProvider.js.map +1 -0
  22. package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts +6 -0
  23. package/dist/src/core/bootstrap/providers/CoreServiceProvider.d.ts.map +1 -0
  24. package/dist/src/core/bootstrap/providers/CoreServiceProvider.js +21 -0
  25. package/dist/src/core/bootstrap/providers/CoreServiceProvider.js.map +1 -0
  26. package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts +7 -0
  27. package/dist/src/core/bootstrap/providers/HelperServiceProvider.d.ts.map +1 -0
  28. package/dist/src/core/bootstrap/providers/HelperServiceProvider.js +14 -0
  29. package/dist/src/core/bootstrap/providers/HelperServiceProvider.js.map +1 -0
  30. package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts +9 -0
  31. package/dist/src/core/bootstrap/providers/RouteServiceProvider.d.ts.map +1 -0
  32. package/dist/src/core/bootstrap/providers/RouteServiceProvider.js +18 -0
  33. package/dist/src/core/bootstrap/providers/RouteServiceProvider.js.map +1 -0
  34. package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts +38 -0
  35. package/dist/src/core/bootstrap/providers/ServiceProvider.d.ts.map +1 -0
  36. package/dist/src/core/bootstrap/providers/ServiceProvider.js +41 -0
  37. package/dist/src/core/bootstrap/providers/ServiceProvider.js.map +1 -0
  38. package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts +9 -0
  39. package/dist/src/core/bootstrap/providers/ViewServiceProvider.d.ts.map +1 -0
  40. package/dist/src/core/bootstrap/providers/ViewServiceProvider.js +18 -0
  41. package/dist/src/core/bootstrap/providers/ViewServiceProvider.js.map +1 -0
  42. package/dist/src/core/bootstrap/providers/default-providers.d.ts +14 -0
  43. package/dist/src/core/bootstrap/providers/default-providers.d.ts.map +1 -0
  44. package/dist/src/core/bootstrap/providers/default-providers.js +79 -0
  45. package/dist/src/core/bootstrap/providers/default-providers.js.map +1 -0
  46. package/dist/src/core/bootstrap/providers/index.d.ts +10 -0
  47. package/dist/src/core/bootstrap/providers/index.d.ts.map +1 -0
  48. package/dist/src/core/bootstrap/providers/index.js +9 -0
  49. package/dist/src/core/bootstrap/providers/index.js.map +1 -0
  50. package/dist/src/core/bootstrap/providers/provider-order.d.ts +19 -0
  51. package/dist/src/core/bootstrap/providers/provider-order.d.ts.map +1 -0
  52. package/dist/src/core/bootstrap/providers/provider-order.js +51 -0
  53. package/dist/src/core/bootstrap/providers/provider-order.js.map +1 -0
  54. package/dist/src/core/cache/ViewCache.d.ts +46 -0
  55. package/dist/src/core/cache/ViewCache.d.ts.map +1 -0
  56. package/dist/src/core/cache/ViewCache.js +85 -0
  57. package/dist/src/core/cache/ViewCache.js.map +1 -0
  58. package/dist/src/core/cache/index.d.ts +2 -0
  59. package/dist/src/core/cache/index.d.ts.map +1 -0
  60. package/dist/src/core/cache/index.js +2 -0
  61. package/dist/src/core/cache/index.js.map +1 -0
  62. package/dist/src/core/contracts/ApiInterface.d.ts +11 -0
  63. package/dist/src/core/contracts/ApiInterface.d.ts.map +1 -0
  64. package/dist/src/core/contracts/ApiInterface.js +2 -0
  65. package/dist/src/core/contracts/ApiInterface.js.map +1 -0
  66. package/dist/src/core/contracts/ApplicationInterface.d.ts +66 -0
  67. package/dist/src/core/contracts/ApplicationInterface.d.ts.map +1 -0
  68. package/dist/src/core/contracts/ApplicationInterface.js +3 -0
  69. package/dist/src/core/contracts/ApplicationInterface.js.map +1 -0
  70. package/dist/src/core/contracts/BlockInterface.d.ts +67 -0
  71. package/dist/src/core/contracts/BlockInterface.d.ts.map +1 -0
  72. package/dist/src/core/contracts/BlockInterface.js +2 -0
  73. package/dist/src/core/contracts/BlockInterface.js.map +1 -0
  74. package/dist/src/core/contracts/ComponentInterface.d.ts +24 -0
  75. package/dist/src/core/contracts/ComponentInterface.d.ts.map +1 -0
  76. package/dist/src/core/contracts/ComponentInterface.js +2 -0
  77. package/dist/src/core/contracts/ComponentInterface.js.map +1 -0
  78. package/dist/src/core/contracts/ElementInterface.d.ts +117 -0
  79. package/dist/src/core/contracts/ElementInterface.d.ts.map +1 -0
  80. package/dist/src/core/contracts/ElementInterface.js +2 -0
  81. package/dist/src/core/contracts/ElementInterface.js.map +1 -0
  82. package/dist/src/core/contracts/EventServiceInterface.d.ts +37 -0
  83. package/dist/src/core/contracts/EventServiceInterface.d.ts.map +1 -0
  84. package/dist/src/core/contracts/EventServiceInterface.js +3 -0
  85. package/dist/src/core/contracts/EventServiceInterface.js.map +1 -0
  86. package/dist/src/core/contracts/HelperInterface.d.ts +231 -0
  87. package/dist/src/core/contracts/HelperInterface.d.ts.map +1 -0
  88. package/dist/src/core/contracts/HelperInterface.js +3 -0
  89. package/dist/src/core/contracts/HelperInterface.js.map +1 -0
  90. package/dist/src/core/contracts/HttpServiceInterface.d.ts +38 -0
  91. package/dist/src/core/contracts/HttpServiceInterface.d.ts.map +1 -0
  92. package/dist/src/core/contracts/HttpServiceInterface.js +3 -0
  93. package/dist/src/core/contracts/HttpServiceInterface.js.map +1 -0
  94. package/dist/src/core/contracts/LoggerServiceInterface.d.ts +37 -0
  95. package/dist/src/core/contracts/LoggerServiceInterface.d.ts.map +1 -0
  96. package/dist/src/core/contracts/LoggerServiceInterface.js +3 -0
  97. package/dist/src/core/contracts/LoggerServiceInterface.js.map +1 -0
  98. package/dist/src/core/contracts/LoopContextInterface.d.ts +14 -0
  99. package/dist/src/core/contracts/LoopContextInterface.d.ts.map +1 -0
  100. package/dist/src/core/contracts/LoopContextInterface.js +3 -0
  101. package/dist/src/core/contracts/LoopContextInterface.js.map +1 -0
  102. package/dist/src/core/contracts/MarkerInterface.d.ts +55 -0
  103. package/dist/src/core/contracts/MarkerInterface.d.ts.map +1 -0
  104. package/dist/src/core/contracts/MarkerInterface.js +2 -0
  105. package/dist/src/core/contracts/MarkerInterface.js.map +1 -0
  106. package/dist/src/core/contracts/ReactiveInterface.d.ts +38 -0
  107. package/dist/src/core/contracts/ReactiveInterface.d.ts.map +1 -0
  108. package/dist/src/core/contracts/ReactiveInterface.js +2 -0
  109. package/dist/src/core/contracts/ReactiveInterface.js.map +1 -0
  110. package/dist/src/core/contracts/RouterInterface.d.ts +58 -0
  111. package/dist/src/core/contracts/RouterInterface.d.ts.map +1 -0
  112. package/dist/src/core/contracts/RouterInterface.js +2 -0
  113. package/dist/src/core/contracts/RouterInterface.js.map +1 -0
  114. package/dist/src/core/contracts/ServiceProviderInterface.d.ts +12 -0
  115. package/dist/src/core/contracts/ServiceProviderInterface.d.ts.map +1 -0
  116. package/dist/src/core/contracts/ServiceProviderInterface.js +2 -0
  117. package/dist/src/core/contracts/ServiceProviderInterface.js.map +1 -0
  118. package/dist/src/core/contracts/StorageServiceInterface.d.ts +43 -0
  119. package/dist/src/core/contracts/StorageServiceInterface.d.ts.map +1 -0
  120. package/dist/src/core/contracts/StorageServiceInterface.js +3 -0
  121. package/dist/src/core/contracts/StorageServiceInterface.js.map +1 -0
  122. package/dist/src/core/contracts/StoreServiceInterface.d.ts +45 -0
  123. package/dist/src/core/contracts/StoreServiceInterface.d.ts.map +1 -0
  124. package/dist/src/core/contracts/StoreServiceInterface.js +3 -0
  125. package/dist/src/core/contracts/StoreServiceInterface.js.map +1 -0
  126. package/dist/src/core/contracts/ViewCacheInterface.d.ts +13 -0
  127. package/dist/src/core/contracts/ViewCacheInterface.d.ts.map +1 -0
  128. package/dist/src/core/contracts/ViewCacheInterface.js +3 -0
  129. package/dist/src/core/contracts/ViewCacheInterface.js.map +1 -0
  130. package/dist/src/core/contracts/ViewControllerInterface.d.ts +115 -0
  131. package/dist/src/core/contracts/ViewControllerInterface.d.ts.map +1 -0
  132. package/dist/src/core/contracts/ViewControllerInterface.js +2 -0
  133. package/dist/src/core/contracts/ViewControllerInterface.js.map +1 -0
  134. package/dist/src/core/contracts/ViewInterface.d.ts +24 -0
  135. package/dist/src/core/contracts/ViewInterface.d.ts.map +1 -0
  136. package/dist/src/core/contracts/ViewInterface.js +2 -0
  137. package/dist/src/core/contracts/ViewInterface.js.map +1 -0
  138. package/dist/src/core/contracts/ViewManagerInterface.d.ts +44 -0
  139. package/dist/src/core/contracts/ViewManagerInterface.d.ts.map +1 -0
  140. package/dist/src/core/contracts/ViewManagerInterface.js +2 -0
  141. package/dist/src/core/contracts/ViewManagerInterface.js.map +1 -0
  142. package/dist/src/core/contracts/ViewStateInterface.d.ts +40 -0
  143. package/dist/src/core/contracts/ViewStateInterface.d.ts.map +1 -0
  144. package/dist/src/core/contracts/ViewStateInterface.js +3 -0
  145. package/dist/src/core/contracts/ViewStateInterface.js.map +1 -0
  146. package/dist/src/core/contracts/common.d.ts +28 -0
  147. package/dist/src/core/contracts/common.d.ts.map +1 -0
  148. package/dist/src/core/contracts/common.js +29 -0
  149. package/dist/src/core/contracts/common.js.map +1 -0
  150. package/dist/src/core/contracts/utils.d.ts +20 -0
  151. package/dist/src/core/contracts/utils.d.ts.map +1 -0
  152. package/dist/src/core/contracts/utils.js +4 -0
  153. package/dist/src/core/contracts/utils.js.map +1 -0
  154. package/dist/src/core/contracts/views.d.ts +23 -0
  155. package/dist/src/core/contracts/views.d.ts.map +1 -0
  156. package/dist/src/core/contracts/views.js +2 -0
  157. package/dist/src/core/contracts/views.js.map +1 -0
  158. package/dist/src/core/elements/Block.d.ts +58 -0
  159. package/dist/src/core/elements/Block.d.ts.map +1 -0
  160. package/dist/src/core/elements/Block.js +77 -0
  161. package/dist/src/core/elements/Block.js.map +1 -0
  162. package/dist/src/core/elements/BlockOutlet.d.ts +34 -0
  163. package/dist/src/core/elements/BlockOutlet.d.ts.map +1 -0
  164. package/dist/src/core/elements/BlockOutlet.js +59 -0
  165. package/dist/src/core/elements/BlockOutlet.js.map +1 -0
  166. package/dist/src/core/elements/Component.d.ts +62 -0
  167. package/dist/src/core/elements/Component.d.ts.map +1 -0
  168. package/dist/src/core/elements/Component.js +72 -0
  169. package/dist/src/core/elements/Component.js.map +1 -0
  170. package/dist/src/core/elements/ElementManager.d.ts +23 -0
  171. package/dist/src/core/elements/ElementManager.d.ts.map +1 -0
  172. package/dist/src/core/elements/ElementManager.js +33 -0
  173. package/dist/src/core/elements/ElementManager.js.map +1 -0
  174. package/dist/src/core/elements/Fragment.d.ts +51 -0
  175. package/dist/src/core/elements/Fragment.d.ts.map +1 -0
  176. package/dist/src/core/elements/Fragment.js +133 -0
  177. package/dist/src/core/elements/Fragment.js.map +1 -0
  178. package/dist/src/core/elements/Html.d.ts +52 -0
  179. package/dist/src/core/elements/Html.d.ts.map +1 -0
  180. package/dist/src/core/elements/Html.js +299 -0
  181. package/dist/src/core/elements/Html.js.map +1 -0
  182. package/dist/src/core/elements/Output.d.ts +69 -0
  183. package/dist/src/core/elements/Output.d.ts.map +1 -0
  184. package/dist/src/core/elements/Output.js +121 -0
  185. package/dist/src/core/elements/Output.js.map +1 -0
  186. package/dist/src/core/elements/Reactive.d.ts +77 -0
  187. package/dist/src/core/elements/Reactive.d.ts.map +1 -0
  188. package/dist/src/core/elements/Reactive.js +199 -0
  189. package/dist/src/core/elements/Reactive.js.map +1 -0
  190. package/dist/src/core/elements/TextElement.d.ts +46 -0
  191. package/dist/src/core/elements/TextElement.d.ts.map +1 -0
  192. package/dist/src/core/elements/TextElement.js +94 -0
  193. package/dist/src/core/elements/TextElement.js.map +1 -0
  194. package/dist/src/core/elements/Wrapper.d.ts +50 -0
  195. package/dist/src/core/elements/Wrapper.d.ts.map +1 -0
  196. package/dist/src/core/elements/Wrapper.js +96 -0
  197. package/dist/src/core/elements/Wrapper.js.map +1 -0
  198. package/dist/src/core/elements/Yield.d.ts +34 -0
  199. package/dist/src/core/elements/Yield.d.ts.map +1 -0
  200. package/dist/src/core/elements/Yield.js +56 -0
  201. package/dist/src/core/elements/Yield.js.map +1 -0
  202. package/dist/src/core/elements/helpers.d.ts +8 -0
  203. package/dist/src/core/elements/helpers.d.ts.map +1 -0
  204. package/dist/src/core/elements/helpers.js +40 -0
  205. package/dist/src/core/elements/helpers.js.map +1 -0
  206. package/dist/src/core/elements/index.d.ts +12 -0
  207. package/dist/src/core/elements/index.d.ts.map +1 -0
  208. package/dist/src/core/elements/index.js +12 -0
  209. package/dist/src/core/elements/index.js.map +1 -0
  210. package/dist/src/core/hellpers/ApiClient.d.ts +58 -0
  211. package/dist/src/core/hellpers/ApiClient.d.ts.map +1 -0
  212. package/dist/src/core/hellpers/ApiClient.js +122 -0
  213. package/dist/src/core/hellpers/ApiClient.js.map +1 -0
  214. package/dist/src/core/hellpers/app.d.ts +3 -0
  215. package/dist/src/core/hellpers/app.d.ts.map +1 -0
  216. package/dist/src/core/hellpers/app.js +53 -0
  217. package/dist/src/core/hellpers/app.js.map +1 -0
  218. package/dist/src/core/hellpers/utils.d.ts +26 -0
  219. package/dist/src/core/hellpers/utils.d.ts.map +1 -0
  220. package/dist/src/core/hellpers/utils.js +96 -0
  221. package/dist/src/core/hellpers/utils.js.map +1 -0
  222. package/dist/src/core/routers/Router.d.ts +212 -0
  223. package/dist/src/core/routers/Router.d.ts.map +1 -0
  224. package/dist/src/core/routers/Router.js +623 -0
  225. package/dist/src/core/routers/Router.js.map +1 -0
  226. package/dist/src/core/routers/index.d.ts +3 -0
  227. package/dist/src/core/routers/index.d.ts.map +1 -0
  228. package/dist/src/core/routers/index.js +2 -0
  229. package/dist/src/core/routers/index.js.map +1 -0
  230. package/dist/src/core/services/BlockManager.d.ts +63 -0
  231. package/dist/src/core/services/BlockManager.d.ts.map +1 -0
  232. package/dist/src/core/services/BlockManager.js +191 -0
  233. package/dist/src/core/services/BlockManager.js.map +1 -0
  234. package/dist/src/core/services/DomService.d.ts +9 -0
  235. package/dist/src/core/services/DomService.d.ts.map +1 -0
  236. package/dist/src/core/services/DomService.js +18 -0
  237. package/dist/src/core/services/DomService.js.map +1 -0
  238. package/dist/src/core/services/EventService.d.ts +58 -0
  239. package/dist/src/core/services/EventService.d.ts.map +1 -0
  240. package/dist/src/core/services/EventService.js +212 -0
  241. package/dist/src/core/services/EventService.js.map +1 -0
  242. package/dist/src/core/services/HelperService.d.ts +443 -0
  243. package/dist/src/core/services/HelperService.d.ts.map +1 -0
  244. package/dist/src/core/services/HelperService.js +1650 -0
  245. package/dist/src/core/services/HelperService.js.map +1 -0
  246. package/dist/src/core/services/HttpService.d.ts +63 -0
  247. package/dist/src/core/services/HttpService.d.ts.map +1 -0
  248. package/dist/src/core/services/HttpService.js +182 -0
  249. package/dist/src/core/services/HttpService.js.map +1 -0
  250. package/dist/src/core/services/LoggerService.d.ts +56 -0
  251. package/dist/src/core/services/LoggerService.d.ts.map +1 -0
  252. package/dist/src/core/services/LoggerService.js +94 -0
  253. package/dist/src/core/services/LoggerService.js.map +1 -0
  254. package/dist/src/core/services/MarkerRegistry.d.ts +94 -0
  255. package/dist/src/core/services/MarkerRegistry.d.ts.map +1 -0
  256. package/dist/src/core/services/MarkerRegistry.js +199 -0
  257. package/dist/src/core/services/MarkerRegistry.js.map +1 -0
  258. package/dist/src/core/services/MarkerService.d.ts +299 -0
  259. package/dist/src/core/services/MarkerService.d.ts.map +1 -0
  260. package/dist/src/core/services/MarkerService.js +723 -0
  261. package/dist/src/core/services/MarkerService.js.map +1 -0
  262. package/dist/src/core/services/StorageService.d.ts +69 -0
  263. package/dist/src/core/services/StorageService.d.ts.map +1 -0
  264. package/dist/src/core/services/StorageService.js +243 -0
  265. package/dist/src/core/services/StorageService.js.map +1 -0
  266. package/dist/src/core/services/StoreService.d.ts +80 -0
  267. package/dist/src/core/services/StoreService.d.ts.map +1 -0
  268. package/dist/src/core/services/StoreService.js +301 -0
  269. package/dist/src/core/services/StoreService.js.map +1 -0
  270. package/dist/src/core/services/index.d.ts +9 -0
  271. package/dist/src/core/services/index.d.ts.map +1 -0
  272. package/dist/src/core/services/index.js +9 -0
  273. package/dist/src/core/services/index.js.map +1 -0
  274. package/dist/src/core/types/utils.d.ts +29 -0
  275. package/dist/src/core/types/utils.d.ts.map +1 -0
  276. package/dist/src/core/types/utils.js +22 -0
  277. package/dist/src/core/types/utils.js.map +1 -0
  278. package/dist/src/core/view/LoopContext.d.ts +38 -0
  279. package/dist/src/core/view/LoopContext.d.ts.map +1 -0
  280. package/dist/src/core/view/LoopContext.js +100 -0
  281. package/dist/src/core/view/LoopContext.js.map +1 -0
  282. package/dist/src/core/view/Section.d.ts +41 -0
  283. package/dist/src/core/view/Section.d.ts.map +1 -0
  284. package/dist/src/core/view/Section.js +67 -0
  285. package/dist/src/core/view/Section.js.map +1 -0
  286. package/dist/src/core/view/View.d.ts +63 -0
  287. package/dist/src/core/view/View.d.ts.map +1 -0
  288. package/dist/src/core/view/View.js +85 -0
  289. package/dist/src/core/view/View.js.map +1 -0
  290. package/dist/src/core/view/ViewController.d.ts +277 -0
  291. package/dist/src/core/view/ViewController.d.ts.map +1 -0
  292. package/dist/src/core/view/ViewController.js +862 -0
  293. package/dist/src/core/view/ViewController.js.map +1 -0
  294. package/dist/src/core/view/ViewManager.d.ts +141 -0
  295. package/dist/src/core/view/ViewManager.d.ts.map +1 -0
  296. package/dist/src/core/view/ViewManager.js +565 -0
  297. package/dist/src/core/view/ViewManager.js.map +1 -0
  298. package/dist/src/core/view/ViewState.d.ts +117 -0
  299. package/dist/src/core/view/ViewState.d.ts.map +1 -0
  300. package/dist/src/core/view/ViewState.js +410 -0
  301. package/dist/src/core/view/ViewState.js.map +1 -0
  302. package/dist/src/core/view/index.d.ts +6 -0
  303. package/dist/src/core/view/index.d.ts.map +1 -0
  304. package/dist/src/core/view/index.js +6 -0
  305. package/dist/src/core/view/index.js.map +1 -0
  306. package/index.ts +62 -0
  307. package/package.json +70 -0
  308. package/plugins.ts +77 -0
  309. package/src/core/app/Application.ts +410 -0
  310. package/src/core/bootstrap/app.ts +58 -0
  311. package/src/core/bootstrap/providers/ApiServiceProvider.ts +18 -0
  312. package/src/core/bootstrap/providers/CoreServiceProvider.ts +19 -0
  313. package/src/core/bootstrap/providers/HelperServiceProvider.ts +12 -0
  314. package/src/core/bootstrap/providers/RouteServiceProvider.ts +17 -0
  315. package/src/core/bootstrap/providers/ServiceProvider.ts +51 -0
  316. package/src/core/bootstrap/providers/ViewServiceProvider.ts +17 -0
  317. package/src/core/bootstrap/providers/default-providers.ts +94 -0
  318. package/src/core/bootstrap/providers/index.ts +9 -0
  319. package/src/core/bootstrap/providers/provider-order.test.ts +67 -0
  320. package/src/core/bootstrap/providers/provider-order.ts +66 -0
  321. package/src/core/cache/ViewCache.ts +102 -0
  322. package/src/core/cache/index.ts +1 -0
  323. package/src/core/contracts/ApiInterface.ts +11 -0
  324. package/src/core/contracts/ApplicationInterface.ts +79 -0
  325. package/src/core/contracts/BlockInterface.ts +76 -0
  326. package/src/core/contracts/ComponentInterface.ts +26 -0
  327. package/src/core/contracts/ElementInterface.ts +140 -0
  328. package/src/core/contracts/EventServiceInterface.ts +47 -0
  329. package/src/core/contracts/HelperInterface.ts +256 -0
  330. package/src/core/contracts/HttpServiceInterface.ts +51 -0
  331. package/src/core/contracts/LoggerServiceInterface.ts +48 -0
  332. package/src/core/contracts/LoopContextInterface.ts +15 -0
  333. package/src/core/contracts/MarkerInterface.ts +77 -0
  334. package/src/core/contracts/ReactiveInterface.ts +45 -0
  335. package/src/core/contracts/RouterInterface.ts +65 -0
  336. package/src/core/contracts/ServiceProviderInterface.ts +12 -0
  337. package/src/core/contracts/StorageServiceInterface.ts +48 -0
  338. package/src/core/contracts/StoreServiceInterface.ts +50 -0
  339. package/src/core/contracts/ViewCacheInterface.ts +14 -0
  340. package/src/core/contracts/ViewControllerInterface.ts +135 -0
  341. package/src/core/contracts/ViewInterface.ts +31 -0
  342. package/src/core/contracts/ViewManagerInterface.ts +45 -0
  343. package/src/core/contracts/ViewStateInterface.ts +47 -0
  344. package/src/core/contracts/common.ts +27 -0
  345. package/src/core/contracts/utils.ts +23 -0
  346. package/src/core/contracts/views.ts +23 -0
  347. package/src/core/elements/Block.ts +113 -0
  348. package/src/core/elements/BlockOutlet.ts +83 -0
  349. package/src/core/elements/Component.ts +114 -0
  350. package/src/core/elements/ElementManager.ts +48 -0
  351. package/src/core/elements/Fragment.ts +168 -0
  352. package/src/core/elements/Html.ts +364 -0
  353. package/src/core/elements/Output.ts +167 -0
  354. package/src/core/elements/Reactive.ts +253 -0
  355. package/src/core/elements/TextElement.ts +110 -0
  356. package/src/core/elements/Wrapper.ts +131 -0
  357. package/src/core/elements/Yield.ts +70 -0
  358. package/src/core/elements/helpers.ts +56 -0
  359. package/src/core/elements/index.ts +26 -0
  360. package/src/core/hellpers/ApiClient.ts +145 -0
  361. package/src/core/hellpers/app.ts +57 -0
  362. package/src/core/hellpers/utils.ts +109 -0
  363. package/src/core/routers/Router.ts +765 -0
  364. package/src/core/routers/index.ts +2 -0
  365. package/src/core/services/BlockManager.ts +210 -0
  366. package/src/core/services/DomService.ts +26 -0
  367. package/src/core/services/EventService.ts +266 -0
  368. package/src/core/services/HelperService.ts +1653 -0
  369. package/src/core/services/HttpService.ts +235 -0
  370. package/src/core/services/LoggerService.ts +139 -0
  371. package/src/core/services/MarkerRegistry.ts +239 -0
  372. package/src/core/services/MarkerService.ts +923 -0
  373. package/src/core/services/StorageService.ts +268 -0
  374. package/src/core/services/StoreService.ts +338 -0
  375. package/src/core/services/index.ts +8 -0
  376. package/src/core/types/utils.ts +30 -0
  377. package/src/core/view/LoopContext.ts +109 -0
  378. package/src/core/view/Section.ts +86 -0
  379. package/src/core/view/View.ts +109 -0
  380. package/src/core/view/ViewController.ts +1002 -0
  381. package/src/core/view/ViewManager.ts +700 -0
  382. package/src/core/view/ViewState.ts +453 -0
  383. package/src/core/view/index.ts +5 -0
@@ -0,0 +1,765 @@
1
+ /**
2
+ * Router — SPA Router cho OneView v3.
3
+ *
4
+ * Port từ core/Router nhưng nhẹ hơn (~400 dòng thay vì 960),
5
+ * tích hợp trực tiếp với ViewManager mới.
6
+ *
7
+ * Features:
8
+ * - History API / Hash mode
9
+ * - Laravel-style route patterns: {param}, {param?}
10
+ * - Navigation guards (beforeEach, afterEach)
11
+ * - Named routes + URL generation
12
+ * - Auto navigation (link interception)
13
+ * - Route caching (pattern match + ActiveRoute)
14
+ * - Browser back/forward handling
15
+ */
16
+
17
+ import type { ViewManagerInterface } from "../contracts/ViewManagerInterface";
18
+
19
+ // ─── Types ──────────────────────────────────────────────────────
20
+
21
+ export interface RouteDefinition {
22
+ /** URL pattern: '/users/{id}', '/posts/{page?}' */
23
+ path: string;
24
+ /** View name: 'web.home', 'layouts.main' */
25
+ component?: string;
26
+ /** @deprecated Use component */
27
+ view?: string;
28
+ /** Named route identifier */
29
+ name?: string;
30
+ /** Route metadata (auth, roles, etc.) */
31
+ meta?: Record<string, any>;
32
+ }
33
+
34
+ export interface Route {
35
+ path: string;
36
+ component?: string;
37
+ /** @deprecated */
38
+ view?: string;
39
+ name?: string;
40
+ params?: Record<string, string>;
41
+ query?: Record<string, string>;
42
+ meta?: Record<string, any>;
43
+ }
44
+
45
+ export interface RouteMatch {
46
+ route: Route;
47
+ params: Record<string, string>;
48
+ }
49
+
50
+ export interface RouterConfig {
51
+ mode?: 'history' | 'hash';
52
+ base?: string;
53
+ defaultRoute?: string;
54
+ routes?: RouteDefinition[];
55
+ allRoutes?: RouteDefinition[];
56
+ beforeEach?: NavigationGuard;
57
+ afterEach?: AfterNavigationHook;
58
+ }
59
+
60
+ export type NavigationGuard = (to: Route, from: ActiveRoute | null, urlPath: string) => boolean | Promise<boolean>;
61
+ export type AfterNavigationHook = (to: Route, from: ActiveRoute | null) => void;
62
+
63
+ // ─── ActiveRoute ────────────────────────────────────────────────
64
+
65
+ export class ActiveRoute {
66
+ public readonly $route: Route;
67
+ public readonly $urlPath: string;
68
+ public readonly $params: Record<string, string>;
69
+ public readonly $query: Record<string, string>;
70
+ public readonly $fragment: string;
71
+
72
+ constructor(
73
+ route: Route,
74
+ urlPath: string,
75
+ params: Record<string, string> = {},
76
+ query: Record<string, string> = {},
77
+ fragment: string = ''
78
+ ) {
79
+ this.$route = route;
80
+ this.$urlPath = urlPath;
81
+ this.$params = params;
82
+ this.$query = query;
83
+ this.$fragment = fragment;
84
+
85
+ // Dynamic param access: activeRoute.id → activeRoute.$params.id
86
+ for (const key of Object.keys(params)) {
87
+ Object.defineProperty(this, key, {
88
+ get: () => this.$params[key],
89
+ enumerable: true,
90
+ configurable: false,
91
+ });
92
+ }
93
+ for (const key of Object.keys(query)) {
94
+ if (!(key in this)) {
95
+ Object.defineProperty(this, key, {
96
+ get: () => this.$query[key],
97
+ enumerable: true,
98
+ configurable: true,
99
+ });
100
+ }
101
+ }
102
+ }
103
+
104
+ setQuery(query: Record<string, string>): void {
105
+ if (typeof query !== 'object' || query === null) return;
106
+ for (const key in query) {
107
+ this.$query[key] = query[key];
108
+ if (!(key in this)) {
109
+ Object.defineProperty(this, key, {
110
+ get: () => this.$query[key],
111
+ enumerable: true,
112
+ configurable: true,
113
+ });
114
+ }
115
+ }
116
+ }
117
+
118
+ getPath(): string { return this.$urlPath; }
119
+ getParams(): Record<string, string> { return this.$params; }
120
+ getParam(name: string): string | null { return this.$params[name] ?? null; }
121
+ getQuery(): Record<string, string> { return this.$query; }
122
+ param(name: string): string | null { return this.$params[name] ?? null; }
123
+ query(name: string): string | null { return this.$query[name] ?? null; }
124
+ }
125
+
126
+ // ─── Router ─────────────────────────────────────────────────────
127
+
128
+ export class Router {
129
+ /** Global active route (static access) */
130
+ public static activeRoute: ActiveRoute | null = null;
131
+
132
+ /** ViewManager integration */
133
+ private viewManager: ViewManagerInterface | null = null;
134
+
135
+ /** App reference */
136
+ private App: any = null;
137
+
138
+ /** Route table */
139
+ private routes: Array<{ path: string; component: string; options: any }> = [];
140
+
141
+ /** Named routes config */
142
+ private routeConfigs: Record<string, RouteDefinition> = {};
143
+
144
+ /** Current active route (instance) */
145
+ private currentRoute: ActiveRoute | null = null;
146
+
147
+ /** Router mode */
148
+ private mode: 'history' | 'hash' = 'history';
149
+
150
+ /** Base path prefix */
151
+ private base: string = '';
152
+
153
+ /** Default route (fallback) */
154
+ private defaultRoute: string = '/';
155
+
156
+ /** Current URI for duplicate detection */
157
+ private currentUri: string = '';
158
+
159
+ /** Navigation guards */
160
+ private _beforeEach: NavigationGuard | null = null;
161
+ private _afterEach: AfterNavigationHook | null = null;
162
+
163
+ /** Caches */
164
+ private routeCache: Map<string, RouteMatch | null> = new Map();
165
+
166
+ /** State */
167
+ private isStarted = false;
168
+ private isNavigating = false;
169
+
170
+ /** Bound handlers for cleanup */
171
+ private _handlePopState: () => void;
172
+ private _handleAutoNavigation: (e: MouseEvent) => void;
173
+
174
+ constructor(app?: any) {
175
+ this.App = app || null;
176
+ this.currentUri = typeof window !== 'undefined'
177
+ ? window.location.pathname + window.location.search
178
+ : '/';
179
+
180
+ this._handlePopState = this.handlePopState.bind(this);
181
+ this._handleAutoNavigation = this.handleAutoNavigation.bind(this);
182
+ }
183
+
184
+ // ─── Configuration ──────────────────────────────────────────
185
+
186
+ init(config: RouterConfig): this {
187
+
188
+ return this;
189
+ }
190
+
191
+ setApp(app: any): this {
192
+ this.App = app;
193
+ return this;
194
+ }
195
+
196
+ setViewManager(vm: ViewManagerInterface): this {
197
+ this.viewManager = vm;
198
+ return this;
199
+ }
200
+
201
+ setMode(mode: 'history' | 'hash'): this {
202
+ this.mode = mode;
203
+ return this;
204
+ }
205
+
206
+ setBase(base: string): this {
207
+ this.base = base;
208
+ return this;
209
+ }
210
+
211
+ setDefaultRoute(route: string): this {
212
+ this.defaultRoute = route;
213
+ return this;
214
+ }
215
+
216
+ // ─── Route Registration ─────────────────────────────────────
217
+
218
+ /**
219
+ * Add a single route.
220
+ */
221
+ addRoute(path: string, component: string, options: any = {}): this {
222
+ this.routes.push({ path, component, options });
223
+ this.routeCache.clear();
224
+ return this;
225
+ }
226
+
227
+ /**
228
+ * Add named route config.
229
+ */
230
+ addRouteConfig(config: RouteDefinition): this {
231
+ if (config.name) {
232
+ this.routeConfigs[config.name] = config;
233
+ }
234
+ return this;
235
+ }
236
+
237
+ /**
238
+ * Add multiple routes at once.
239
+ */
240
+ addRoutes(routes: RouteDefinition[]): this {
241
+ for (const route of routes) {
242
+ const component = route.component || route.view || '';
243
+ this.addRoute(route.path, component, route.meta || {});
244
+ if (route.name) {
245
+ this.addRouteConfig(route);
246
+ }
247
+ }
248
+ return this;
249
+ }
250
+
251
+ /**
252
+ * Configure router from a config object.
253
+ */
254
+ configure(config: RouterConfig): this {
255
+ if (config.mode) this.mode = config.mode;
256
+ if (config.base) this.base = config.base;
257
+ if (config.defaultRoute) this.defaultRoute = config.defaultRoute;
258
+ if (config.routes) this.addRoutes(config.routes);
259
+ if (config.allRoutes) this.addRoutes(config.allRoutes);
260
+ if (config.beforeEach) this._beforeEach = config.beforeEach;
261
+ if (config.afterEach) this._afterEach = config.afterEach;
262
+ return this;
263
+ }
264
+
265
+ // ─── Guards ─────────────────────────────────────────────────
266
+
267
+ beforeEach(guard: NavigationGuard): this {
268
+ this._beforeEach = guard;
269
+ return this;
270
+ }
271
+
272
+ afterEach(hook: AfterNavigationHook): this {
273
+ this._afterEach = hook;
274
+ return this;
275
+ }
276
+
277
+ // ─── Navigation ─────────────────────────────────────────────
278
+
279
+ /**
280
+ * Navigate to a URL path.
281
+ */
282
+ navigate(path: string): void {
283
+ if (this.mode === 'history') {
284
+ window.history.pushState({}, '', path);
285
+ } else {
286
+ window.location.hash = path;
287
+ }
288
+ this.handleRoute(path);
289
+ this.currentUri = path;
290
+ }
291
+
292
+ /** Alias for navigate */
293
+ push(path: string): void {
294
+ this.navigate(path);
295
+ }
296
+
297
+ /**
298
+ * Navigate to a named route with params.
299
+ */
300
+ navigateTo(routeName: string, params: Record<string, any> = {}): void {
301
+ const url = this.getURL(routeName, params);
302
+ if (url) {
303
+ this.navigate(url);
304
+ } else {
305
+ console.error(`[Router] Named route "${routeName}" not found`);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Replace current history entry without adding to stack.
311
+ */
312
+ replace(path: string): void {
313
+ if (this.mode === 'history') {
314
+ window.history.replaceState({}, '', path);
315
+ } else {
316
+ window.location.hash = path;
317
+ }
318
+ this.handleRoute(path);
319
+ this.currentUri = path;
320
+ }
321
+
322
+ /**
323
+ * Go back in history.
324
+ */
325
+ back(): void {
326
+ window.history.back();
327
+ }
328
+
329
+ /**
330
+ * Go forward in history.
331
+ */
332
+ forward(): void {
333
+ window.history.forward();
334
+ }
335
+
336
+ // ─── URL Generation ─────────────────────────────────────────
337
+
338
+ /**
339
+ * Generate URL for a named route.
340
+ */
341
+ getURL(name: string, params: Record<string, any> = {}): string | null {
342
+ const config = this.routeConfigs[name];
343
+ if (!config) return null;
344
+
345
+ let url = this.generateUrl(config.path, params);
346
+ if (!(url.startsWith('/') || url.startsWith('http'))) {
347
+ url = this.base + url;
348
+ }
349
+ return url;
350
+ }
351
+
352
+ /**
353
+ * Generate URL with parameters (Laravel route() style).
354
+ *
355
+ * - Required params {name}: must be provided
356
+ * - Optional params {name?}: removed from URL if not provided
357
+ * - Extra params: appended as query string
358
+ *
359
+ * @example
360
+ * generateUrl('/cate/{slug}', { slug: 'demo', page: 2 })
361
+ * // → '/cate/demo?page=2'
362
+ */
363
+ generateUrl(pattern: string, params: Record<string, any> = {}): string {
364
+ let url = pattern;
365
+ const usedKeys = new Set<string>();
366
+
367
+ // Required params: {name}
368
+ const requiredParams = [...pattern.matchAll(/\{([a-zA-Z0-9_]+)\}/g)]
369
+ .filter(m => !m[0].endsWith('?}'))
370
+ .map(m => m[1]);
371
+
372
+ // Optional params: {name?}
373
+ const optionalParams = [...pattern.matchAll(/\{([a-zA-Z0-9_]+)\?\}/g)]
374
+ .map(m => m[1]);
375
+
376
+ for (const name of requiredParams) {
377
+ if (params[name] == null) {
378
+ console.error(`[Router] Missing required param: ${name} in ${pattern}`);
379
+ return url;
380
+ }
381
+ url = url.replace(`{${name}}`, encodeURIComponent(String(params[name])));
382
+ usedKeys.add(name);
383
+ }
384
+
385
+ for (const name of optionalParams) {
386
+ if (params[name] != null && params[name] !== '') {
387
+ url = url.replace(`{${name}?}`, encodeURIComponent(String(params[name])));
388
+ usedKeys.add(name);
389
+ } else {
390
+ url = url.replace(`/{${name}?}`, '');
391
+ url = url.replace(`{${name}?}`, '');
392
+ }
393
+ }
394
+
395
+ url = this.normalizePath(url);
396
+
397
+ // Extra params → query string
398
+ const queryParts: string[] = [];
399
+ for (const [key, value] of Object.entries(params)) {
400
+ if (!usedKeys.has(key) && value != null) {
401
+ queryParts.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
402
+ }
403
+ }
404
+ if (queryParts.length > 0) {
405
+ url += '?' + queryParts.join('&');
406
+ }
407
+
408
+ return url;
409
+ }
410
+
411
+ // ─── Route Matching ─────────────────────────────────────────
412
+
413
+ /**
414
+ * Match a path against registered routes.
415
+ */
416
+ match(path: string): RouteMatch | null {
417
+ return this.matchRoute(path);
418
+ }
419
+
420
+ getCurrentRoute(): ActiveRoute | null {
421
+ return this.currentRoute;
422
+ }
423
+
424
+ // ─── Lifecycle ──────────────────────────────────────────────
425
+
426
+ /**
427
+ * Start the router — attach event listeners and handle initial route.
428
+ */
429
+ start(skipInitial: boolean = false): void {
430
+ if (this.isStarted) {
431
+ console.warn('[Router] Already started');
432
+ return;
433
+ }
434
+
435
+ const initialPath = this.mode === 'history'
436
+ ? (window.location.pathname + window.location.search)
437
+ : (window.location.hash.substring(1) || this.defaultRoute);
438
+
439
+ // Set initial active route (without mounting view)
440
+ this.setActiveRouteForPath(initialPath);
441
+
442
+ // Attach listeners
443
+ if (this.mode === 'history') {
444
+ window.addEventListener('popstate', this._handlePopState);
445
+ } else {
446
+ window.addEventListener('hashchange', this._handlePopState);
447
+ }
448
+ document.addEventListener('click', this._handleAutoNavigation);
449
+
450
+ // Handle initial route
451
+ if (!skipInitial) {
452
+ this.handleRoute(initialPath);
453
+ }
454
+
455
+ this.isStarted = true;
456
+ }
457
+
458
+ /**
459
+ * Stop the router — remove event listeners.
460
+ */
461
+ stop(): void {
462
+ window.removeEventListener('popstate', this._handlePopState);
463
+ window.removeEventListener('hashchange', this._handlePopState);
464
+ document.removeEventListener('click', this._handleAutoNavigation);
465
+ this.isStarted = false;
466
+ }
467
+
468
+ /**
469
+ * Full destroy — cleanup everything.
470
+ */
471
+ destroy(): void {
472
+ this.stop();
473
+ this.routes = [];
474
+ this.routeConfigs = {};
475
+ this.routeCache.clear();
476
+ this._beforeEach = null;
477
+ this._afterEach = null;
478
+ this.currentRoute = null;
479
+ Router.activeRoute = null;
480
+ }
481
+
482
+ // ─── Helper ────────────────────────────────
483
+ getFullUrl(): string {
484
+ if (typeof window === 'undefined') return '';
485
+ return window.location.href;
486
+ }
487
+ // ─── Internal: Route Handling ────────────────────────────────
488
+
489
+ /**
490
+ * Core route handler — match route, run guards, mount view.
491
+ */
492
+ private async handleRoute(path: string): Promise<void> {
493
+ if (this.isNavigating) return;
494
+ this.isNavigating = true;
495
+
496
+ try {
497
+ const normalizedPath = this.normalizePath(path);
498
+ const query = this.parseQuery(window.location.search);
499
+ const fragment = window.location.hash.substring(1);
500
+
501
+ const match = this.matchRoute(normalizedPath);
502
+ if (!match) {
503
+ console.warn(`[Router] No route matched: ${path}`);
504
+ return;
505
+ }
506
+
507
+ const { route, params } = match;
508
+ const from = this.currentRoute;
509
+
510
+ // Create ActiveRoute
511
+ const activeRoute = new ActiveRoute(route, normalizedPath, params, query, fragment);
512
+
513
+ // Before guard
514
+ if (this._beforeEach) {
515
+ const allow = await this._beforeEach(route, from, normalizedPath);
516
+ if (allow === false) return;
517
+ }
518
+
519
+ // Update global state
520
+ Router.activeRoute = activeRoute;
521
+ this.currentRoute = activeRoute;
522
+
523
+ // Mount view via ViewManager
524
+ const viewComponent = route.component || route.view;
525
+ if (viewComponent) {
526
+ if (this.viewManager) {
527
+ await this.viewManager.mountView(viewComponent, params, activeRoute);
528
+ } else if (this.App?.View) {
529
+ // Fallback: App.View is the ViewManager (registered on app)
530
+ await this.App.View.mountView(viewComponent, params, activeRoute);
531
+ }
532
+ }
533
+
534
+ // After hook
535
+ if (this._afterEach) {
536
+ this._afterEach({ ...route, path: normalizedPath }, from);
537
+ }
538
+ } catch (error) {
539
+ console.error('[Router] Navigation error:', error);
540
+ } finally {
541
+ this.isNavigating = false;
542
+ }
543
+ }
544
+
545
+ /**
546
+ * Handle browser back/forward.
547
+ */
548
+ private handlePopState(): void {
549
+ const path = this.mode === 'history'
550
+ ? window.location.pathname + window.location.search
551
+ : window.location.hash.slice(1) || this.defaultRoute;
552
+ this.handleRoute(path);
553
+ }
554
+
555
+ /**
556
+ * Auto-navigation: intercept clicks on <a>, [data-nav-link], [data-navigate].
557
+ */
558
+ private handleAutoNavigation(e: MouseEvent): void {
559
+ const target = e.target as HTMLElement;
560
+
561
+ // 1. [data-nav-link] — highest priority
562
+ const navLinkEl = target.closest('[data-nav-link]') as HTMLElement;
563
+ if (navLinkEl) {
564
+ if (navLinkEl.hasAttribute('data-nav-disabled')) return;
565
+ const navPath = navLinkEl.getAttribute('data-nav-link');
566
+ if (navPath && navPath.trim() !== '' && navPath !== this.currentUri) {
567
+ e.preventDefault();
568
+ this.navigate(navPath);
569
+ return;
570
+ }
571
+ }
572
+
573
+ // 2. [data-navigate]
574
+ const navigateEl = target.closest('[data-navigate]') as HTMLElement;
575
+ if (navigateEl) {
576
+ if (navigateEl.hasAttribute('data-nav-disabled')) return;
577
+ const navPath = navigateEl.getAttribute('data-navigate');
578
+ if (navPath && navPath.trim() !== '' && navPath !== this.currentUri) {
579
+ e.preventDefault();
580
+ this.navigate(navPath);
581
+ return;
582
+ }
583
+ }
584
+
585
+ // 3. Standard <a> tags
586
+ const link = target.closest('a[href]') as HTMLAnchorElement;
587
+ if (!link) return;
588
+
589
+ // Skip: target="_blank", disabled, special protocols
590
+ if (link.target === '_blank') return;
591
+ if (link.dataset.nav === 'disabled' || link.dataset.nav === 'false') return;
592
+ const href = link.href;
593
+ if (href.startsWith('mailto:') || href.startsWith('tel:') || href.startsWith('javascript:')) return;
594
+
595
+ // Check if same-origin
596
+ try {
597
+ const linkUrl = new URL(href);
598
+ const currentUrl = new URL(window.location.href);
599
+ if (linkUrl.host !== currentUrl.host) return; // External
600
+
601
+ const path = linkUrl.pathname + linkUrl.search;
602
+ if (path === this.currentUri) return; // Same page
603
+ e.preventDefault();
604
+ this.navigate(path);
605
+ } catch {
606
+ // Relative URL
607
+ if (href && !href.startsWith('http') && !href.startsWith('//')) {
608
+ if (href === this.currentUri) return;
609
+ e.preventDefault();
610
+ this.navigate(href);
611
+ }
612
+ }
613
+ }
614
+
615
+ // ─── Internal: Pattern Matching ─────────────────────────────
616
+
617
+ private matchRoute(path: string): RouteMatch | null {
618
+ const normalizedPath = this.normalizePath(path);
619
+
620
+ if (this.routeCache.has(normalizedPath)) {
621
+ return this.routeCache.get(normalizedPath)!;
622
+ }
623
+
624
+ for (const routeDef of this.routes) {
625
+ const params = this.extractParams(routeDef.path, normalizedPath);
626
+ if (params !== null) {
627
+ const route: Route = {
628
+ path: routeDef.path,
629
+ component: routeDef.component,
630
+ view: routeDef.component,
631
+ params,
632
+ ...routeDef.options,
633
+ };
634
+ const match: RouteMatch = { route, params };
635
+ this.routeCache.set(normalizedPath, match);
636
+ return match;
637
+ }
638
+ }
639
+
640
+ this.routeCache.set(normalizedPath, null);
641
+ return null;
642
+ }
643
+
644
+ /**
645
+ * Extract params from a Laravel-style route pattern.
646
+ * Supports: {param}, {param?}, * (wildcard)
647
+ */
648
+ private extractParams(pattern: string, path: string): Record<string, string> | null {
649
+ if (pattern.includes('*') || pattern === '{any}') {
650
+ return { wildcard: path };
651
+ }
652
+
653
+ const normalizedPattern = this.normalizePath(pattern);
654
+ const normalizedPath = this.normalizePath(path);
655
+
656
+ const segments = normalizedPattern.split('/');
657
+ let regexParts: string[] = [];
658
+ const paramNames: string[] = [];
659
+
660
+ for (const seg of segments) {
661
+ if (!seg) continue;
662
+
663
+ // Optional param: {name?}
664
+ const optMatch = seg.match(/^\{([a-zA-Z0-9_]+)\?\}$/);
665
+ if (optMatch) {
666
+ paramNames.push(optMatch[1]);
667
+ regexParts.push('(?:\\/([^\\/]+))?');
668
+ continue;
669
+ }
670
+
671
+ // Required param: {name}
672
+ const reqMatch = seg.match(/^\{([a-zA-Z0-9_]+)\}$/);
673
+ if (reqMatch) {
674
+ paramNames.push(reqMatch[1]);
675
+ regexParts.push('\\/([^\\/]+)');
676
+ continue;
677
+ }
678
+
679
+ // Static segment
680
+ const escaped = seg.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
681
+ regexParts.push('\\/' + escaped);
682
+ }
683
+
684
+ const regex = new RegExp(`^${regexParts.join('')}$`);
685
+ const match = normalizedPath.match(regex);
686
+ if (!match) return null;
687
+
688
+ const params: Record<string, string> = {};
689
+ paramNames.forEach((name, i) => {
690
+ const value = match[i + 1];
691
+ if (value !== undefined && value !== '') {
692
+ params[name] = value;
693
+ }
694
+ });
695
+
696
+ return params;
697
+ }
698
+
699
+ // ─── Internal: Helpers ──────────────────────────────────────
700
+
701
+ private normalizePath(path: string): string {
702
+ let normalized = path.startsWith('/') ? path : `/${path}`;
703
+ if (normalized.length > 1 && normalized.endsWith('/')) {
704
+ normalized = normalized.slice(0, -1);
705
+ }
706
+ return normalized || '/';
707
+ }
708
+
709
+ private parseQuery(search: string): Record<string, string> {
710
+ const query: Record<string, string> = {};
711
+ const params = new URLSearchParams(search);
712
+ params.forEach((value, key) => { query[key] = value; });
713
+ return query;
714
+ }
715
+
716
+ private setActiveRouteForPath(path: string): void {
717
+ const normalizedPath = this.normalizePath(path);
718
+ const match = this.matchRoute(normalizedPath);
719
+ if (match) {
720
+ const query = this.parseQuery(window.location.search);
721
+ const fragment = window.location.hash.substring(1);
722
+ const activeRoute = new ActiveRoute(match.route, normalizedPath, match.params, query, fragment);
723
+ Router.activeRoute = activeRoute;
724
+ this.currentRoute = activeRoute;
725
+ }
726
+ }
727
+
728
+ // ─── Static Helpers ─────────────────────────────────────────
729
+
730
+ static getActiveRoute(): ActiveRoute | null {
731
+ return Router.activeRoute;
732
+ }
733
+
734
+ static getCurrentPath(): string {
735
+ return typeof window !== 'undefined' ? window.location.pathname : '/';
736
+ }
737
+
738
+ static getCurrentQuery(): Record<string, string> {
739
+ if (typeof window === 'undefined') return {};
740
+ const query: Record<string, string> = {};
741
+ const params = new URLSearchParams(window.location.search);
742
+ params.forEach((value, key) => { query[key] = value; });
743
+ return query;
744
+ }
745
+
746
+ static getCurrentHash(): string {
747
+ return typeof window !== 'undefined' ? window.location.hash.substring(1) : '';
748
+ }
749
+ }
750
+
751
+ // ─── Composables ────────────────────────────────────────────────
752
+
753
+ export function useRoute(): ActiveRoute | null {
754
+ return Router.getActiveRoute();
755
+ }
756
+
757
+ export function useParams(): Record<string, string> {
758
+ return useRoute()?.$params || {};
759
+ }
760
+
761
+ export function useQuery(): Record<string, string> {
762
+ return Router.getCurrentQuery();
763
+ }
764
+
765
+ export default Router;