@adukiorg/anza 0.2.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 (349) hide show
  1. package/CHANGELOG.md +137 -0
  2. package/README.md +215 -0
  3. package/bin/anza.js +63 -0
  4. package/bin/create.js +150 -0
  5. package/importmap.json +72 -0
  6. package/package.json +100 -0
  7. package/src/core/animations/index.js +55 -0
  8. package/src/core/animations/play.js +111 -0
  9. package/src/core/animations/registry.js +54 -0
  10. package/src/core/animations/scroll.js +50 -0
  11. package/src/core/animations/tokens.js +58 -0
  12. package/src/core/animations/usage.md +301 -0
  13. package/src/core/animations/waapi.js +86 -0
  14. package/src/core/api/cache.js +120 -0
  15. package/src/core/api/caches/glob.js +24 -0
  16. package/src/core/api/caches/index.js +118 -0
  17. package/src/core/api/events/index.js +75 -0
  18. package/src/core/api/fetch.js +99 -0
  19. package/src/core/api/index.js +158 -0
  20. package/src/core/api/pipeline.js +98 -0
  21. package/src/core/api/plan.md +209 -0
  22. package/src/core/api/prefixes/index.js +66 -0
  23. package/src/core/api/retry.js +69 -0
  24. package/src/core/api/stream.js +127 -0
  25. package/src/core/api/upload.js +180 -0
  26. package/src/core/api/usage.md +206 -0
  27. package/src/core/events/bus.js +38 -0
  28. package/src/core/events/delegate.js +79 -0
  29. package/src/core/events/index.js +26 -0
  30. package/src/core/events/listen.js +50 -0
  31. package/src/core/events/missing.md +103 -0
  32. package/src/core/events/once.js +49 -0
  33. package/src/core/events/plan.md +177 -0
  34. package/src/core/events/types/index.js +34 -0
  35. package/src/core/events/usage.md +107 -0
  36. package/src/core/offline/bridge.js +51 -0
  37. package/src/core/offline/clock.js +100 -0
  38. package/src/core/offline/connectivity.js +116 -0
  39. package/src/core/offline/index.js +41 -0
  40. package/src/core/offline/missing.md +89 -0
  41. package/src/core/offline/plan.md +143 -0
  42. package/src/core/offline/queue.js +168 -0
  43. package/src/core/offline/state.js +18 -0
  44. package/src/core/offline/sync.js +106 -0
  45. package/src/core/offline/usage.md +273 -0
  46. package/src/core/platform/guard.js +104 -0
  47. package/src/core/platform/index.js +42 -0
  48. package/src/core/platform/missing.md +119 -0
  49. package/src/core/platform/platform.d.ts +88 -0
  50. package/src/core/platform/polyfills/anchor.js +79 -0
  51. package/src/core/platform/polyfills/navigation.js +142 -0
  52. package/src/core/platform/polyfills/popover.js +142 -0
  53. package/src/core/platform/polyfills/scheduler.js +194 -0
  54. package/src/core/platform/polyfills/shadow.js +35 -0
  55. package/src/core/platform/polyfills/urlpattern.js +119 -0
  56. package/src/core/platform/supports.js +186 -0
  57. package/src/core/platform/usage.md +287 -0
  58. package/src/core/router/cache.js +95 -0
  59. package/src/core/router/container.js +146 -0
  60. package/src/core/router/handler.js +52 -0
  61. package/src/core/router/history.js +120 -0
  62. package/src/core/router/index.js +158 -0
  63. package/src/core/router/intercept.js +376 -0
  64. package/src/core/router/match.js +145 -0
  65. package/src/core/router/missing.md +716 -0
  66. package/src/core/router/outlet.js +139 -0
  67. package/src/core/router/plan.md +370 -0
  68. package/src/core/router/sync/index.js +16 -0
  69. package/src/core/router/sync/tab.js +115 -0
  70. package/src/core/router/sync/transport.js +139 -0
  71. package/src/core/router/transitions.js +59 -0
  72. package/src/core/router/usage.md +773 -0
  73. package/src/core/security/crypto.js +159 -0
  74. package/src/core/security/index.js +49 -0
  75. package/src/core/security/missing.md +97 -0
  76. package/src/core/security/permissions.js +64 -0
  77. package/src/core/security/sanitize.js +100 -0
  78. package/src/core/security/usage.md +283 -0
  79. package/src/core/state/derived.js +117 -0
  80. package/src/core/state/index.js +23 -0
  81. package/src/core/state/missing.md +165 -0
  82. package/src/core/state/persist.js +284 -0
  83. package/src/core/state/store.js +308 -0
  84. package/src/core/state/sync.js +46 -0
  85. package/src/core/state/usage.md +440 -0
  86. package/src/core/storage/cache.js +83 -0
  87. package/src/core/storage/idb.js +196 -0
  88. package/src/core/storage/index.js +373 -0
  89. package/src/core/storage/lru.js +107 -0
  90. package/src/core/storage/missing.md +165 -0
  91. package/src/core/storage/opfs.js +190 -0
  92. package/src/core/storage/plan.md +69 -0
  93. package/src/core/storage/quota.js +69 -0
  94. package/src/core/storage/usage.md +226 -0
  95. package/src/core/ui/base.js +50 -0
  96. package/src/core/ui/define/container.js +82 -0
  97. package/src/core/ui/define/define.js +12 -0
  98. package/src/core/ui/define/element.js +390 -0
  99. package/src/core/ui/define/index.js +9 -0
  100. package/src/core/ui/define/orchestrator.js +105 -0
  101. package/src/core/ui/define/proxy.js +644 -0
  102. package/src/core/ui/define/state.js +6 -0
  103. package/src/core/ui/define/utils.js +134 -0
  104. package/src/core/ui/implementation.md +170 -0
  105. package/src/core/ui/index.js +41 -0
  106. package/src/core/ui/observe.js +117 -0
  107. package/src/core/ui/plan.md +510 -0
  108. package/src/core/ui/schedule.js +60 -0
  109. package/src/core/ui/template.js +37 -0
  110. package/src/core/ui/transitions.js +37 -0
  111. package/src/core/ui/ui.types.md +890 -0
  112. package/src/core/ui/usage.md +1124 -0
  113. package/src/core/ui/watch.md +346 -0
  114. package/src/core/workers/broadcast.js +138 -0
  115. package/src/core/workers/dedicated.js +153 -0
  116. package/src/core/workers/index.js +169 -0
  117. package/src/core/workers/locks.js +160 -0
  118. package/src/core/workers/offscreen.js +166 -0
  119. package/src/core/workers/plan.md +381 -0
  120. package/src/core/workers/pool.js +267 -0
  121. package/src/core/workers/shared.js +137 -0
  122. package/src/core/workers/usage.md +622 -0
  123. package/src/elements/base.js +12 -0
  124. package/src/elements/data/card/index.html +9 -0
  125. package/src/elements/data/card/index.js +19 -0
  126. package/src/elements/data/card/index.tags.json +1 -0
  127. package/src/elements/data/card/style.css +46 -0
  128. package/src/elements/data/chart/index.html +1 -0
  129. package/src/elements/data/chart/index.js +143 -0
  130. package/src/elements/data/chart/index.tags.json +1 -0
  131. package/src/elements/data/chart/style.css +13 -0
  132. package/src/elements/data/list/index.html +3 -0
  133. package/src/elements/data/list/index.js +19 -0
  134. package/src/elements/data/list/index.tags.json +1 -0
  135. package/src/elements/data/list/style.css +39 -0
  136. package/src/elements/data/stat/index.html +9 -0
  137. package/src/elements/data/stat/index.js +19 -0
  138. package/src/elements/data/stat/index.tags.json +1 -0
  139. package/src/elements/data/stat/style.css +50 -0
  140. package/src/elements/data/table/index.html +1 -0
  141. package/src/elements/data/table/index.js +16 -0
  142. package/src/elements/data/table/index.tags.json +1 -0
  143. package/src/elements/data/table/style.css +50 -0
  144. package/src/elements/feedback/alert/index.html +11 -0
  145. package/src/elements/feedback/alert/index.js +28 -0
  146. package/src/elements/feedback/alert/index.tags.json +1 -0
  147. package/src/elements/feedback/alert/style.css +75 -0
  148. package/src/elements/feedback/empty/index.html +13 -0
  149. package/src/elements/feedback/empty/index.js +34 -0
  150. package/src/elements/feedback/empty/index.tags.json +1 -0
  151. package/src/elements/feedback/empty/style.css +45 -0
  152. package/src/elements/feedback/progress/index.html +7 -0
  153. package/src/elements/feedback/progress/index.js +46 -0
  154. package/src/elements/feedback/progress/index.tags.json +1 -0
  155. package/src/elements/feedback/progress/style.css +36 -0
  156. package/src/elements/feedback/skeleton/index.html +1 -0
  157. package/src/elements/feedback/skeleton/index.js +78 -0
  158. package/src/elements/feedback/skeleton/index.tags.json +1 -0
  159. package/src/elements/feedback/skeleton/style.css +28 -0
  160. package/src/elements/feedback/toast/index.html +3 -0
  161. package/src/elements/feedback/toast/index.js +65 -0
  162. package/src/elements/feedback/toast/index.tags.json +1 -0
  163. package/src/elements/feedback/toast/style.css +36 -0
  164. package/src/elements/forms/checkbox/index.html +7 -0
  165. package/src/elements/forms/checkbox/index.js +104 -0
  166. package/src/elements/forms/checkbox/index.tags.json +1 -0
  167. package/src/elements/forms/checkbox/style.css +86 -0
  168. package/src/elements/forms/field/index.html +13 -0
  169. package/src/elements/forms/field/index.js +42 -0
  170. package/src/elements/forms/field/index.tags.json +1 -0
  171. package/src/elements/forms/field/style.css +42 -0
  172. package/src/elements/forms/form/index.html +3 -0
  173. package/src/elements/forms/form/index.js +122 -0
  174. package/src/elements/forms/form/index.tags.json +1 -0
  175. package/src/elements/forms/form/style.css +11 -0
  176. package/src/elements/forms/input/index.html +4 -0
  177. package/src/elements/forms/input/index.js +103 -0
  178. package/src/elements/forms/input/index.tags.json +1 -0
  179. package/src/elements/forms/input/style.css +39 -0
  180. package/src/elements/forms/radio/index.html +4 -0
  181. package/src/elements/forms/radio/index.js +109 -0
  182. package/src/elements/forms/radio/index.tags.json +1 -0
  183. package/src/elements/forms/radio/style.css +65 -0
  184. package/src/elements/forms/select/index.html +9 -0
  185. package/src/elements/forms/select/index.js +114 -0
  186. package/src/elements/forms/select/index.tags.json +1 -0
  187. package/src/elements/forms/select/style.css +95 -0
  188. package/src/elements/forms/textarea/index.html +4 -0
  189. package/src/elements/forms/textarea/index.js +115 -0
  190. package/src/elements/forms/textarea/index.tags.json +1 -0
  191. package/src/elements/forms/textarea/style.css +46 -0
  192. package/src/elements/forms/toggle/index.html +4 -0
  193. package/src/elements/forms/toggle/index.js +89 -0
  194. package/src/elements/forms/toggle/index.tags.json +1 -0
  195. package/src/elements/forms/toggle/style.css +63 -0
  196. package/src/elements/forms/upload/index.html +13 -0
  197. package/src/elements/forms/upload/index.js +120 -0
  198. package/src/elements/forms/upload/index.tags.json +1 -0
  199. package/src/elements/forms/upload/style.css +61 -0
  200. package/src/elements/index.js +71 -0
  201. package/src/elements/layout/app/index.html +7 -0
  202. package/src/elements/layout/app/index.js +16 -0
  203. package/src/elements/layout/app/index.tags.json +1 -0
  204. package/src/elements/layout/app/style.css +41 -0
  205. package/src/elements/layout/grid/index.html +3 -0
  206. package/src/elements/layout/grid/index.js +41 -0
  207. package/src/elements/layout/grid/index.tags.json +1 -0
  208. package/src/elements/layout/grid/style.css +12 -0
  209. package/src/elements/layout/header/index.html +8 -0
  210. package/src/elements/layout/header/index.js +16 -0
  211. package/src/elements/layout/header/index.tags.json +1 -0
  212. package/src/elements/layout/header/style.css +28 -0
  213. package/src/elements/layout/scroll/index.html +3 -0
  214. package/src/elements/layout/scroll/index.js +19 -0
  215. package/src/elements/layout/scroll/index.tags.json +1 -0
  216. package/src/elements/layout/scroll/style.css +24 -0
  217. package/src/elements/layout/sidebar/index.html +3 -0
  218. package/src/elements/layout/sidebar/index.js +24 -0
  219. package/src/elements/layout/sidebar/index.tags.json +1 -0
  220. package/src/elements/layout/sidebar/style.css +30 -0
  221. package/src/elements/layout/split/index.html +3 -0
  222. package/src/elements/layout/split/index.js +18 -0
  223. package/src/elements/layout/split/index.tags.json +1 -0
  224. package/src/elements/layout/split/style.css +28 -0
  225. package/src/elements/layout/stack/index.html +3 -0
  226. package/src/elements/layout/stack/index.js +31 -0
  227. package/src/elements/layout/stack/index.tags.json +1 -0
  228. package/src/elements/layout/stack/style.css +15 -0
  229. package/src/elements/layout/surface/index.html +3 -0
  230. package/src/elements/layout/surface/index.js +19 -0
  231. package/src/elements/layout/surface/index.tags.json +1 -0
  232. package/src/elements/layout/surface/style.css +29 -0
  233. package/src/elements/navigation/breadcrumb/index.html +5 -0
  234. package/src/elements/navigation/breadcrumb/index.js +16 -0
  235. package/src/elements/navigation/breadcrumb/index.tags.json +1 -0
  236. package/src/elements/navigation/breadcrumb/style.css +36 -0
  237. package/src/elements/navigation/nav/index.html +3 -0
  238. package/src/elements/navigation/nav/index.js +24 -0
  239. package/src/elements/navigation/nav/index.tags.json +1 -0
  240. package/src/elements/navigation/nav/style.css +38 -0
  241. package/src/elements/navigation/pagination/index.html +3 -0
  242. package/src/elements/navigation/pagination/index.js +94 -0
  243. package/src/elements/navigation/pagination/index.tags.json +1 -0
  244. package/src/elements/navigation/pagination/style.css +39 -0
  245. package/src/elements/navigation/steps/index.html +6 -0
  246. package/src/elements/navigation/steps/index.js +64 -0
  247. package/src/elements/navigation/steps/index.tags.json +1 -0
  248. package/src/elements/navigation/steps/style.css +78 -0
  249. package/src/elements/navigation/tabs/index.html +6 -0
  250. package/src/elements/navigation/tabs/index.js +132 -0
  251. package/src/elements/navigation/tabs/index.tags.json +1 -0
  252. package/src/elements/navigation/tabs/style.css +52 -0
  253. package/src/elements/overlay/dialog/index.html +5 -0
  254. package/src/elements/overlay/dialog/index.js +57 -0
  255. package/src/elements/overlay/dialog/index.tags.json +1 -0
  256. package/src/elements/overlay/dialog/style.css +31 -0
  257. package/src/elements/overlay/drawer/index.html +3 -0
  258. package/src/elements/overlay/drawer/index.js +56 -0
  259. package/src/elements/overlay/drawer/index.tags.json +1 -0
  260. package/src/elements/overlay/drawer/style.css +48 -0
  261. package/src/elements/overlay/menu/index.html +3 -0
  262. package/src/elements/overlay/menu/index.js +107 -0
  263. package/src/elements/overlay/menu/index.tags.json +1 -0
  264. package/src/elements/overlay/menu/style.css +43 -0
  265. package/src/elements/overlay/popover/index.html +3 -0
  266. package/src/elements/overlay/popover/index.js +44 -0
  267. package/src/elements/overlay/popover/index.tags.json +1 -0
  268. package/src/elements/overlay/popover/style.css +21 -0
  269. package/src/elements/overlay/sheet/index.html +8 -0
  270. package/src/elements/overlay/sheet/index.js +105 -0
  271. package/src/elements/overlay/sheet/index.tags.json +1 -0
  272. package/src/elements/overlay/sheet/style.css +64 -0
  273. package/src/elements/overlay/tooltip/index.html +6 -0
  274. package/src/elements/overlay/tooltip/index.js +16 -0
  275. package/src/elements/overlay/tooltip/index.tags.json +1 -0
  276. package/src/elements/overlay/tooltip/style.css +41 -0
  277. package/src/elements/primitives/avatar/index.html +2 -0
  278. package/src/elements/primitives/avatar/index.js +79 -0
  279. package/src/elements/primitives/avatar/index.tags.json +1 -0
  280. package/src/elements/primitives/avatar/style.css +36 -0
  281. package/src/elements/primitives/badge/index.html +3 -0
  282. package/src/elements/primitives/badge/index.js +20 -0
  283. package/src/elements/primitives/badge/index.tags.json +1 -0
  284. package/src/elements/primitives/badge/style.css +67 -0
  285. package/src/elements/primitives/button/index.html +3 -0
  286. package/src/elements/primitives/button/index.js +61 -0
  287. package/src/elements/primitives/button/index.tags.json +1 -0
  288. package/src/elements/primitives/button/style.css +66 -0
  289. package/src/elements/primitives/divider/index.html +1 -0
  290. package/src/elements/primitives/divider/index.js +43 -0
  291. package/src/elements/primitives/divider/index.tags.json +1 -0
  292. package/src/elements/primitives/divider/style.css +39 -0
  293. package/src/elements/primitives/icon/index.html +3 -0
  294. package/src/elements/primitives/icon/index.js +66 -0
  295. package/src/elements/primitives/icon/index.tags.json +1 -0
  296. package/src/elements/primitives/icon/style.css +20 -0
  297. package/src/elements/primitives/link/index.html +3 -0
  298. package/src/elements/primitives/link/index.js +129 -0
  299. package/src/elements/primitives/link/index.tags.json +1 -0
  300. package/src/elements/primitives/link/style.css +40 -0
  301. package/src/elements/primitives/spinner/index.html +1 -0
  302. package/src/elements/primitives/spinner/index.js +62 -0
  303. package/src/elements/primitives/spinner/index.tags.json +1 -0
  304. package/src/elements/primitives/spinner/style.css +20 -0
  305. package/src/elements/primitives/text/index.html +1 -0
  306. package/src/elements/primitives/text/index.js +79 -0
  307. package/src/elements/primitives/text/index.tags.json +1 -0
  308. package/src/elements/primitives/text/style.css +25 -0
  309. package/src/index.js +23 -0
  310. package/src/styles/base.css +66 -0
  311. package/src/styles/index.css +10 -0
  312. package/src/styles/layers.css +9 -0
  313. package/src/styles/reset.css +66 -0
  314. package/src/sw/activate.js +51 -0
  315. package/src/sw/expire.js +47 -0
  316. package/src/sw/index.js +28 -0
  317. package/src/sw/install.js +35 -0
  318. package/src/sw/push.js +58 -0
  319. package/src/sw/queue.js +60 -0
  320. package/src/sw/routes.js +71 -0
  321. package/src/sw/strategies.js +247 -0
  322. package/src/sw/sync.js +80 -0
  323. package/src/tokens/index.css +26 -0
  324. package/src/tokens/primitives/colors.css +54 -0
  325. package/src/tokens/primitives/motion.css +34 -0
  326. package/src/tokens/primitives/radius.css +16 -0
  327. package/src/tokens/primitives/shadow.css +34 -0
  328. package/src/tokens/primitives/spacing.css +27 -0
  329. package/src/tokens/primitives/typography.css +46 -0
  330. package/src/tokens/primitives/zindex.css +18 -0
  331. package/src/tokens/registered/colors.css +133 -0
  332. package/src/tokens/registered/dimensions.css +31 -0
  333. package/src/tokens/semantic/components.css +125 -0
  334. package/src/tokens/semantic/contrast.css +33 -0
  335. package/src/tokens/semantic/dark.css +61 -0
  336. package/src/tokens/semantic/light.css +64 -0
  337. package/types/core/animations/index.d.ts +52 -0
  338. package/types/core/api/index.d.ts +68 -0
  339. package/types/core/events/index.d.ts +50 -0
  340. package/types/core/offline/index.d.ts +68 -0
  341. package/types/core/platform/index.d.ts +60 -0
  342. package/types/core/router/index.d.ts +203 -0
  343. package/types/core/security/index.d.ts +33 -0
  344. package/types/core/state/index.d.ts +68 -0
  345. package/types/core/storage/index.d.ts +40 -0
  346. package/types/core/ui/index.d.ts +446 -0
  347. package/types/core/workers/index.d.ts +221 -0
  348. package/types/elements/index.d.ts +150 -0
  349. package/types/index.d.ts +18 -0
@@ -0,0 +1,773 @@
1
+ # Native Router Usage Guide
2
+
3
+ The Native Router layer owns same-document navigation for browser-native applications. It registers URL patterns, matches paths with `URLPattern`, intercepts Navigation API events, runs guards, emits route lifecycle events, coordinates named UI containers, synchronizes duplicate tabs, and opens or closes route-scoped background connections.
4
+
5
+ Status: this guide documents the implemented public router contract: `router.register`, `router.load`, `router.match`, `router.clear`, `router.guard`, `router.guards`, `router.notFound`, `router.miss`, `router.on`, `router.nav.to`, history wrappers, container registry APIs, `router.sync`, `router.links`, connection coordination APIs, and declarative `ui.element` / `ui.container` integration.
6
+
7
+ Import from the router entry point:
8
+
9
+ ```javascript
10
+ import { router } from '@adukiorg/anza/router';
11
+ ```
12
+
13
+ Named exports are also available:
14
+
15
+ ```javascript
16
+ import {
17
+ register,
18
+ load,
19
+ match,
20
+ navigate,
21
+ replace,
22
+ back,
23
+ forward,
24
+ on,
25
+ nav
26
+ } from '@adukiorg/anza/router';
27
+ ```
28
+
29
+ ## 1. Choosing an API
30
+
31
+ | Need | Use |
32
+ |---|---|
33
+ | Register a route | `router.register` |
34
+ | Bulk register routes from JSON | `router.load` |
35
+ | Match a URL manually | `router.match` |
36
+ | Remove all routes | `router.clear` |
37
+ | Protect navigation | `router.guard` |
38
+ | Manage guards | `router.guards` |
39
+ | Handle unmatched routes | `router.notFound` |
40
+ | Manage unmatched-route handler | `router.miss` |
41
+ | Listen to route events | `router.on` |
42
+ | Navigate with callbacks | `router.nav.to` |
43
+ | Navigate directly | `router.navigate` |
44
+ | Replace current URL | `router.replace` |
45
+ | Traverse history | `router.back`, `router.forward`, `router.go` |
46
+ | Read history state | `router.current`, `router.entries` |
47
+ | Register a layout target | `router.registerContainer` |
48
+ | Route with UI components | `ui.element`, `ui.container` |
49
+ | Route-scoped connections | `router.links`, `router.registerConnection` |
50
+ | Prefetch route assets | `router.prefetch` |
51
+ | Manage the route cache | `router.cache` |
52
+ | Cross-tab route sync | `router.sync` |
53
+ | Navigation listener teardown | `router.destroy` |
54
+
55
+ ## 2. Route Registration
56
+
57
+ Use `router.register(pattern, handler, meta)` to add a route.
58
+
59
+ ```javascript
60
+ router.register('/dashboard', 'page-dashboard');
61
+ router.register('/members/:member', 'page-member');
62
+ router.register('/files/*', 'page-files');
63
+ ```
64
+
65
+ The handler is usually a tag name string.
66
+
67
+ ```javascript
68
+ router.register('/settings', 'page-settings');
69
+ ```
70
+
71
+ It can also be a zero-argument function that returns a tag name. This is useful for lazy resolution.
72
+
73
+ ```javascript
74
+ router.register('/reports', async () => {
75
+ await import('/pages/reports.js');
76
+ return 'page-reports';
77
+ });
78
+ ```
79
+
80
+ Route metadata is stored on the route entry and is used by the router and UI integration.
81
+
82
+ ```javascript
83
+ router.register('/members/:member', 'page-member', {
84
+ container: 'main',
85
+ title: 'Member'
86
+ });
87
+ ```
88
+
89
+ ### Bulk Route Loading
90
+
91
+ Use `router.load(routesData)` to register multiple routes from a JSON array or object. This is useful for loading route definitions from a configuration file or API response.
92
+
93
+ ```javascript
94
+ // Load from an array
95
+ router.load([
96
+ { pattern: '/dashboard', handler: 'page-dashboard', meta: { container: 'main' } },
97
+ { pattern: '/members/:member', handler: 'page-member', meta: { container: 'main' } },
98
+ { pattern: '/files/*', handler: 'page-files', meta: { container: 'main' } }
99
+ ]);
100
+
101
+ // Load from a single object
102
+ router.load({
103
+ pattern: '/settings',
104
+ handler: 'page-settings',
105
+ meta: { container: 'main' }
106
+ });
107
+ ```
108
+
109
+ `router.load` accepts the same handler shapes as `router.register` (tag strings, lazy factories, callbacks, or object forms).
110
+
111
+ ### Handler contract
112
+
113
+ A handler is exactly one of the following shapes. This single contract is shared
114
+ by `match()` and the navigation interceptor, so a handler is never invoked twice:
115
+
116
+ | Shape | Meaning |
117
+ |---|---|
118
+ | `'page-tag'` | static element tag |
119
+ | `async () => 'page-tag'` | lazy tag factory (zero arity) |
120
+ | `{ tag: 'page-tag' }` | static tag (object form) |
121
+ | `{ load: async () => 'page-tag' }` | lazy tag factory (object form) |
122
+ | `(params, event) => { ... }` | callback (arity > 0) |
123
+ | `{ handler: (params, event) => { ... } }` | callback (object form) |
124
+
125
+ ```javascript
126
+ router.register('/dashboard', 'page-dashboard'); // tag
127
+ router.register('/reports', { load: () => loadReports() }); // lazy
128
+ router.register('/ping', (params, event) => log(params)); // callback
129
+ ```
130
+
131
+ Rules:
132
+
133
+ - Prefer a tag string for routed UI.
134
+ - Use a zero-argument function or `{ load }` for lazy tag resolution.
135
+ - `match()` resolves a tag without ever running callbacks; callbacks run once, on
136
+ navigation only.
137
+ - More specific routes are sorted before less specific routes.
138
+
139
+ ### Route cache (Cache API)
140
+
141
+ The router exposes an internal cache backed by the browser Cache API for view
142
+ assets and lazily-loaded module responses. Prefetch on hover or when a link
143
+ becomes visible to make navigation instant.
144
+
145
+ ```javascript
146
+ // Warm the cache for a route's assets
147
+ await router.prefetch('/pages/reports.js');
148
+
149
+ // Lower-level control
150
+ await router.cache.set('/pages/reports.js', response, 60_000); // ttl ms
151
+ const hit = await router.cache.get('/pages/reports.js'); // Response | null
152
+ await router.cache.purge('/pages/reports.js'); // one entry
153
+ await router.cache.purge(); // whole cache
154
+ ```
155
+
156
+ Entries use an `x-expires-at` TTL header (the same convention as the storage and
157
+ api caches) and expire automatically on read.
158
+
159
+ ## 3. Pattern Matching
160
+
161
+ `router.match(url)` returns the first matching route or `null`.
162
+
163
+ ```javascript
164
+ const found = await router.match('/members/42');
165
+
166
+ if (found) {
167
+ console.log(found.tag); // "page-member"
168
+ console.log(found.params.member); // "42"
169
+ }
170
+ ```
171
+
172
+ Match result shape:
173
+
174
+ ```javascript
175
+ {
176
+ route,
177
+ tag,
178
+ params,
179
+ result
180
+ }
181
+ ```
182
+
183
+ Examples:
184
+
185
+ ```javascript
186
+ router.register('/about', 'page-about');
187
+ router.register('/members/:member', 'page-member');
188
+ router.register('/assets/*', 'page-assets');
189
+ ```
190
+
191
+ Specificity order:
192
+
193
+ 1. Static paths: `/settings/profile`
194
+ 2. Parameter paths: `/members/:member`
195
+ 3. Wildcards: `/assets/*`
196
+
197
+ Within each group, longer patterns sort first.
198
+
199
+ ## 4. Clearing Routes
200
+
201
+ Use `router.clear()` in tests or full app teardown.
202
+
203
+ ```javascript
204
+ router.clear();
205
+ ```
206
+
207
+ `clear()` only clears the route table. It does not clear guards, event listeners, containers, sync, or background connections.
208
+
209
+ Use `router.destroy()` to remove Navigation API listeners and reset guard, event, not-found, and sync listener state.
210
+
211
+ ```javascript
212
+ router.destroy();
213
+ ```
214
+
215
+ ## 5. Navigation Events
216
+
217
+ Use `router.on(type, callback, signal?)` to subscribe to router events.
218
+
219
+ ```javascript
220
+ const stop = router.on('found', ({ tag, params, url, direction }) => {
221
+ console.log(tag, params, url, direction);
222
+ });
223
+
224
+ stop();
225
+ ```
226
+
227
+ Supported events:
228
+
229
+ ```javascript
230
+ 'found'
231
+ 'notfound'
232
+ 'error'
233
+ ```
234
+
235
+ Event payloads:
236
+
237
+ ```javascript
238
+ router.on('found', ({ tag, params, url, direction }) => {});
239
+ router.on('notfound', ({ url }) => {});
240
+ router.on('error', ({ error, url, route, phase }) => {});
241
+ ```
242
+
243
+ With abort cleanup:
244
+
245
+ ```javascript
246
+ const ctrl = new AbortController();
247
+
248
+ router.on('found', refresh, ctrl.signal);
249
+
250
+ ctrl.abort();
251
+ ```
252
+
253
+ Rules:
254
+
255
+ - Listener errors are caught and logged.
256
+ - `router.on` returns a disposer.
257
+ - Pass an `AbortSignal` when the listener belongs to a component lifecycle.
258
+
259
+ ## 6. Programmatic Navigation
260
+
261
+ Use `router.navigate(url, options?)` for normal navigation.
262
+
263
+ ```javascript
264
+ router.navigate('/members/42');
265
+ ```
266
+
267
+ With state and transient info:
268
+
269
+ ```javascript
270
+ router.navigate('/members/42', {
271
+ state: { scroll: 0 },
272
+ info: { source: 'search' }
273
+ });
274
+ ```
275
+
276
+ Use `router.replace(url, options?)` when the URL change should not add a new history entry.
277
+
278
+ ```javascript
279
+ router.replace('/members?page=2');
280
+ ```
281
+
282
+ History traversal:
283
+
284
+ ```javascript
285
+ router.back();
286
+ router.forward();
287
+ router.go(-2);
288
+ ```
289
+
290
+ Read current entries:
291
+
292
+ ```javascript
293
+ const entry = router.current();
294
+ const all = router.entries();
295
+
296
+ if (router.canBack()) {
297
+ router.back();
298
+ }
299
+ ```
300
+
301
+ These wrappers delegate to `window.navigation`. In non-browser environments or runtimes without Navigation API support, they return safe fallback values.
302
+
303
+ ## 7. Fluent Navigation
304
+
305
+ Use `router.nav.to(url)` when you want callbacks tied to a single navigation.
306
+
307
+ ```javascript
308
+ router.nav.to('/members/42')
309
+ .on('found', ({ tag, params }) => {
310
+ console.log(tag, params.member);
311
+ })
312
+ .on('notfound', ({ url }) => {
313
+ console.warn('No route:', url);
314
+ })
315
+ .on('error', (err) => {
316
+ console.error(err);
317
+ });
318
+ ```
319
+
320
+ Rules:
321
+
322
+ - `nav.to` returns a transition controller.
323
+ - `.on(event, callback)` is chainable.
324
+ - Supported fluent events are `found`, `notfound`, and `error`.
325
+ - URL matching for callbacks compares pathnames, so absolute and relative URLs can both work.
326
+
327
+ ## 8. Guards
328
+
329
+ Use `router.guard(fn)` to protect navigations.
330
+
331
+ ```javascript
332
+ const stop = router.guard((destination) => {
333
+ const url = new URL(destination.url);
334
+
335
+ if (url.pathname.startsWith('/admin') && !session.admin) {
336
+ return '/login';
337
+ }
338
+
339
+ return null;
340
+ });
341
+
342
+ stop();
343
+ ```
344
+
345
+ Guard shape:
346
+
347
+ ```javascript
348
+ (destination, controller) => string | null | undefined | Promise<string | null | undefined>
349
+ ```
350
+
351
+ Rules:
352
+
353
+ - Return a URL string to redirect.
354
+ - Return `null` or `undefined` to allow.
355
+ - Guards run in registration order.
356
+ - In browsers with `precommitHandler`, guards run before commit.
357
+ - In Safari-style fallback paths, guards also run inside the handler after commit and then replace the URL if blocked.
358
+
359
+ Grouped guard API:
360
+
361
+ ```javascript
362
+ const stop = router.guards.add(fn);
363
+ router.guards.clear();
364
+ ```
365
+
366
+ ## 9. Not Found
367
+
368
+ Use `router.notFound(handler)` for unmatched routes.
369
+
370
+ ```javascript
371
+ const stop = router.notFound(async (event) => {
372
+ const page = document.createElement('page-not-found');
373
+ const main = router.getContainer('main');
374
+ main?.replaceChildren(page);
375
+ });
376
+
377
+ stop();
378
+ ```
379
+
380
+ The router also emits `notfound`.
381
+
382
+ ```javascript
383
+ router.on('notfound', ({ url }) => {
384
+ console.warn(`No route matched ${url}`);
385
+ });
386
+ ```
387
+
388
+ Rules:
389
+
390
+ - `notFound` sets one global unmatched-route handler.
391
+ - If no handler exists, the router logs an error.
392
+
393
+ Grouped unmatched-route API:
394
+
395
+ ```javascript
396
+ router.miss.set(handler);
397
+ router.miss.clear();
398
+ ```
399
+
400
+ ## 10. Declarative UI Routes
401
+
402
+ The UI layer can register routes automatically through `ui.element`.
403
+
404
+ ```javascript
405
+ import { ui } from '@adukiorg/anza/ui';
406
+
407
+ ui.element('page-member', {
408
+ url: '/members/:member',
409
+ container: 'main',
410
+
411
+ props: {
412
+ member: { type: String, default: '' }
413
+ },
414
+
415
+ template: './member.html',
416
+ style: './member.css',
417
+
418
+ mount({ el }) {
419
+ loadMember(el.member);
420
+ },
421
+
422
+ update({ el, name, val }) {
423
+ if (name === 'member') {
424
+ loadMember(val);
425
+ }
426
+ }
427
+ }, import.meta.url);
428
+ ```
429
+
430
+ What happens:
431
+
432
+ 1. `ui.element` calls `router.register(url, tag, meta)`.
433
+ 2. The router matches the URL.
434
+ 3. The UI orchestrator listens for `found`.
435
+ 4. The orchestrator finds the named container.
436
+ 5. The page element is created and params are assigned as properties.
437
+ 6. If the same page tag is already mounted, params are updated on the existing instance.
438
+
439
+ The native toolchain also emits `routes.json` for declarative routes during `scan`, `build`, and `dev`.
440
+
441
+ ```json
442
+ {
443
+ "version": 1,
444
+ "routes": [
445
+ {
446
+ "tag": "page-member",
447
+ "path": "/members/:member",
448
+ "container": "main",
449
+ "params": ["member"]
450
+ }
451
+ ]
452
+ }
453
+ ```
454
+
455
+ ## 11. Containers
456
+
457
+ Use `ui.container` for router-owned layout slots.
458
+
459
+ ```javascript
460
+ ui.container('app-main', {
461
+ template: '<slot></slot>',
462
+ style: ':host { display: block; }'
463
+ });
464
+ ```
465
+
466
+ Mount it in HTML:
467
+
468
+ ```html
469
+ <app-main name="main"></app-main>
470
+ ```
471
+
472
+ The container registers itself with the router using its `name` attribute or tag name.
473
+
474
+ ```javascript
475
+ const main = router.getContainer('main');
476
+ ```
477
+
478
+ Container registry APIs:
479
+
480
+ ```javascript
481
+ router.registerContainer('main', element);
482
+ router.unregisterContainer('main', element);
483
+ router.getContainer('main');
484
+ router.clearContainers();
485
+ ```
486
+
487
+ Rules:
488
+
489
+ - Container names are unique.
490
+ - Registering a second live container with the same name throws.
491
+ - `getContainer(name)` can also resolve CSS selectors such as `#app`.
492
+ - A route with `meta.container` fails if its required container is not active.
493
+
494
+ ## 12. Container Swaps
495
+
496
+ `ui.container` injects `swapView(newElement, options?)`.
497
+
498
+ ```javascript
499
+ await main.swapView(document.createElement('page-home'), {
500
+ direction: 'push'
501
+ });
502
+ ```
503
+
504
+ Swap behavior:
505
+
506
+ 1. Try element-scoped `startViewTransition`.
507
+ 2. Fall back to `document.startViewTransition`.
508
+ 3. Fall back to `replaceChildren`.
509
+
510
+ The container sets `data-transition-direction` while swapping.
511
+
512
+ ```css
513
+ :host([data-transition-direction="push"]) {
514
+ view-transition-name: main;
515
+ }
516
+ ```
517
+
518
+ ## 13. Manual Containers
519
+
520
+ You can register a normal DOM node manually.
521
+
522
+ ```javascript
523
+ const el = document.querySelector('#main');
524
+
525
+ router.registerContainer('main', el);
526
+
527
+ router.on('found', ({ tag, params }) => {
528
+ const page = document.createElement(tag);
529
+ Object.assign(page, params);
530
+ el.replaceChildren(page);
531
+ });
532
+ ```
533
+
534
+ Clean up when the container is removed.
535
+
536
+ ```javascript
537
+ router.unregisterContainer('main', el);
538
+ ```
539
+
540
+ ## 14. Route-Scoped Connections
541
+
542
+ Use `router.links.add(pattern, factory)` to manage background connections tied to active routes.
543
+
544
+ ```javascript
545
+ const stop = router.links.add('/rooms/:room', async ({ url, params }) => {
546
+ console.log(params.room);
547
+
548
+ const socket = new WebSocket(`wss://example.com${url.pathname}`);
549
+
550
+ return {
551
+ close() {
552
+ socket.close();
553
+ }
554
+ };
555
+ });
556
+
557
+ stop();
558
+ ```
559
+
560
+ The older direct API remains available:
561
+
562
+ ```javascript
563
+ const stop = router.registerConnection('/rooms/:room', factory);
564
+ ```
565
+
566
+ The router coordinates connections after `navigatesuccess`.
567
+
568
+ ```javascript
569
+ router.getActiveConnections();
570
+ router.links.remove('/rooms/:room');
571
+ router.links.clear();
572
+ router.clearConnections();
573
+ ```
574
+
575
+ ### Links API
576
+
577
+ The `router.links` object provides normalized verbs for managing route-scoped connections:
578
+
579
+ | Method | Purpose |
580
+ |---|---|
581
+ | `router.links.add(pattern, factory)` | Register a connection factory (returns disposer) |
582
+ | `router.links.remove(pattern)` | Remove a specific connection factory and close its active connection |
583
+ | `router.links.clear()` | Remove all connection factories and close all active connections |
584
+
585
+ Connection behavior:
586
+
587
+ - A matching route opens the connection if it is not active.
588
+ - Navigating away closes stale connections.
589
+ - The factory receives `{ url, params }`.
590
+ - A connection object can expose `close()` or `disconnect()`.
591
+ - Factory errors are caught and logged.
592
+
593
+ For tests or manual coordination:
594
+
595
+ ```javascript
596
+ import { coordinateConnections } from '@adukiorg/anza/router';
597
+
598
+ await coordinateConnections('/rooms/general');
599
+ ```
600
+
601
+ ## 15. Cross-Tab Sync
602
+
603
+ The router bootstraps tab sync on client load when `BroadcastChannel` and `window.navigation` exist.
604
+
605
+ Behavior:
606
+
607
+ - On `navigatesuccess`, the current URL is broadcast on `native-router-sync`.
608
+ - Other same-origin tabs receive the message.
609
+ - The receiving tab calls `router.navigate(url, { state })`.
610
+ - A loop guard prevents repeated same-URL sync events.
611
+
612
+ Public controls:
613
+
614
+ ```javascript
615
+ router.sync.stop();
616
+ router.sync.start();
617
+ router.sync.active();
618
+ router.sync.close();
619
+ ```
620
+
621
+ Rules:
622
+
623
+ - `stop()` pauses handlers without closing the channel.
624
+ - `start()` resumes sync.
625
+ - `active()` reports whether sync is running.
626
+ - `close()` stops sync, closes the channel, and resets internal state.
627
+
628
+ ## 16. View Transitions
629
+
630
+ The router wraps route event work in `transitions.run`.
631
+
632
+ ```javascript
633
+ import { transitions } from '@adukiorg/anza/router';
634
+
635
+ await transitions.run(() => {
636
+ outlet.replaceChildren(page);
637
+ });
638
+ ```
639
+
640
+ Shared element source:
641
+
642
+ ```javascript
643
+ await transitions.run(() => {
644
+ outlet.replaceChildren(detail);
645
+ }, {
646
+ sourceElement: card,
647
+ name: 'selected-card'
648
+ });
649
+ ```
650
+
651
+ Rules:
652
+
653
+ - If View Transitions are unsupported, the callback runs directly.
654
+ - If `prefers-reduced-motion: reduce` matches, the callback runs directly.
655
+ - `sourceElement.style.viewTransitionName` is set temporarily.
656
+ - The name is cleared after transition completion.
657
+ - Aborted transition errors are caught and logged.
658
+
659
+ ## 17. Router-Aware Links
660
+
661
+ The elements package registers `<nav-link>`.
662
+
663
+ ```html
664
+ <nav-link href="/members">Members</nav-link>
665
+ ```
666
+
667
+ Behavior:
668
+
669
+ - Same-origin clicks call router navigation.
670
+ - Modified clicks, `_blank`, hash-only links, and external links use normal browser behavior.
671
+ - Matching active routes set `aria-current="page"` on the internal anchor.
672
+ - External links dispatch a cancelable `external` event.
673
+
674
+ External gate:
675
+
676
+ ```javascript
677
+ document.addEventListener('external', (event) => {
678
+ if (!confirm(`Open ${event.detail.href}?`)) {
679
+ event.preventDefault();
680
+ }
681
+ });
682
+ ```
683
+
684
+ ## 18. Testing Router Code
685
+
686
+ Clear route state between tests.
687
+
688
+ ```javascript
689
+ beforeEach(() => {
690
+ router.clear();
691
+ router.guards.clear();
692
+ router.miss.clear();
693
+ router.clearContainers();
694
+ router.clearConnections();
695
+ router.sync.stop();
696
+ });
697
+ ```
698
+
699
+ Test matching directly:
700
+
701
+ ```javascript
702
+ router.register('/members/:member', 'page-member');
703
+
704
+ const found = await router.match('/members/42');
705
+
706
+ if (found.params.member !== '42') {
707
+ throw new Error('wrong param');
708
+ }
709
+ ```
710
+
711
+ Test route events with disposers:
712
+
713
+ ```javascript
714
+ const stop = router.on('found', (detail) => {
715
+ console.log(detail.tag);
716
+ });
717
+
718
+ stop();
719
+ ```
720
+
721
+ ## 19. Runtime Requirements
722
+
723
+ The router is built around modern browser APIs:
724
+
725
+ - `window.navigation`
726
+ - `URLPattern`
727
+ - `BroadcastChannel`
728
+ - `WeakRef`
729
+ - `FinalizationRegistry`
730
+ - `MutationObserver`
731
+ - `document.startViewTransition`
732
+
733
+ Polyfills exist in `core/platform` for Navigation API and URLPattern. Other APIs degrade where possible.
734
+
735
+ ## 20. Security
736
+
737
+ - Treat route params as untrusted strings.
738
+ - Validate params before using them in network requests.
739
+ - Use guards for client-side UX protection, not as the only security boundary.
740
+ - Protect sensitive routes on the server or service worker layer too.
741
+ - Avoid direct `location.href`, `history.pushState`, and `history.replaceState`.
742
+ - Use `router.navigate` and `router.replace`.
743
+ - Keep guard callbacks fast and deterministic.
744
+
745
+ ## 21. Performance
746
+
747
+ - Register routes once at startup.
748
+ - Prefer tag strings or zero-argument lazy tag factories.
749
+ - Keep route guards fast.
750
+ - Use route-scoped connections for sockets and streams.
751
+ - Use containers to avoid remounting stable layouts.
752
+ - Use `replace` for filter/search URL updates that should not create history entries.
753
+ - Debounce search-as-you-type URL updates.
754
+
755
+ ## 22. Checklist
756
+
757
+ - Use `router.register` for manual routes.
758
+ - Use `router.load` to bulk-register routes from JSON configuration.
759
+ - Use `ui.element({ url, container })` for declarative page routes.
760
+ - Use `ui.container` for named route outlets.
761
+ - Use `router.on('found')`, `router.on('notfound')`, and `router.on('error')` for events.
762
+ - Use `router.guard` for client-side route gates.
763
+ - Use `router.guards.clear()` in tests or teardown.
764
+ - Use `router.notFound` for unmatched routes.
765
+ - Use `router.miss.clear()` to reset the unmatched-route handler.
766
+ - Use `router.navigate` and `router.replace` for URL writes.
767
+ - Use `router.nav.to` for chainable navigation callbacks.
768
+ - Use `router.links` (add/remove/clear) for route-scoped background connections.
769
+ - Use `router.registerConnection` for the direct connection factory API.
770
+ - Use `router.sync.stop()` when tab mirroring should pause.
771
+ - Clear routes, containers, and connections in tests.
772
+ - Keep URL state in the URL.
773
+ - Treat route params as untrusted input.