@bquery/bquery 1.6.0 → 1.8.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 (402) hide show
  1. package/README.md +192 -18
  2. package/dist/a11y/announce.d.ts +43 -0
  3. package/dist/a11y/announce.d.ts.map +1 -0
  4. package/dist/a11y/audit.d.ts +42 -0
  5. package/dist/a11y/audit.d.ts.map +1 -0
  6. package/dist/a11y/index.d.ts +53 -0
  7. package/dist/a11y/index.d.ts.map +1 -0
  8. package/dist/a11y/media-preferences.d.ts +77 -0
  9. package/dist/a11y/media-preferences.d.ts.map +1 -0
  10. package/dist/a11y/roving-tab-index.d.ts +38 -0
  11. package/dist/a11y/roving-tab-index.d.ts.map +1 -0
  12. package/dist/a11y/skip-link.d.ts +37 -0
  13. package/dist/a11y/skip-link.d.ts.map +1 -0
  14. package/dist/a11y/trap-focus.d.ts +49 -0
  15. package/dist/a11y/trap-focus.d.ts.map +1 -0
  16. package/dist/a11y/types.d.ts +152 -0
  17. package/dist/a11y/types.d.ts.map +1 -0
  18. package/dist/a11y-DVBCy09c.js +421 -0
  19. package/dist/a11y-DVBCy09c.js.map +1 -0
  20. package/dist/a11y.es.mjs +14 -0
  21. package/dist/component/component.d.ts.map +1 -1
  22. package/dist/component/html.d.ts.map +1 -1
  23. package/dist/component/index.d.ts +2 -1
  24. package/dist/component/index.d.ts.map +1 -1
  25. package/dist/component/library.d.ts.map +1 -1
  26. package/dist/component/scope.d.ts +138 -0
  27. package/dist/component/scope.d.ts.map +1 -0
  28. package/dist/component/types.d.ts +53 -1
  29. package/dist/component/types.d.ts.map +1 -1
  30. package/dist/component-L3-JfOFz.js +684 -0
  31. package/dist/component-L3-JfOFz.js.map +1 -0
  32. package/dist/component.es.mjs +9 -6
  33. package/dist/{config-DRmZZno3.js → config-DhT9auRm.js} +4 -4
  34. package/dist/{config-DRmZZno3.js.map → config-DhT9auRm.js.map} +1 -1
  35. package/dist/constraints-D5RHQLmP.js +100 -0
  36. package/dist/constraints-D5RHQLmP.js.map +1 -0
  37. package/dist/core/collection.d.ts +134 -0
  38. package/dist/core/collection.d.ts.map +1 -1
  39. package/dist/core/element.d.ts +120 -0
  40. package/dist/core/element.d.ts.map +1 -1
  41. package/dist/core/env.d.ts +18 -0
  42. package/dist/core/env.d.ts.map +1 -0
  43. package/dist/core/index.d.ts +1 -0
  44. package/dist/core/index.d.ts.map +1 -1
  45. package/dist/core/shared.d.ts +14 -0
  46. package/dist/core/shared.d.ts.map +1 -1
  47. package/dist/core/utils/index.d.ts +52 -41
  48. package/dist/core/utils/index.d.ts.map +1 -1
  49. package/dist/core-DdtZHzsS.js +168 -0
  50. package/dist/core-DdtZHzsS.js.map +1 -0
  51. package/dist/{core-CCEabVHl.js → core-EMYSLzaT.js} +293 -194
  52. package/dist/core-EMYSLzaT.js.map +1 -0
  53. package/dist/core.es.mjs +48 -46
  54. package/dist/custom-directives-Dr4C5lVV.js +9 -0
  55. package/dist/custom-directives-Dr4C5lVV.js.map +1 -0
  56. package/dist/devtools/devtools.d.ts +212 -0
  57. package/dist/devtools/devtools.d.ts.map +1 -0
  58. package/dist/devtools/index.d.ts +20 -0
  59. package/dist/devtools/index.d.ts.map +1 -0
  60. package/dist/devtools/types.d.ts +69 -0
  61. package/dist/devtools/types.d.ts.map +1 -0
  62. package/dist/devtools-BhB2iDPT.js +122 -0
  63. package/dist/devtools-BhB2iDPT.js.map +1 -0
  64. package/dist/devtools.es.mjs +19 -0
  65. package/dist/dnd/draggable.d.ts +51 -0
  66. package/dist/dnd/draggable.d.ts.map +1 -0
  67. package/dist/dnd/droppable.d.ts +38 -0
  68. package/dist/dnd/droppable.d.ts.map +1 -0
  69. package/dist/dnd/index.d.ts +47 -0
  70. package/dist/dnd/index.d.ts.map +1 -0
  71. package/dist/dnd/sortable.d.ts +43 -0
  72. package/dist/dnd/sortable.d.ts.map +1 -0
  73. package/dist/dnd/types.d.ts +250 -0
  74. package/dist/dnd/types.d.ts.map +1 -0
  75. package/dist/dnd-NwZBYh4l.js +244 -0
  76. package/dist/dnd-NwZBYh4l.js.map +1 -0
  77. package/dist/dnd.es.mjs +6 -0
  78. package/dist/env-CTdvLaH2.js +19 -0
  79. package/dist/env-CTdvLaH2.js.map +1 -0
  80. package/dist/forms/create-form.d.ts +49 -0
  81. package/dist/forms/create-form.d.ts.map +1 -0
  82. package/dist/forms/index.d.ts +40 -0
  83. package/dist/forms/index.d.ts.map +1 -0
  84. package/dist/forms/types.d.ts +185 -0
  85. package/dist/forms/types.d.ts.map +1 -0
  86. package/dist/forms/use-field.d.ts +34 -0
  87. package/dist/forms/use-field.d.ts.map +1 -0
  88. package/dist/forms/validators.d.ts +204 -0
  89. package/dist/forms/validators.d.ts.map +1 -0
  90. package/dist/forms-UcRHsYxC.js +227 -0
  91. package/dist/forms-UcRHsYxC.js.map +1 -0
  92. package/dist/forms.es.mjs +16 -0
  93. package/dist/full.d.ts +30 -11
  94. package/dist/full.d.ts.map +1 -1
  95. package/dist/full.es.mjs +209 -93
  96. package/dist/full.iife.js +47 -31
  97. package/dist/full.iife.js.map +1 -1
  98. package/dist/full.umd.js +47 -31
  99. package/dist/full.umd.js.map +1 -1
  100. package/dist/function-Cybd57JV.js +33 -0
  101. package/dist/function-Cybd57JV.js.map +1 -0
  102. package/dist/i18n/formatting.d.ts +40 -0
  103. package/dist/i18n/formatting.d.ts.map +1 -0
  104. package/dist/i18n/i18n.d.ts +48 -0
  105. package/dist/i18n/i18n.d.ts.map +1 -0
  106. package/dist/i18n/index.d.ts +57 -0
  107. package/dist/i18n/index.d.ts.map +1 -0
  108. package/dist/i18n/translate.d.ts +83 -0
  109. package/dist/i18n/translate.d.ts.map +1 -0
  110. package/dist/i18n/types.d.ts +156 -0
  111. package/dist/i18n/types.d.ts.map +1 -0
  112. package/dist/i18n-kuF6Ekj6.js +89 -0
  113. package/dist/i18n-kuF6Ekj6.js.map +1 -0
  114. package/dist/i18n.es.mjs +6 -0
  115. package/dist/index.d.ts +11 -0
  116. package/dist/index.d.ts.map +1 -1
  117. package/dist/index.es.mjs +257 -143
  118. package/dist/media/battery.d.ts +35 -0
  119. package/dist/media/battery.d.ts.map +1 -0
  120. package/dist/media/breakpoints.d.ts +51 -0
  121. package/dist/media/breakpoints.d.ts.map +1 -0
  122. package/dist/media/clipboard.d.ts +30 -0
  123. package/dist/media/clipboard.d.ts.map +1 -0
  124. package/dist/media/device-sensors.d.ts +54 -0
  125. package/dist/media/device-sensors.d.ts.map +1 -0
  126. package/dist/media/geolocation.d.ts +38 -0
  127. package/dist/media/geolocation.d.ts.map +1 -0
  128. package/dist/media/index.d.ts +42 -0
  129. package/dist/media/index.d.ts.map +1 -0
  130. package/dist/media/media-query.d.ts +36 -0
  131. package/dist/media/media-query.d.ts.map +1 -0
  132. package/dist/media/network.d.ts +35 -0
  133. package/dist/media/network.d.ts.map +1 -0
  134. package/dist/media/types.d.ts +173 -0
  135. package/dist/media/types.d.ts.map +1 -0
  136. package/dist/media/viewport.d.ts +32 -0
  137. package/dist/media/viewport.d.ts.map +1 -0
  138. package/dist/media-i-fB5WxI.js +340 -0
  139. package/dist/media-i-fB5WxI.js.map +1 -0
  140. package/dist/media.es.mjs +12 -0
  141. package/dist/motion/index.d.ts +7 -3
  142. package/dist/motion/index.d.ts.map +1 -1
  143. package/dist/motion/morph.d.ts +27 -0
  144. package/dist/motion/morph.d.ts.map +1 -0
  145. package/dist/motion/parallax.d.ts +30 -0
  146. package/dist/motion/parallax.d.ts.map +1 -0
  147. package/dist/motion/reduced-motion.d.ts +36 -3
  148. package/dist/motion/reduced-motion.d.ts.map +1 -1
  149. package/dist/motion/types.d.ts +58 -0
  150. package/dist/motion/types.d.ts.map +1 -1
  151. package/dist/motion/typewriter.d.ts +31 -0
  152. package/dist/motion/typewriter.d.ts.map +1 -0
  153. package/dist/motion-BJsAuULb.js +530 -0
  154. package/dist/motion-BJsAuULb.js.map +1 -0
  155. package/dist/motion.es.mjs +27 -23
  156. package/dist/{view-C70lA3vf.js → mount-B4Y8bk8Z.js} +166 -160
  157. package/dist/mount-B4Y8bk8Z.js.map +1 -0
  158. package/dist/{object-qGpWr6-J.js → object-BCk-1c8T.js} +5 -4
  159. package/dist/{object-qGpWr6-J.js.map → object-BCk-1c8T.js.map} +1 -1
  160. package/dist/{platform-Dr9b6fsq.js → platform-Dw2gE3zI.js} +21 -22
  161. package/dist/{platform-Dr9b6fsq.js.map → platform-Dw2gE3zI.js.map} +1 -1
  162. package/dist/platform.es.mjs +2 -2
  163. package/dist/plugin/index.d.ts +22 -0
  164. package/dist/plugin/index.d.ts.map +1 -0
  165. package/dist/plugin/registry.d.ts +108 -0
  166. package/dist/plugin/registry.d.ts.map +1 -0
  167. package/dist/plugin/types.d.ts +110 -0
  168. package/dist/plugin/types.d.ts.map +1 -0
  169. package/dist/plugin-C2WuC8SF.js +66 -0
  170. package/dist/plugin-C2WuC8SF.js.map +1 -0
  171. package/dist/plugin.es.mjs +9 -0
  172. package/dist/reactive/async-data.d.ts +28 -3
  173. package/dist/reactive/async-data.d.ts.map +1 -1
  174. package/dist/reactive/computed.d.ts +10 -0
  175. package/dist/reactive/computed.d.ts.map +1 -1
  176. package/dist/reactive/effect.d.ts +3 -0
  177. package/dist/reactive/effect.d.ts.map +1 -1
  178. package/dist/reactive/http.d.ts +194 -0
  179. package/dist/reactive/http.d.ts.map +1 -0
  180. package/dist/reactive/index.d.ts +2 -2
  181. package/dist/reactive/index.d.ts.map +1 -1
  182. package/dist/reactive/pagination.d.ts +126 -0
  183. package/dist/reactive/pagination.d.ts.map +1 -0
  184. package/dist/reactive/polling.d.ts +55 -0
  185. package/dist/reactive/polling.d.ts.map +1 -0
  186. package/dist/reactive/readonly.d.ts +20 -1
  187. package/dist/reactive/readonly.d.ts.map +1 -1
  188. package/dist/reactive/rest.d.ts +293 -0
  189. package/dist/reactive/rest.d.ts.map +1 -0
  190. package/dist/reactive/scope.d.ts +140 -0
  191. package/dist/reactive/scope.d.ts.map +1 -0
  192. package/dist/reactive/signal.d.ts +16 -2
  193. package/dist/reactive/signal.d.ts.map +1 -1
  194. package/dist/reactive/to-value.d.ts +57 -0
  195. package/dist/reactive/to-value.d.ts.map +1 -0
  196. package/dist/reactive/websocket.d.ts +285 -0
  197. package/dist/reactive/websocket.d.ts.map +1 -0
  198. package/dist/reactive-DwkhUJfP.js +1148 -0
  199. package/dist/reactive-DwkhUJfP.js.map +1 -0
  200. package/dist/reactive.es.mjs +38 -20
  201. package/dist/registry-B08iilIh.js +26 -0
  202. package/dist/registry-B08iilIh.js.map +1 -0
  203. package/dist/router/bq-link.d.ts +112 -0
  204. package/dist/router/bq-link.d.ts.map +1 -0
  205. package/dist/router/constraints.d.ts +9 -0
  206. package/dist/router/constraints.d.ts.map +1 -0
  207. package/dist/router/index.d.ts +15 -7
  208. package/dist/router/index.d.ts.map +1 -1
  209. package/dist/router/match.d.ts +0 -1
  210. package/dist/router/match.d.ts.map +1 -1
  211. package/dist/router/path-pattern.d.ts +14 -0
  212. package/dist/router/path-pattern.d.ts.map +1 -0
  213. package/dist/router/query.d.ts.map +1 -1
  214. package/dist/router/router.d.ts +3 -1
  215. package/dist/router/router.d.ts.map +1 -1
  216. package/dist/router/state.d.ts +25 -2
  217. package/dist/router/state.d.ts.map +1 -1
  218. package/dist/router/types.d.ts +48 -4
  219. package/dist/router/types.d.ts.map +1 -1
  220. package/dist/router/use-route.d.ts +50 -0
  221. package/dist/router/use-route.d.ts.map +1 -0
  222. package/dist/router/utils.d.ts +3 -0
  223. package/dist/router/utils.d.ts.map +1 -1
  224. package/dist/router-CQikC9Ed.js +492 -0
  225. package/dist/router-CQikC9Ed.js.map +1 -0
  226. package/dist/router.es.mjs +14 -10
  227. package/dist/{sanitize-Bs2dkMby.js → sanitize-B1V4JswB.js} +2 -1
  228. package/dist/{sanitize-Bs2dkMby.js.map → sanitize-B1V4JswB.js.map} +1 -1
  229. package/dist/security/index.d.ts +2 -2
  230. package/dist/security/index.d.ts.map +1 -1
  231. package/dist/security.es.mjs +1 -1
  232. package/dist/ssr/hydrate.d.ts +65 -0
  233. package/dist/ssr/hydrate.d.ts.map +1 -0
  234. package/dist/ssr/index.d.ts +59 -0
  235. package/dist/ssr/index.d.ts.map +1 -0
  236. package/dist/ssr/render.d.ts +62 -0
  237. package/dist/ssr/render.d.ts.map +1 -0
  238. package/dist/ssr/serialize.d.ts +118 -0
  239. package/dist/ssr/serialize.d.ts.map +1 -0
  240. package/dist/ssr/types.d.ts +70 -0
  241. package/dist/ssr/types.d.ts.map +1 -0
  242. package/dist/ssr-_dAcGdzu.js +248 -0
  243. package/dist/ssr-_dAcGdzu.js.map +1 -0
  244. package/dist/ssr.es.mjs +9 -0
  245. package/dist/store/create-store.d.ts.map +1 -1
  246. package/dist/store/index.d.ts +1 -1
  247. package/dist/store/index.d.ts.map +1 -1
  248. package/dist/store/persisted.d.ts +38 -4
  249. package/dist/store/persisted.d.ts.map +1 -1
  250. package/dist/store/types.d.ts +138 -1
  251. package/dist/store/types.d.ts.map +1 -1
  252. package/dist/store/utils.d.ts +2 -2
  253. package/dist/store/utils.d.ts.map +1 -1
  254. package/dist/store-Cb3gPRve.js +338 -0
  255. package/dist/store-Cb3gPRve.js.map +1 -0
  256. package/dist/store.es.mjs +11 -10
  257. package/dist/storybook/index.d.ts.map +1 -1
  258. package/dist/storybook.es.mjs +1 -1
  259. package/dist/storybook.es.mjs.map +1 -1
  260. package/dist/testing/index.d.ts +23 -0
  261. package/dist/testing/index.d.ts.map +1 -0
  262. package/dist/testing/testing.d.ts +156 -0
  263. package/dist/testing/testing.d.ts.map +1 -0
  264. package/dist/testing/types.d.ts +134 -0
  265. package/dist/testing/types.d.ts.map +1 -0
  266. package/dist/testing-C5Sjfsna.js +224 -0
  267. package/dist/testing-C5Sjfsna.js.map +1 -0
  268. package/dist/testing.es.mjs +9 -0
  269. package/dist/type-guards-BMX2c0LP.js +44 -0
  270. package/dist/type-guards-BMX2c0LP.js.map +1 -0
  271. package/dist/untrack-D0fnO5k2.js +36 -0
  272. package/dist/untrack-D0fnO5k2.js.map +1 -0
  273. package/dist/view/custom-directives.d.ts +20 -0
  274. package/dist/view/custom-directives.d.ts.map +1 -0
  275. package/dist/view/evaluate.d.ts.map +1 -1
  276. package/dist/view/process.d.ts.map +1 -1
  277. package/dist/view.es.mjs +9 -9
  278. package/package.json +47 -11
  279. package/src/a11y/announce.ts +131 -0
  280. package/src/a11y/audit.ts +314 -0
  281. package/src/a11y/index.ts +68 -0
  282. package/src/a11y/media-preferences.ts +255 -0
  283. package/src/a11y/roving-tab-index.ts +164 -0
  284. package/src/a11y/skip-link.ts +255 -0
  285. package/src/a11y/trap-focus.ts +184 -0
  286. package/src/a11y/types.ts +183 -0
  287. package/src/component/component.ts +599 -524
  288. package/src/component/html.ts +153 -153
  289. package/src/component/index.ts +52 -50
  290. package/src/component/library.ts +540 -518
  291. package/src/component/scope.ts +212 -0
  292. package/src/component/types.ts +310 -256
  293. package/src/core/collection.ts +249 -1
  294. package/src/core/element.ts +252 -11
  295. package/src/core/env.ts +60 -0
  296. package/src/core/index.ts +1 -0
  297. package/src/core/shared.ts +64 -0
  298. package/src/core/utils/index.ts +66 -1
  299. package/src/devtools/devtools.ts +410 -0
  300. package/src/devtools/index.ts +48 -0
  301. package/src/devtools/types.ts +104 -0
  302. package/src/dnd/draggable.ts +296 -0
  303. package/src/dnd/droppable.ts +228 -0
  304. package/src/dnd/index.ts +62 -0
  305. package/src/dnd/sortable.ts +307 -0
  306. package/src/dnd/types.ts +293 -0
  307. package/src/forms/create-form.ts +320 -0
  308. package/src/forms/index.ts +70 -0
  309. package/src/forms/types.ts +203 -0
  310. package/src/forms/use-field.ts +231 -0
  311. package/src/forms/validators.ts +294 -0
  312. package/src/full.ts +554 -229
  313. package/src/i18n/formatting.ts +67 -0
  314. package/src/i18n/i18n.ts +200 -0
  315. package/src/i18n/index.ts +67 -0
  316. package/src/i18n/translate.ts +182 -0
  317. package/src/i18n/types.ts +171 -0
  318. package/src/index.ts +72 -0
  319. package/src/media/battery.ts +116 -0
  320. package/src/media/breakpoints.ts +129 -0
  321. package/src/media/clipboard.ts +80 -0
  322. package/src/media/device-sensors.ts +158 -0
  323. package/src/media/geolocation.ts +119 -0
  324. package/src/media/index.ts +76 -0
  325. package/src/media/media-query.ts +92 -0
  326. package/src/media/network.ts +115 -0
  327. package/src/media/types.ts +177 -0
  328. package/src/media/viewport.ts +84 -0
  329. package/src/motion/index.ts +11 -2
  330. package/src/motion/morph.ts +151 -0
  331. package/src/motion/parallax.ts +120 -0
  332. package/src/motion/reduced-motion.ts +52 -3
  333. package/src/motion/types.ts +63 -0
  334. package/src/motion/typewriter.ts +164 -0
  335. package/src/plugin/index.ts +37 -0
  336. package/src/plugin/registry.ts +284 -0
  337. package/src/plugin/types.ts +137 -0
  338. package/src/reactive/async-data.ts +250 -29
  339. package/src/reactive/computed.ts +53 -1
  340. package/src/reactive/effect.ts +29 -6
  341. package/src/reactive/http.ts +790 -0
  342. package/src/reactive/index.ts +60 -0
  343. package/src/reactive/pagination.ts +317 -0
  344. package/src/reactive/polling.ts +179 -0
  345. package/src/reactive/readonly.ts +52 -8
  346. package/src/reactive/rest.ts +859 -0
  347. package/src/reactive/scope.ts +276 -0
  348. package/src/reactive/signal.ts +61 -1
  349. package/src/reactive/to-value.ts +71 -0
  350. package/src/reactive/websocket.ts +849 -0
  351. package/src/router/bq-link.ts +279 -0
  352. package/src/router/constraints.ts +204 -0
  353. package/src/router/index.ts +15 -7
  354. package/src/router/match.ts +255 -49
  355. package/src/router/path-pattern.ts +52 -0
  356. package/src/router/query.ts +3 -0
  357. package/src/router/router.ts +258 -48
  358. package/src/router/state.ts +51 -3
  359. package/src/router/types.ts +50 -4
  360. package/src/router/use-route.ts +68 -0
  361. package/src/router/utils.ts +44 -3
  362. package/src/security/index.ts +12 -17
  363. package/src/security/sanitize.ts +70 -70
  364. package/src/security/trusted-html.ts +71 -71
  365. package/src/ssr/hydrate.ts +84 -0
  366. package/src/ssr/index.ts +70 -0
  367. package/src/ssr/render.ts +508 -0
  368. package/src/ssr/serialize.ts +296 -0
  369. package/src/ssr/types.ts +81 -0
  370. package/src/store/create-store.ts +146 -8
  371. package/src/store/define-store.ts +49 -49
  372. package/src/store/index.ts +5 -0
  373. package/src/store/mapping.ts +74 -74
  374. package/src/store/persisted.ts +245 -62
  375. package/src/store/types.ts +247 -92
  376. package/src/store/utils.ts +4 -10
  377. package/src/store/watch.ts +53 -53
  378. package/src/storybook/index.ts +480 -479
  379. package/src/testing/index.ts +42 -0
  380. package/src/testing/testing.ts +593 -0
  381. package/src/testing/types.ts +170 -0
  382. package/src/view/custom-directives.ts +28 -0
  383. package/src/view/evaluate.ts +2 -0
  384. package/src/view/process.ts +19 -3
  385. package/dist/component-BEQgt5hl.js +0 -600
  386. package/dist/component-BEQgt5hl.js.map +0 -1
  387. package/dist/core-BGQJVw0-.js +0 -35
  388. package/dist/core-BGQJVw0-.js.map +0 -1
  389. package/dist/core-CCEabVHl.js.map +0 -1
  390. package/dist/effect-AFRW_Plg.js +0 -84
  391. package/dist/effect-AFRW_Plg.js.map +0 -1
  392. package/dist/motion-D9TcHxOF.js +0 -415
  393. package/dist/motion-D9TcHxOF.js.map +0 -1
  394. package/dist/reactive-DSkct0dO.js +0 -254
  395. package/dist/reactive-DSkct0dO.js.map +0 -1
  396. package/dist/router-CbDhl8rS.js +0 -188
  397. package/dist/router-CbDhl8rS.js.map +0 -1
  398. package/dist/store-BwDvI45q.js +0 -263
  399. package/dist/store-BwDvI45q.js.map +0 -1
  400. package/dist/untrack-B0rVscTc.js +0 -7
  401. package/dist/untrack-B0rVscTc.js.map +0 -1
  402. package/dist/view-C70lA3vf.js.map +0 -1
@@ -4,60 +4,275 @@
4
4
  */
5
5
 
6
6
  import { parseQuery } from './query';
7
+ import { getNormalizedRouteConstraint, getRouteConstraintRegex } from './constraints';
8
+ import { isParamChar, isParamStart, readConstraint } from './path-pattern';
7
9
  import type { Route, RouteDefinition } from './types';
8
10
 
9
- // ============================================================================
10
- // Route Matching
11
- // ============================================================================
11
+ const readConstraintOrThrow = (
12
+ path: string,
13
+ startIndex: number
14
+ ): { constraint: string; endIndex: number } => {
15
+ const parsedConstraint = readConstraint(path, startIndex);
16
+ if (!parsedConstraint) {
17
+ throw new Error(
18
+ `bQuery router: Invalid route param constraint syntax in path "${path}" at index ${startIndex}.`
19
+ );
20
+ }
21
+ return parsedConstraint;
22
+ };
12
23
 
13
- /**
14
- * Converts a route path pattern to a RegExp for matching.
15
- * Uses placeholder approach to preserve :param and * patterns during escaping.
16
- * Returns positional capture groups for maximum compatibility.
17
- * @internal
18
- */
19
- const pathToRegex = (path: string): RegExp => {
20
- // Handle wildcard-only route
21
- if (path === '*') {
22
- return /^.*$/;
24
+ type RouteParamDescriptor = {
25
+ name: string;
26
+ constraint?: string;
27
+ nextIndex: number;
28
+ };
29
+
30
+ const validatedRoutePathCache = new Set<string>();
31
+
32
+ const readParamDescriptor = (path: string, index: number): RouteParamDescriptor | null => {
33
+ if (path[index] !== ':' || !isParamStart(path[index + 1])) {
34
+ return null;
23
35
  }
24
36
 
25
- // Unique placeholders using null chars (won't appear in normal paths)
26
- const PARAM_MARKER = '\u0000P\u0000';
27
- const WILDCARD_MARKER = '\u0000W\u0000';
37
+ let nameEnd = index + 2;
38
+ while (nameEnd < path.length && isParamChar(path[nameEnd])) {
39
+ nameEnd++;
40
+ }
41
+
42
+ let nextIndex = nameEnd;
43
+ let constraint: string | undefined;
28
44
 
29
- // Step 1: Extract :param patterns before escaping
30
- let pattern = path.replace(/:([a-zA-Z_][a-zA-Z0-9_]*)/g, () => {
31
- return PARAM_MARKER;
32
- });
45
+ if (path[nameEnd] === '(') {
46
+ const parsedConstraint = readConstraintOrThrow(path, nameEnd);
47
+ constraint = parsedConstraint.constraint;
48
+ nextIndex = parsedConstraint.endIndex;
49
+ }
50
+
51
+ return {
52
+ name: path.slice(index + 1, nameEnd),
53
+ constraint,
54
+ nextIndex,
55
+ };
56
+ };
57
+
58
+ const validateRoutePathPattern = (path: string): void => {
59
+ if (validatedRoutePathCache.has(path)) {
60
+ return;
61
+ }
33
62
 
34
- // Step 2: Extract * wildcards before escaping
35
- pattern = pattern.replace(/\*/g, WILDCARD_MARKER);
63
+ for (let i = 0; i < path.length; ) {
64
+ const char = path[i];
36
65
 
37
- // Step 3: Escape ALL regex metacharacters: \ ^ $ . * + ? ( ) [ ] { } |
38
- pattern = pattern.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&');
66
+ if (char === ':' && isParamStart(path[i + 1])) {
67
+ const param = readParamDescriptor(path, i);
68
+ if (param?.constraint) {
69
+ getNormalizedRouteConstraint(param.constraint);
70
+ }
71
+ i = param?.nextIndex ?? i + 1;
72
+ continue;
73
+ }
39
74
 
40
- // Step 4: Restore param capture groups (positional, not named)
41
- pattern = pattern.replace(/\u0000P\u0000/g, () => `([^/]+)`);
75
+ i++;
76
+ }
42
77
 
43
- // Step 5: Restore wildcards as .*
44
- pattern = pattern.replace(/\u0000W\u0000/g, '.*');
78
+ validatedRoutePathCache.add(path);
79
+ };
45
80
 
46
- return new RegExp(`^${pattern}$`);
81
+ const findSegmentBoundary = (value: string, startIndex: number): number => {
82
+ const slashIndex = value.indexOf('/', startIndex);
83
+ return slashIndex === -1 ? value.length : slashIndex;
47
84
  };
48
85
 
49
- /**
50
- * Extracts param names from a route path.
51
- * @internal
52
- */
53
- const extractParamNames = (path: string): string[] => {
54
- const matches = path.match(/:([a-zA-Z_][a-zA-Z0-9_]*)/g);
55
- return matches ? matches.map((m) => m.slice(1)) : [];
86
+ const readNextStaticChunk = (path: string, startIndex: number): string => {
87
+ let chunkEnd = startIndex;
88
+
89
+ while (chunkEnd < path.length) {
90
+ if (path[chunkEnd] === '*') {
91
+ break;
92
+ }
93
+
94
+ if (path[chunkEnd] === ':' && isParamStart(path[chunkEnd + 1])) {
95
+ break;
96
+ }
97
+
98
+ chunkEnd++;
99
+ }
100
+
101
+ return path.slice(startIndex, chunkEnd);
102
+ };
103
+
104
+ const findAnchoredCandidateEnds = (
105
+ actualPath: string,
106
+ startIndex: number,
107
+ limit: number,
108
+ nextStaticChunk: string
109
+ ): number[] => {
110
+ const candidates: number[] = [];
111
+ let searchIndex = startIndex;
112
+
113
+ while (searchIndex <= limit) {
114
+ const candidateEnd = actualPath.indexOf(nextStaticChunk, searchIndex);
115
+ if (candidateEnd === -1 || candidateEnd > limit) {
116
+ break;
117
+ }
118
+
119
+ candidates.push(candidateEnd);
120
+ searchIndex = candidateEnd + 1;
121
+ }
122
+
123
+ return candidates.reverse();
124
+ };
125
+
126
+ const matchPathPattern = (routePath: string, actualPath: string): Record<string, string> | null => {
127
+ // Memoization keeps wildcard/param backtracking linear for repeated subproblems
128
+ // within a single route/path match attempt.
129
+ const memo = new Map<string, Record<string, string> | null>();
130
+
131
+ const matchFrom = (routeIndex: number, pathIndex: number): Record<string, string> | null => {
132
+ const memoKey = `${routeIndex}:${pathIndex}`;
133
+ if (memo.has(memoKey)) {
134
+ return memo.get(memoKey) ?? null;
135
+ }
136
+
137
+ if (routeIndex === routePath.length) {
138
+ const result = pathIndex === actualPath.length ? {} : null;
139
+ memo.set(memoKey, result);
140
+ return result;
141
+ }
142
+
143
+ const routeChar = routePath[routeIndex];
144
+
145
+ if (routeChar === '*') {
146
+ if (routeIndex === routePath.length - 1) {
147
+ const result = {};
148
+ memo.set(memoKey, result);
149
+ return result;
150
+ }
151
+
152
+ const nextStaticChunk = readNextStaticChunk(routePath, routeIndex + 1);
153
+ const anchoredCandidateEnds =
154
+ nextStaticChunk.length > 0
155
+ ? findAnchoredCandidateEnds(actualPath, pathIndex, actualPath.length, nextStaticChunk)
156
+ : null;
157
+
158
+ const iterateCandidateEnds = anchoredCandidateEnds
159
+ ? (callback: (candidateEnd: number) => Record<string, string> | null) => {
160
+ for (const candidateEnd of anchoredCandidateEnds) {
161
+ const result = callback(candidateEnd);
162
+ if (result) {
163
+ return result;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ : (callback: (candidateEnd: number) => Record<string, string> | null) => {
169
+ for (let candidateEnd = actualPath.length; candidateEnd >= pathIndex; candidateEnd--) {
170
+ const result = callback(candidateEnd);
171
+ if (result) {
172
+ return result;
173
+ }
174
+ }
175
+ return null;
176
+ };
177
+
178
+ const wildcardMatch = iterateCandidateEnds((candidateEnd) => {
179
+ const suffixMatch = matchFrom(routeIndex + 1, candidateEnd);
180
+ if (suffixMatch) {
181
+ memo.set(memoKey, suffixMatch);
182
+ return suffixMatch;
183
+ }
184
+ return null;
185
+ });
186
+
187
+ if (wildcardMatch) {
188
+ return wildcardMatch;
189
+ }
190
+
191
+ memo.set(memoKey, null);
192
+ return null;
193
+ }
194
+
195
+ const param = readParamDescriptor(routePath, routeIndex);
196
+ if (param) {
197
+ const constraintRegex = param.constraint
198
+ ? getRouteConstraintRegex(param.constraint)
199
+ : undefined;
200
+ const candidateLimit = param.constraint
201
+ ? actualPath.length
202
+ : findSegmentBoundary(actualPath, pathIndex);
203
+ const nextStaticChunk = readNextStaticChunk(routePath, param.nextIndex);
204
+ const anchoredCandidateEnds =
205
+ nextStaticChunk.length > 0
206
+ ? findAnchoredCandidateEnds(actualPath, pathIndex, candidateLimit, nextStaticChunk)
207
+ : null;
208
+
209
+ const iterateCandidateEnds = anchoredCandidateEnds
210
+ ? (callback: (candidateEnd: number) => Record<string, string> | null) => {
211
+ for (const candidateEnd of anchoredCandidateEnds) {
212
+ if (candidateEnd <= pathIndex) {
213
+ continue;
214
+ }
215
+ const result = callback(candidateEnd);
216
+ if (result) {
217
+ return result;
218
+ }
219
+ }
220
+ return null;
221
+ }
222
+ : (callback: (candidateEnd: number) => Record<string, string> | null) => {
223
+ for (let candidateEnd = candidateLimit; candidateEnd > pathIndex; candidateEnd--) {
224
+ const result = callback(candidateEnd);
225
+ if (result) {
226
+ return result;
227
+ }
228
+ }
229
+ return null;
230
+ };
231
+
232
+ const paramMatch = iterateCandidateEnds((candidateEnd) => {
233
+ const candidateValue = actualPath.slice(pathIndex, candidateEnd);
234
+
235
+ if (constraintRegex) {
236
+ if (!constraintRegex.test(candidateValue)) {
237
+ return null;
238
+ }
239
+ }
240
+
241
+ const suffixMatch = matchFrom(param.nextIndex, candidateEnd);
242
+ if (suffixMatch) {
243
+ const result = {
244
+ [param.name]: candidateValue,
245
+ ...suffixMatch,
246
+ };
247
+ memo.set(memoKey, result);
248
+ return result;
249
+ }
250
+ return null;
251
+ });
252
+
253
+ if (paramMatch) {
254
+ return paramMatch;
255
+ }
256
+
257
+ memo.set(memoKey, null);
258
+ return null;
259
+ }
260
+
261
+ if (pathIndex >= actualPath.length || routeChar !== actualPath[pathIndex]) {
262
+ memo.set(memoKey, null);
263
+ return null;
264
+ }
265
+
266
+ const result = matchFrom(routeIndex + 1, pathIndex + 1);
267
+ memo.set(memoKey, result);
268
+ return result;
269
+ };
270
+
271
+ return matchFrom(0, 0);
56
272
  };
57
273
 
58
274
  /**
59
275
  * Matches a path against route definitions and extracts params.
60
- * Uses positional captures for maximum compatibility.
61
276
  * @internal
62
277
  */
63
278
  export const matchRoute = (
@@ -65,18 +280,9 @@ export const matchRoute = (
65
280
  routes: RouteDefinition[]
66
281
  ): { matched: RouteDefinition; params: Record<string, string> } | null => {
67
282
  for (const route of routes) {
68
- const regex = pathToRegex(route.path);
69
- const match = path.match(regex);
70
-
71
- if (match) {
72
- const paramNames = extractParamNames(route.path);
73
- const params: Record<string, string> = {};
74
-
75
- // Map positional captures to param names
76
- paramNames.forEach((name, index) => {
77
- params[name] = match[index + 1] || '';
78
- });
79
-
283
+ validateRoutePathPattern(route.path);
284
+ const params = matchPathPattern(route.path, path);
285
+ if (params) {
80
286
  return { matched: route, params };
81
287
  }
82
288
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Shared helpers for parsing route path params and constraints.
3
+ * @internal
4
+ */
5
+
6
+ /** Validates whether a character can start a route param name. @internal */
7
+ export const isParamStart = (char: string | undefined): boolean =>
8
+ char !== undefined &&
9
+ ((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z') || char === '_');
10
+
11
+ /** Validates whether a character can appear after the start of a route param name. @internal */
12
+ export const isParamChar = (char: string | undefined): boolean =>
13
+ isParamStart(char) || (char !== undefined && char >= '0' && char <= '9');
14
+
15
+ /** Reads a route param constraint while preserving escaped chars and nested groups. @internal */
16
+ export const readConstraint = (
17
+ path: string,
18
+ startIndex: number
19
+ ): { constraint: string; endIndex: number } | null => {
20
+ let depth = 1;
21
+ let constraint = '';
22
+ let i = startIndex + 1;
23
+ let inCharacterClass = false;
24
+
25
+ while (i < path.length) {
26
+ const char = path[i];
27
+
28
+ if (char === '\\' && i + 1 < path.length) {
29
+ constraint += char + path[i + 1];
30
+ i += 2;
31
+ continue;
32
+ }
33
+
34
+ if (char === '[' && !inCharacterClass) {
35
+ inCharacterClass = true;
36
+ } else if (char === ']' && inCharacterClass) {
37
+ inCharacterClass = false;
38
+ } else if (!inCharacterClass && char === '(') {
39
+ depth++;
40
+ } else if (!inCharacterClass && char === ')') {
41
+ depth--;
42
+ if (depth === 0) {
43
+ return { constraint, endIndex: i + 1 };
44
+ }
45
+ }
46
+
47
+ constraint += char;
48
+ i++;
49
+ }
50
+
51
+ return null;
52
+ };
@@ -3,6 +3,8 @@
3
3
  * @module bquery/router
4
4
  */
5
5
 
6
+ import { isPrototypePollutionKey } from '../core/utils/object';
7
+
6
8
  /**
7
9
  * Parses query string into an object.
8
10
  * Single values are stored as strings, duplicate keys become arrays.
@@ -18,6 +20,7 @@ export const parseQuery = (search: string): Record<string, string | string[]> =>
18
20
  const params = new URLSearchParams(search);
19
21
 
20
22
  params.forEach((value, key) => {
23
+ if (isPrototypePollutionKey(key)) return;
21
24
  const existing = query[key];
22
25
  if (existing === undefined) {
23
26
  // First occurrence: store as string