@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,119 @@
1
+ /**
2
+ * src/core/platform/polyfills/urlpattern.js
3
+ *
4
+ * A robust URLPattern fallback for browsers lacking native support.
5
+ * Implements pathname matching with named parameters, modifiers, wildcards,
6
+ * and smart trailing slash normalization.
7
+ *
8
+ * Source: doc 18 §3, library2.md §Phase 1-A
9
+ */
10
+
11
+ class URLPatternPolyfill {
12
+ #pathnameRegex;
13
+ #paramNames = [];
14
+
15
+ constructor(init) {
16
+ let pathnamePattern = '*';
17
+ if (typeof init === 'string') {
18
+ if (init.includes('://')) {
19
+ try {
20
+ const match = init.match(/^[a-zA-Z]+:\/\/[^\/]+(\/[^?#]*)/);
21
+ pathnamePattern = match ? match[1] : '/';
22
+ } catch {
23
+ pathnamePattern = init;
24
+ }
25
+ } else {
26
+ pathnamePattern = init;
27
+ }
28
+ } else if (init && typeof init === 'object') {
29
+ pathnamePattern = init.pathname || '*';
30
+ }
31
+
32
+ // Ensure pathnamePattern starts with /
33
+ if (!pathnamePattern.startsWith('/') && pathnamePattern !== '*') {
34
+ pathnamePattern = '/' + pathnamePattern;
35
+ }
36
+
37
+ this.#paramNames = [];
38
+ let wildcardCount = 0;
39
+
40
+ const segments = pathnamePattern.split('/');
41
+ const regexSegments = segments.map((segment, idx) => {
42
+ if (idx === 0 && segment === '') return '';
43
+
44
+ if (segment.startsWith(':')) {
45
+ let paramName = segment.slice(1);
46
+ let modifier = '';
47
+ if (paramName.endsWith('?') || paramName.endsWith('*') || paramName.endsWith('+')) {
48
+ modifier = paramName.slice(-1);
49
+ paramName = paramName.slice(0, -1);
50
+ }
51
+ this.#paramNames.push(paramName);
52
+
53
+ if (modifier === '?') {
54
+ return '([^/]*)';
55
+ } else if (modifier === '*') {
56
+ return '(.*)';
57
+ } else if (modifier === '+') {
58
+ return '([^/]+.*)';
59
+ } else {
60
+ return '([^/]+)';
61
+ }
62
+ } else if (segment === '*') {
63
+ this.#paramNames.push(String(wildcardCount++));
64
+ return '(.*)';
65
+ } else if (segment.includes('*')) {
66
+ this.#paramNames.push(String(wildcardCount++));
67
+ return segment.replace(/\*/g, '(.*)');
68
+ } else {
69
+ return segment.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
70
+ }
71
+ });
72
+
73
+ let patternStr = regexSegments.join('/');
74
+ if (patternStr === '') patternStr = '/';
75
+
76
+ // Support optional trailing slash if the pattern does not end in a wildcard or parameter modifier
77
+ if (!pathnamePattern.endsWith('*') && !pathnamePattern.endsWith('?') && pathnamePattern !== '/') {
78
+ patternStr += '/?';
79
+ }
80
+
81
+ this.#pathnameRegex = new RegExp('^' + patternStr + '$');
82
+ }
83
+
84
+ test(url) {
85
+ try {
86
+ const parsed = new URL(url, globalThis.location?.href || 'https://a.com');
87
+ return this.#pathnameRegex.test(parsed.pathname);
88
+ } catch {
89
+ return false;
90
+ }
91
+ }
92
+
93
+ exec(url) {
94
+ try {
95
+ const parsed = new URL(url, globalThis.location?.href || 'https://a.com');
96
+ const match = parsed.pathname.match(this.#pathnameRegex);
97
+ if (!match) return null;
98
+
99
+ const groups = {};
100
+ this.#paramNames.forEach((name, index) => {
101
+ groups[name] = match[index + 1] || '';
102
+ });
103
+
104
+ return {
105
+ pathname: {
106
+ input: parsed.pathname,
107
+ groups
108
+ }
109
+ };
110
+ } catch {
111
+ return null;
112
+ }
113
+ }
114
+ }
115
+
116
+ if (!globalThis.URLPattern) {
117
+ globalThis.URLPattern = URLPatternPolyfill;
118
+ }
119
+ export default URLPatternPolyfill;
@@ -0,0 +1,186 @@
1
+ /**
2
+ * core/platform/supports.js
3
+ *
4
+ * All feature-detection booleans for the platform.
5
+ * Each flag is lazily evaluated on first access and then cached.
6
+ * Detection tests are conservative — they check for correct behaviour,
7
+ * not just API presence, where known browser bugs exist.
8
+ *
9
+ * Source authority: doc 18 — Limitations, Browser Gaps, and Polyfill Strategy §17
10
+ */
11
+
12
+ const _cache = Object.create(null);
13
+
14
+ function lazy(key, detect) {
15
+ Object.defineProperty(supports, key, {
16
+ get() {
17
+ if (!(key in _cache)) _cache[key] = detect();
18
+ return _cache[key];
19
+ },
20
+ configurable: true,
21
+ enumerable: true,
22
+ });
23
+ }
24
+
25
+ export const supports = {};
26
+
27
+ // --- Routing ---
28
+
29
+ lazy('navigationAPI', () => 'navigation' in window);
30
+
31
+ lazy('urlPattern', () => 'URLPattern' in globalThis);
32
+
33
+ // --- Component Model ---
34
+
35
+ lazy('declarativeShadowDOM', () =>
36
+ 'shadowRootMode' in HTMLTemplateElement.prototype
37
+ );
38
+
39
+ lazy('customStatePseudo', () =>
40
+ typeof ElementInternals !== 'undefined' &&
41
+ 'states' in ElementInternals.prototype
42
+ );
43
+
44
+ lazy('formAssociated', () =>
45
+ typeof HTMLElement !== 'undefined' &&
46
+ 'attachInternals' in HTMLElement.prototype
47
+ );
48
+
49
+ // --- Overlay / Popover ---
50
+
51
+ lazy('popoverAPI', () => 'popover' in HTMLElement.prototype);
52
+
53
+ lazy('anchorPositioning', () =>
54
+ CSS.supports('anchor-name', '--a')
55
+ );
56
+
57
+ // --- Animation ---
58
+
59
+ lazy('viewTransitions', () => 'startViewTransition' in document);
60
+
61
+ lazy('scrollTimeline', () => 'ScrollTimeline' in window);
62
+
63
+ lazy('viewTimeline', () => 'ViewTimeline' in window);
64
+
65
+ // --- Scheduling ---
66
+
67
+ lazy('schedulerPostTask', () =>
68
+ 'scheduler' in globalThis && 'postTask' in scheduler
69
+ );
70
+
71
+ lazy('schedulerYield', () =>
72
+ 'scheduler' in globalThis && 'yield' in scheduler
73
+ );
74
+
75
+ // --- CSS ---
76
+
77
+ lazy('contentVisibility', () =>
78
+ CSS.supports('content-visibility', 'auto')
79
+ );
80
+
81
+ lazy('cssScope', () => CSS.supports('selector(:scope)'));
82
+
83
+ lazy('cssLayer', () => {
84
+ try {
85
+ // @supports at-rule(@layer) is not universally parseable in JS;
86
+ // inject a sheet and check if a layer-wrapped rule is applied.
87
+ const s = document.createElement('style');
88
+ s.textContent = '@layer _test { ._testlayer { --_l: 1 } }';
89
+ document.head.append(s);
90
+ const ok = !!s.sheet;
91
+ s.remove();
92
+ return ok;
93
+ } catch { return false; }
94
+ });
95
+
96
+ lazy('cssModuleScripts', () => {
97
+ // CSS Module Scripts: import with { type: 'css' }
98
+ // Absent Firefox / Safari as of 2026 — doc 18 §14, library2.md §CSS Distribution
99
+ try {
100
+ return HTMLScriptElement.supports('importmap'); // proxy: if importmap exists, env is modern enough to test
101
+ // We check actual CSS module support via import attributes presence
102
+ } catch { return false; }
103
+ });
104
+
105
+ // --- Module System ---
106
+
107
+ lazy('importMaps', () =>
108
+ HTMLScriptElement.supports && HTMLScriptElement.supports('importmap')
109
+ );
110
+
111
+ // --- Security ---
112
+
113
+ lazy('sanitizerAPI', () => 'Sanitizer' in window);
114
+
115
+ lazy('trustedTypes', () => 'trustedTypes' in window);
116
+
117
+ lazy('subtleCrypto', () =>
118
+ typeof crypto !== 'undefined' && 'subtle' in crypto
119
+ );
120
+
121
+ // --- Storage ---
122
+
123
+ lazy('opfs', () =>
124
+ 'storage' in navigator &&
125
+ typeof navigator.storage.getDirectory === 'function'
126
+ );
127
+
128
+ lazy('storageManager', () => 'storage' in navigator);
129
+
130
+ lazy('fileSystemPickers', () => 'showOpenFilePicker' in window);
131
+
132
+ lazy('compression', () =>
133
+ typeof CompressionStream !== 'undefined' &&
134
+ typeof DecompressionStream !== 'undefined'
135
+ );
136
+
137
+ lazy('storagePersistence', () =>
138
+ 'storage' in navigator &&
139
+ typeof navigator.storage.persist === 'function' &&
140
+ typeof navigator.storage.persisted === 'function'
141
+ );
142
+
143
+ // --- Networking / Workers ---
144
+
145
+ lazy('backgroundSync', () =>
146
+ 'ServiceWorkerRegistration' in window &&
147
+ 'sync' in ServiceWorkerRegistration.prototype
148
+ );
149
+
150
+ lazy('speculationRules', () =>
151
+ HTMLScriptElement.supports &&
152
+ HTMLScriptElement.supports('speculationrules')
153
+ );
154
+
155
+ lazy('sharedWorker', () => 'SharedWorker' in window);
156
+
157
+ lazy('webLocks', () => 'locks' in navigator);
158
+
159
+ lazy('offscreenCanvas', () => 'OffscreenCanvas' in window);
160
+
161
+ // --- Notifications / Push ---
162
+
163
+ lazy('pushAPI', () => 'PushManager' in window);
164
+
165
+ lazy('notificationsAPI', () => 'Notification' in window);
166
+
167
+ // --- Device ---
168
+
169
+ lazy('screenWakeLock', () => 'wakeLock' in navigator);
170
+
171
+ lazy('idleDetection', () => 'IdleDetector' in window);
172
+
173
+ lazy('webAuthn', () => 'PublicKeyCredential' in window);
174
+
175
+ // --- Reset cached value (for testing only) ---
176
+ export function reset(key) {
177
+ delete _cache[key];
178
+ }
179
+
180
+ // --- Type guard: assert feature support at runtime ---
181
+ export function typeGuard(key, message) {
182
+ if (!supports[key]) {
183
+ const msg = message || `Platform does not support: ${key}`;
184
+ throw new Error(msg);
185
+ }
186
+ }
@@ -0,0 +1,287 @@
1
+ # Platform Module — Developer Usage Guide
2
+
3
+ The `@adukiorg/anza/platform` module acts as a smart, lazy-evaluated browser capability layer and zero-overhead polyfill manager. It exposes simple feature flags, unified asynchronous feature gates, a prioritized task scheduler, leak-proof popovers, and resilient URLPattern pathname matching.
4
+
5
+ ---
6
+
7
+ ## 1. Import System
8
+
9
+ To keep your client application highly performant and structured, import everything directly from the unified platform specifier:
10
+
11
+ ```javascript
12
+ import { supports, guard, reset, typeGuard } from '@adukiorg/anza/platform';
13
+ ```
14
+
15
+ ---
16
+
17
+ ## 2. Browser Capabilities Matrix (`supports`)
18
+
19
+ The `supports` object is a lazy-evaluated, cached registry containing 30+ boolean flags for modern HTML5, CSS, Web Cryptography, OPFS, compression, storage managers, and Worker APIs.
20
+
21
+ ### Example Usage
22
+
23
+ ```javascript
24
+ import { supports } from '@adukiorg/anza/platform';
25
+
26
+ // 1. OPFS support
27
+ if (supports.opfs) {
28
+ console.log('Origin Private File System is natively supported!');
29
+ } else {
30
+ console.log('OPFS absent; using standard IndexedDB storage fallback.');
31
+ }
32
+
33
+ // 2. Storage Persistence capability
34
+ if (supports.storagePersistence) {
35
+ navigator.storage.persist().then(granted => {
36
+ console.log(`Durable storage granted: ${granted}`);
37
+ });
38
+ }
39
+
40
+ // 3. Compression Streams capability
41
+ if (supports.compression) {
42
+ const compressor = new CompressionStream('gzip');
43
+ console.log('Compression Stream is natively supported!');
44
+ }
45
+
46
+ // 4. Check CSS View Transitions support
47
+ if (supports.viewTransitions) {
48
+ document.startViewTransition(() => updateTheDOM());
49
+ } else {
50
+ updateTheDOM();
51
+ }
52
+ ```
53
+
54
+ *Note: For unit testing, you can clear cached lazy-evaluated values using the `reset(flag)` method. This allows mocking feature states dynamically in tests:*
55
+
56
+ ```javascript
57
+ import { reset, supports } from '@adukiorg/anza/platform';
58
+
59
+ // Force urlPattern to false for testing fallback polyfill loading
60
+ Object.defineProperty(supports, 'urlPattern', { value: false, configurable: true });
61
+
62
+ // Run your test assertions here...
63
+
64
+ // Clear mock value and restore original lazy capability checker
65
+ reset('urlPattern');
66
+ ```
67
+
68
+ ### Runtime Type Guarding
69
+
70
+ Use `typeGuard` to assert feature support at runtime. It throws an error with a custom message if the feature is not available, making it useful for early-fail checks in critical code paths:
71
+
72
+ ```javascript
73
+ import { typeGuard } from '@adukiorg/anza/platform';
74
+
75
+ // Assert OPFS support before attempting file operations
76
+ typeGuard('opfs', 'OPFS is required for this feature');
77
+
78
+ // Assert Navigation API support before routing
79
+ typeGuard('navigationAPI', 'Navigation API is required for routing');
80
+
81
+ // Custom error message
82
+ try {
83
+ typeGuard('viewTransitions', 'View transitions are required for page transitions');
84
+ document.startViewTransition(() => updateDOM());
85
+ } catch (err) {
86
+ // Fallback for browsers without view transitions
87
+ updateDOM();
88
+ }
89
+ ```
90
+
91
+ ---
92
+
93
+ ## 3. Dynamic Feature Guards (`guard`)
94
+
95
+ Asynchronous wrappers that transparently route calls to native browser APIs (if present) or automatically load, bootstrap, and install the lightweight polyfills on demand.
96
+
97
+ | Guard Method | Returns | Action / Polyfill Fallback |
98
+ | --- | --- | --- |
99
+ | `await guard.urlPattern()` | `URLPattern` Class | Returns spec-compliant pathname matching template class |
100
+ | `await guard.navigation()` | `navigation` Object | Bootstraps the Single Page Application Navigation API polyfill |
101
+ | `await guard.popover()` | `undefined` | Installs HTMLElement popover prototype methods & light dismiss |
102
+ | `await guard.shadow(root)` | `undefined` | Applies polyfill parser for Declarative Shadow DOM template nodes |
103
+ | `await guard.anchor(float, anchor, opts)` | `undefined` | Computes high-performance dynamic anchor positioning fallback |
104
+ | `await guard.sanitizer()` | Sanitizer Wrapper | Exposes uniform `.sanitizeToString(html)` helper |
105
+ | `await guard.scheduler()` | `scheduler` Object | Returns priority-aware task execution queues |
106
+ | `await guard.yield()` | `Promise<undefined>` | Yields execution thread control back to the event loop |
107
+
108
+ ---
109
+
110
+ ## 4. Prioritized Task Scheduler
111
+
112
+ When native `globalThis.scheduler` is absent, the polyfill exposes a robust microtask/macrotask priority queue system supporting three priority levels (`user-blocking`, `user-visible`, `background`), delayed scheduling, and task cancellation via `AbortSignal`.
113
+
114
+ ### Prioritized Task Enqueuing
115
+
116
+ ```javascript
117
+ import { guard } from '@adukiorg/anza/platform';
118
+
119
+ const scheduler = await guard.scheduler();
120
+
121
+ // 1. High priority UI rendering task
122
+ scheduler.postTask(() => {
123
+ renderCriticalInteractiveUI();
124
+ }, { priority: 'user-blocking' });
125
+
126
+ // 2. Default priority application logic task
127
+ scheduler.postTask(() => {
128
+ fetchFreshStateUpdates();
129
+ }, { priority: 'user-visible' });
130
+
131
+ // 3. Low priority telemetry sync task
132
+ scheduler.postTask(() => {
133
+ flushTelemetryLogs();
134
+ }, { priority: 'background' });
135
+ ```
136
+
137
+ ### Delayed Task Execution
138
+
139
+ ```javascript
140
+ // Schedule a default priority task to run after 200ms
141
+ scheduler.postTask(() => {
142
+ lazyLoadImagePreviews();
143
+ }, { delay: 200 });
144
+ ```
145
+
146
+ ### Task Cancellation via AbortSignal
147
+
148
+ ```javascript
149
+ const controller = new AbortController();
150
+
151
+ scheduler.postTask(() => {
152
+ console.log('This will not execute if aborted in time!');
153
+ }, { signal: controller.signal }).catch(err => {
154
+ if (err.name === 'AbortError') {
155
+ console.log('Task successfully cancelled!');
156
+ }
157
+ });
158
+
159
+ // Abort the controller
160
+ controller.abort();
161
+ ```
162
+
163
+ ### Dynamic Event Loop Yielding
164
+
165
+ Use `guard.yield()` inside complex, long-running loops to split execution into distinct chunks, preventing browser UI freezes and maintaining a 60fps frame rate.
166
+
167
+ ```javascript
168
+ import { guard } from '@adukiorg/anza/platform';
169
+
170
+ async function processMassiveDataset(items) {
171
+ for (let idx = 0; idx < items.length; idx++) {
172
+ computeHeavyItem(items[idx]);
173
+
174
+ // Yield execution control back to the event loop every 50 operations
175
+ if (idx % 50 === 0) {
176
+ await guard.yield();
177
+ }
178
+ }
179
+ }
180
+ ```
181
+
182
+ ---
183
+
184
+ ## 5. Leak-Proof Popover API
185
+
186
+ Provides native popover styling (`fixed` viewport positioning, high-tier top-layer simulations), light dismiss clicks, and complete DOM unmount safety to avoid memory leaks.
187
+
188
+ ### Declarative Markup Integration
189
+
190
+ ```html
191
+ <!-- Trigger Button -->
192
+ <button popovertarget="dropdown-menu" popovertargetaction="toggle">
193
+ Open Dropdown
194
+ </button>
195
+
196
+ <!-- Popover Container Element -->
197
+ <div id="dropdown-menu" popover="auto">
198
+ <h3>Dropdown Options</h3>
199
+ <ul>
200
+ <li><a href="/profile">Profile</a></li>
201
+ <li><a href="/settings">Settings</a></li>
202
+ </ul>
203
+ </div>
204
+ ```
205
+
206
+ ### Programmatic API Control
207
+
208
+ ```javascript
209
+ const menu = document.getElementById('dropdown-menu');
210
+
211
+ // Explicitly show the dropdown
212
+ menu.showPopover();
213
+
214
+ // Explicitly hide the dropdown
215
+ menu.hidePopover();
216
+
217
+ // Toggle visibility state
218
+ menu.togglePopover();
219
+ ```
220
+
221
+ ### Memory Safety & Automated Garbage Collection
222
+
223
+ When a popover is declared with `popover="auto"` or `popover=""`, standard polyfills register global document-level click listener closures, which can trigger severe memory leaks when components are removed or unmounted.
224
+
225
+ Our polyfill leverages an internal `MutationObserver` on `document.body` to track DOM unmount actions. If a popover element is programmatically removed from the document while open, it **automatically invokes hidePopover(), detaches its global click listeners, and disconnects all MutationObservers**, guaranteeing zero memory leaks:
226
+
227
+ ```javascript
228
+ const userMenu = document.createElement('div');
229
+ userMenu.setAttribute('popover', 'auto');
230
+ document.body.appendChild(userMenu);
231
+
232
+ userMenu.showPopover();
233
+
234
+ // Programmatically unmounted from the DOM
235
+ userMenu.remove(); // Internally cleans up all global event listeners automatically!
236
+ ```
237
+
238
+ ---
239
+
240
+ ## 6. Trailing-Slash Resilient URLPattern
241
+
242
+ Spec-compliant URLPattern pathname parser that extracts named parameters and wildcards, Normalizing trailing slashes seamlessly.
243
+
244
+ ### Segment Parameter & Wildcard Matching
245
+
246
+ ```javascript
247
+ import { guard } from '@adukiorg/anza/platform';
248
+
249
+ const URLPattern = await guard.urlPattern();
250
+
251
+ // Template matching both '/posts/2026/my-first-post' and '/posts/2026/my-first-post/'
252
+ const pattern = new URLPattern({ pathname: '/posts/:year/:slug' });
253
+
254
+ const result = pattern.exec('https://example.com/posts/2026/my-first-post/');
255
+ if (result) {
256
+ const { year, slug } = result.pathname.groups;
257
+ console.log(`Year: ${year}, Slug: ${slug}`);
258
+ // Output: Year: 2026, Slug: my-first-post
259
+ }
260
+ ```
261
+
262
+ ### Wildcard Segment Extraction
263
+
264
+ Wildcards are parsed sequentially into indexed string parameter names starting at `'0'`:
265
+
266
+ ```javascript
267
+ const filesPattern = new URLPattern({ pathname: '/docs/*' });
268
+ const match = filesPattern.exec('/docs/api/platform/usage.pdf');
269
+
270
+ if (match) {
271
+ const filePath = match.pathname.groups['0'];
272
+ console.log(`File Path: ${filePath}`);
273
+ // Output: File Path: api/platform/usage.pdf
274
+ }
275
+ ```
276
+
277
+ ### Parameter Modifiers
278
+
279
+ The polyfill supports `?` (optional), `*` (zero-or-more), and `+` (one-or-more) named modifiers:
280
+
281
+ ```javascript
282
+ // Match optional member profiles: /member or /member/fescii
283
+ const pattern = new URLPattern({ pathname: '/member/:username?' });
284
+
285
+ const match = pattern.exec('/member/');
286
+ console.log(match.pathname.groups.username); // Output: ""
287
+ ```
@@ -0,0 +1,95 @@
1
+ /**
2
+ * src/core/router/cache.js
3
+ *
4
+ * Internal route cache backed by the browser Cache API. The router uses this to
5
+ * prefetch and store view assets / lazily-loaded module responses keyed by URL,
6
+ * so revisiting or anticipating a route serves from cache instead of the network.
7
+ *
8
+ * Uses the same `x-expires-at` TTL convention as storage and api caches.
9
+ */
10
+
11
+ const NAME = 'router-cache';
12
+ const DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes
13
+
14
+ async function open() {
15
+ if (typeof caches === 'undefined') return null;
16
+ return caches.open(NAME);
17
+ }
18
+
19
+ function key(url) {
20
+ return new Request(new URL(url, globalThis.location?.href || 'http://localhost').href);
21
+ }
22
+
23
+ /**
24
+ * Read a cached response for a route URL, honoring TTL. Returns `null` on miss
25
+ * or when the Cache API is unavailable.
26
+ */
27
+ export async function get(url) {
28
+ const store = await open();
29
+ if (!store) return null;
30
+
31
+ const req = key(url);
32
+ const res = await store.match(req);
33
+ if (!res) return null;
34
+
35
+ const expires = res.headers.get('x-expires-at');
36
+ if (expires && Date.now() > Number(expires)) {
37
+ await store.delete(req);
38
+ return null;
39
+ }
40
+ return res.clone();
41
+ }
42
+
43
+ /**
44
+ * Store a response for a route URL with an optional TTL (ms).
45
+ */
46
+ export async function set(url, response, ttl = DEFAULT_TTL) {
47
+ const store = await open();
48
+ if (!store || !response || response.type === 'opaque') return;
49
+
50
+ const headers = new Headers(response.headers);
51
+ if (ttl) headers.set('x-expires-at', String(Date.now() + ttl));
52
+
53
+ const body = response.body ? response.clone().body : null;
54
+ const stamped = new Response(body, {
55
+ status: response.status,
56
+ statusText: response.statusText,
57
+ headers
58
+ });
59
+ await store.put(key(url), stamped);
60
+ }
61
+
62
+ /**
63
+ * Prefetch a route URL into the cache. Returns the cached `Response` (or `null`).
64
+ * Use on link hover / when a view becomes visible to make navigation instant.
65
+ */
66
+ export async function prefetch(url, { ttl = DEFAULT_TTL, signal } = {}) {
67
+ const existing = await get(url);
68
+ if (existing) return existing;
69
+
70
+ try {
71
+ const res = await fetch(key(url), { signal });
72
+ if (res && res.ok) {
73
+ await set(url, res, ttl);
74
+ return res.clone();
75
+ }
76
+ } catch {
77
+ // Network failure during prefetch is non-fatal.
78
+ }
79
+ return null;
80
+ }
81
+
82
+ /**
83
+ * Remove a single URL, or clear the entire route cache when called with no args.
84
+ */
85
+ export async function purge(url) {
86
+ const store = await open();
87
+ if (!store) return;
88
+ if (url) {
89
+ await store.delete(key(url));
90
+ } else if (typeof caches !== 'undefined') {
91
+ await caches.delete(NAME);
92
+ }
93
+ }
94
+
95
+ export const cache = { get, set, prefetch, purge };