@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,644 @@
1
+ const rootHooks = new WeakMap();
2
+
3
+ function asArray(value) {
4
+ return Array.isArray(value) ? value : [value];
5
+ }
6
+
7
+ function isAbortSignal(value) {
8
+ return Boolean(
9
+ value &&
10
+ typeof value === 'object' &&
11
+ typeof value.aborted === 'boolean' &&
12
+ typeof value.addEventListener === 'function'
13
+ );
14
+ }
15
+
16
+ function normalizeSignalOptions(value, defaultSignal) {
17
+ if (isAbortSignal(value)) {
18
+ return { signal: value };
19
+ }
20
+
21
+ if (value && typeof value === 'object') {
22
+ return {
23
+ ...value,
24
+ signal: value.signal || defaultSignal
25
+ };
26
+ }
27
+
28
+ return { signal: defaultSignal };
29
+ }
30
+
31
+ function escapeAttrValue(value) {
32
+ if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
33
+ return CSS.escape(value);
34
+ }
35
+ return String(value).replace(/"/g, '\\"');
36
+ }
37
+
38
+ function warn(message) {
39
+ if (typeof console !== 'undefined') {
40
+ console.warn(message);
41
+ }
42
+ }
43
+
44
+ function getEventElement(event) {
45
+ if (event.target?.nodeType === Node.ELEMENT_NODE) {
46
+ return event.target;
47
+ }
48
+
49
+ const path = typeof event.composedPath === 'function' ? event.composedPath() : [];
50
+ for (const node of path) {
51
+ if (node?.nodeType === Node.ELEMENT_NODE) {
52
+ return node;
53
+ }
54
+ }
55
+
56
+ return null;
57
+ }
58
+
59
+ function safeClosest(start, selector) {
60
+ try {
61
+ return start?.closest?.(selector) || null;
62
+ } catch (err) {
63
+ warn(`[Native UI] Invalid delegated event selector "${selector}": ${err.message}`);
64
+ return null;
65
+ }
66
+ }
67
+
68
+ function isElement(value) {
69
+ return value?.nodeType === Node.ELEMENT_NODE;
70
+ }
71
+
72
+ function resolveTargets(shadowRoot, target) {
73
+ if (typeof target === 'string') {
74
+ try {
75
+ return {
76
+ selector: target,
77
+ targets: Array.from(shadowRoot.querySelectorAll(target))
78
+ };
79
+ } catch (err) {
80
+ warn(`[Native UI] Invalid watch selector "${target}": ${err.message}`);
81
+ return null;
82
+ }
83
+ }
84
+
85
+ if (isElement(target)) {
86
+ if (!shadowRoot.contains(target)) {
87
+ throw new Error('[Native UI] WatchError: direct watch target is outside this component shadow root.');
88
+ }
89
+ return {
90
+ selector: null,
91
+ targets: [target]
92
+ };
93
+ }
94
+
95
+ warn('[Native UI] Watch target must be a selector string or Element reference.');
96
+ return null;
97
+ }
98
+
99
+ function isInside(target, root) {
100
+ return target === root || root.contains(target);
101
+ }
102
+
103
+ function matchesTarget(recordTarget, reg) {
104
+ if (!isElement(recordTarget)) return false;
105
+
106
+ if (reg.selector) {
107
+ try {
108
+ return recordTarget.matches(reg.selector) && reg.shadowRoot.contains(recordTarget);
109
+ } catch {
110
+ return false;
111
+ }
112
+ }
113
+
114
+ return reg.targets.some(target => recordTarget === target);
115
+ }
116
+
117
+ function matchesTargetOrSubtree(recordTarget, reg) {
118
+ if (!isElement(recordTarget)) return false;
119
+
120
+ if (reg.selector) {
121
+ try {
122
+ const match = recordTarget.matches(reg.selector)
123
+ ? recordTarget
124
+ : recordTarget.closest(reg.selector);
125
+ return Boolean(match && reg.shadowRoot.contains(match));
126
+ } catch {
127
+ return false;
128
+ }
129
+ }
130
+
131
+ return reg.targets.some(target => isInside(recordTarget, target));
132
+ }
133
+
134
+ /**
135
+ * TagsCache provides a fast, cached query interface for Shadow DOM elements.
136
+ * Pre-warmed at mount using the Rust-generated JSON descriptor.
137
+ */
138
+ export class TagsCache {
139
+ constructor(shadowRoot) {
140
+ this.root = shadowRoot;
141
+ this.oneCache = new Map();
142
+ this.allCache = new Map();
143
+ }
144
+
145
+ one(selector) {
146
+ if (!this.oneCache.has(selector)) {
147
+ this.oneCache.set(selector, this.root.querySelector(selector));
148
+ }
149
+ return this.oneCache.get(selector);
150
+ }
151
+
152
+ all(selector) {
153
+ if (!this.allCache.has(selector)) {
154
+ this.allCache.set(selector, Array.from(this.root.querySelectorAll(selector)));
155
+ }
156
+ return this.allCache.get(selector);
157
+ }
158
+
159
+ each(selector, fn) {
160
+ const items = this.all(selector);
161
+ for (let i = 0; i < items.length; i++) {
162
+ fn(items[i], i);
163
+ }
164
+ }
165
+
166
+ has(selector) {
167
+ return this.one(selector) !== null;
168
+ }
169
+
170
+ clear() {
171
+ this.oneCache.clear();
172
+ this.allCache.clear();
173
+ }
174
+
175
+ prewarmId(id) {
176
+ const el = typeof this.root.getElementById === 'function'
177
+ ? this.root.getElementById(id)
178
+ : this.root.querySelector(`#${escapeAttrValue(id)}`);
179
+ if (el) {
180
+ this.oneCache.set(`#${id}`, el);
181
+ }
182
+ }
183
+
184
+ prewarm(selector, element) {
185
+ if (element) {
186
+ this.oneCache.set(selector, element);
187
+ }
188
+ }
189
+ }
190
+
191
+ export function createRefs(shadowRoot, descriptor) {
192
+ const refs = Object.create(null);
193
+ const names = Array.isArray(descriptor?.refs) ? descriptor.refs : null;
194
+ const nameSet = names ? new Set(names) : null;
195
+
196
+ const all = shadowRoot.querySelectorAll('[ref]');
197
+ for (const node of all) {
198
+ const name = node.getAttribute('ref');
199
+ if (!name) continue;
200
+ if (nameSet && !nameSet.has(name)) continue;
201
+
202
+ if (refs[name]) {
203
+ warn(`[Native UI] Duplicate ref "${name}" found. Using the first match.`);
204
+ } else {
205
+ refs[name] = node;
206
+ }
207
+ }
208
+
209
+ return Object.freeze(refs);
210
+ }
211
+
212
+ export function prewarmTags(tags, refs, descriptor) {
213
+ for (const id of descriptor?.ids || []) {
214
+ tags.prewarmId(id);
215
+ }
216
+
217
+ for (const [name, element] of Object.entries(refs)) {
218
+ tags.prewarm(`[ref="${escapeAttrValue(name)}"]`, element);
219
+ }
220
+ }
221
+
222
+ export function installInvalidationHooks(shadowRoot, tags) {
223
+ // Use a simple, native MutationObserver to clear tags cache when children change (R-07)
224
+ const observer = new MutationObserver(() => {
225
+ tags.clear();
226
+ });
227
+ observer.observe(shadowRoot, { childList: true, subtree: false });
228
+ return () => observer.disconnect();
229
+ }
230
+
231
+ /**
232
+ * Creates the `on` delegated event proxy.
233
+ * Example: on.click('.btn', (e, target) => { ... })
234
+ */
235
+ export function createEventDelegator(shadowRoot, defaultSignal) {
236
+ const registries = new Map();
237
+ const listeners = new Map();
238
+ let nextId = 0;
239
+
240
+ function listenerKey(eventType, capture) {
241
+ return `${String(eventType)}:${capture ? 'capture' : 'bubble'}`;
242
+ }
243
+
244
+ function isPassiveRequired(key) {
245
+ const registry = registries.get(key);
246
+ if (!registry || registry.size === 0) return true;
247
+ for (const reg of registry.values()) {
248
+ if (reg.passive === false) return false;
249
+ }
250
+ return true;
251
+ }
252
+
253
+ function ensureListener(eventType, capture) {
254
+ const key = listenerKey(eventType, capture);
255
+ const currentPassive = isPassiveRequired(key);
256
+
257
+ // Check if we already have a registered listener with the exact same passive configuration (R-02)
258
+ const active = listeners.get(key);
259
+ if (active) {
260
+ if (active.passive === currentPassive) return;
261
+ shadowRoot.removeEventListener(eventType, active.handler, { capture });
262
+ listeners.delete(key);
263
+ }
264
+
265
+ const rootListener = (event) => {
266
+ if (defaultSignal?.aborted) return;
267
+ const registry = registries.get(key);
268
+ if (!registry?.size) return;
269
+
270
+ const start = getEventElement(event);
271
+ if (!start) return;
272
+
273
+ for (const reg of Array.from(registry.values())) {
274
+ if (reg.signal?.aborted) {
275
+ remove(key, reg.id);
276
+ continue;
277
+ }
278
+
279
+ const match = safeClosest(start, reg.selector);
280
+ if (!match || !shadowRoot.contains(match)) continue;
281
+
282
+ reg.handler(event, match);
283
+ if (reg.once) remove(key, reg.id);
284
+ }
285
+ };
286
+
287
+ shadowRoot.addEventListener(eventType, rootListener, {
288
+ signal: defaultSignal,
289
+ capture,
290
+ passive: currentPassive
291
+ });
292
+ listeners.set(key, { handler: rootListener, passive: currentPassive });
293
+ }
294
+
295
+ function remove(key, id) {
296
+ registries.get(key)?.delete(id);
297
+ }
298
+
299
+ function add(eventType, selector, handler, signalOrOptions, once = false) {
300
+ if (typeof selector !== 'string' || typeof handler !== 'function') {
301
+ warn(`[Native UI] on.${String(eventType)} requires a selector and handler.`);
302
+ return () => {};
303
+ }
304
+
305
+ const options = normalizeSignalOptions(signalOrOptions, defaultSignal);
306
+ const capture = Boolean(options.capture);
307
+ const key = listenerKey(eventType, capture);
308
+ const id = ++nextId;
309
+ const signal = options.signal;
310
+
311
+ if (signal?.aborted) return () => {};
312
+
313
+ // Default to true (passive: true) unless options.passive is explicitly false (R-02)
314
+ const passive = options.passive !== false;
315
+
316
+ if (!registries.has(key)) registries.set(key, new Map());
317
+
318
+ registries.get(key).set(id, {
319
+ id,
320
+ selector,
321
+ handler,
322
+ signal,
323
+ once: once || Boolean(options.once),
324
+ passive
325
+ });
326
+
327
+ ensureListener(eventType, capture);
328
+
329
+ const abortDispose = () => {
330
+ remove(key, id);
331
+ ensureListener(eventType, capture);
332
+ };
333
+ signal?.addEventListener('abort', abortDispose, { once: true });
334
+
335
+ const dispose = () => {
336
+ remove(key, id);
337
+ ensureListener(eventType, capture);
338
+ signal?.removeEventListener('abort', abortDispose);
339
+ };
340
+ return dispose;
341
+ }
342
+
343
+ defaultSignal?.addEventListener('abort', () => {
344
+ registries.clear();
345
+ listeners.clear();
346
+ }, { once: true });
347
+
348
+ return new Proxy({}, {
349
+ get(_target, eventType) {
350
+ if (typeof eventType === 'symbol') return undefined;
351
+
352
+ const bind = (selector, handler, signalOrOptions) => (
353
+ add(eventType, selector, handler, signalOrOptions, false)
354
+ );
355
+ bind.once = (selector, handler, signalOrOptions) => (
356
+ add(eventType, selector, handler, signalOrOptions, true)
357
+ );
358
+ return bind;
359
+ }
360
+ });
361
+ }
362
+
363
+ export function createMutationWatcher(shadowRoot, defaultSignal) {
364
+ const registry = new Map();
365
+ let observer = null;
366
+ let nextId = 0;
367
+
368
+ function add(kind, args, once = false) {
369
+ if (defaultSignal?.aborted || typeof MutationObserver === 'undefined') {
370
+ return () => {};
371
+ }
372
+
373
+ const reg = normalizeWatchRegistration(kind, args, once);
374
+ if (!reg) return () => {};
375
+
376
+ registry.set(reg.id, reg);
377
+
378
+ const abortDispose = () => remove(reg.id);
379
+ reg.signal?.addEventListener('abort', abortDispose, { once: true });
380
+ refreshObserver();
381
+
382
+ const dispose = () => {
383
+ remove(reg.id);
384
+ reg.signal?.removeEventListener('abort', abortDispose);
385
+ };
386
+ return dispose;
387
+ }
388
+
389
+ function normalizeWatchRegistration(kind, args, once) {
390
+ const target = args[0];
391
+ const resolved = resolveTargets(shadowRoot, target);
392
+ if (!resolved) return null;
393
+
394
+ if (!resolved.targets.length && typeof target === 'string') {
395
+ warn(`[Native UI] watch.${kind} target "${target}" did not match any elements currently.`);
396
+ }
397
+
398
+ let attrs = null;
399
+ let deep = false;
400
+ let handler = null;
401
+ let signalOrOptions = null;
402
+
403
+ if (kind === 'attr') {
404
+ attrs = args[1] === '*' ? '*' : new Set(asArray(args[1]).filter(Boolean));
405
+ handler = args[2];
406
+ signalOrOptions = args[3];
407
+ if (attrs !== '*' && attrs.size === 0) {
408
+ warn('[Native UI] watch.attr requires at least one attribute name or "*".');
409
+ return null;
410
+ }
411
+ } else if (kind === 'kids') {
412
+ if (typeof args[1] === 'function') {
413
+ handler = args[1];
414
+ signalOrOptions = args[2];
415
+ } else {
416
+ deep = Boolean(args[1]?.deep);
417
+ handler = args[2];
418
+ signalOrOptions = args[3];
419
+ }
420
+ } else {
421
+ handler = args[1];
422
+ signalOrOptions = args[2];
423
+ }
424
+
425
+ if (typeof handler !== 'function') {
426
+ warn(`[Native UI] watch.${kind} requires a handler function.`);
427
+ return null;
428
+ }
429
+
430
+ const options = normalizeSignalOptions(signalOrOptions, defaultSignal);
431
+ if (options.signal?.aborted) return null;
432
+
433
+ return {
434
+ id: ++nextId,
435
+ kind,
436
+ shadowRoot,
437
+ selector: resolved.selector,
438
+ targets: resolved.targets,
439
+ attrs,
440
+ deep,
441
+ handler,
442
+ once: once || Boolean(options.once),
443
+ signal: options.signal
444
+ };
445
+ }
446
+
447
+ function remove(id) {
448
+ if (!registry.delete(id)) return;
449
+ refreshObserver();
450
+ }
451
+
452
+ function refreshObserver() {
453
+ observer?.disconnect();
454
+ if (!registry.size || defaultSignal?.aborted) return;
455
+
456
+ observer ||= new MutationObserver(dispatch);
457
+
458
+ // R-04: Only watch shadow root subtree if selector targets are specified.
459
+ // If target is direct element references, we observe them directly with subtree: false
460
+ let needsSubtree = false;
461
+ for (const reg of registry.values()) {
462
+ if (reg.selector) {
463
+ needsSubtree = true;
464
+ break;
465
+ }
466
+ }
467
+
468
+ const options = computeWatchOptions(needsSubtree);
469
+
470
+ if (needsSubtree) {
471
+ observer.observe(shadowRoot, options);
472
+ } else {
473
+ const seen = new Set();
474
+ for (const reg of registry.values()) {
475
+ for (const target of reg.targets) {
476
+ if (!seen.has(target)) {
477
+ seen.add(target);
478
+ observer.observe(target, options);
479
+ }
480
+ }
481
+ }
482
+ }
483
+ }
484
+
485
+ function computeWatchOptions(needsSubtree) {
486
+ let hasAttr = false;
487
+ let hasAllAttrs = false;
488
+ let hasKids = false;
489
+ let hasText = false;
490
+ let hasTree = false;
491
+ let hasDeepKids = false;
492
+ const attrs = new Set();
493
+
494
+ for (const reg of registry.values()) {
495
+ if (reg.kind === 'tree') hasTree = true;
496
+ if (reg.kind === 'kids') {
497
+ hasKids = true;
498
+ if (reg.deep) hasDeepKids = true;
499
+ }
500
+ if (reg.kind === 'text') hasText = true;
501
+ if (reg.kind === 'attr') {
502
+ hasAttr = true;
503
+ if (reg.attrs === '*') {
504
+ hasAllAttrs = true;
505
+ } else {
506
+ for (const attr of reg.attrs) attrs.add(attr);
507
+ }
508
+ }
509
+ }
510
+
511
+ const shouldObserveAttrs = hasAttr || hasTree;
512
+ const options = {
513
+ attributes: shouldObserveAttrs,
514
+ attributeOldValue: shouldObserveAttrs,
515
+ childList: hasKids || hasTree,
516
+ characterData: hasText || hasTree,
517
+ characterDataOldValue: hasText || hasTree,
518
+ subtree: needsSubtree || hasTree || hasDeepKids || hasText
519
+ };
520
+
521
+ if (shouldObserveAttrs && !hasTree && !hasAllAttrs && attrs.size > 0) {
522
+ options.attributeFilter = Array.from(attrs);
523
+ }
524
+
525
+ return options;
526
+ }
527
+
528
+ function dispatch(records) {
529
+ if (defaultSignal?.aborted) return;
530
+
531
+ for (const reg of Array.from(registry.values())) {
532
+ if (reg.signal?.aborted) {
533
+ remove(reg.id);
534
+ continue;
535
+ }
536
+
537
+ const matches = filterWatchRecords(records, reg);
538
+ if (!matches.length) continue;
539
+
540
+ callWatchHandler(matches, reg);
541
+ if (reg.once) remove(reg.id);
542
+ }
543
+ }
544
+
545
+ defaultSignal?.addEventListener('abort', () => {
546
+ observer?.disconnect();
547
+ registry.clear();
548
+ }, { once: true });
549
+
550
+ return createWatchProxy(add);
551
+ }
552
+
553
+ function createWatchProxy(add) {
554
+ const methods = Object.create(null);
555
+ for (const kind of ['attr', 'kids', 'text', 'tree']) {
556
+ const method = (...args) => add(kind, args, false);
557
+ method.once = (...args) => add(kind, args, true);
558
+ methods[kind] = method;
559
+ }
560
+ return Object.freeze(methods);
561
+ }
562
+
563
+ function filterWatchRecords(records, reg) {
564
+ if (reg.kind === 'tree') {
565
+ return records.filter(record => {
566
+ const target = record.target.nodeType === Node.TEXT_NODE
567
+ ? record.target.parentElement
568
+ : record.target;
569
+ return matchesTargetOrSubtree(target, reg);
570
+ });
571
+ }
572
+
573
+ if (reg.kind === 'attr') {
574
+ return records.filter(record => (
575
+ record.type === 'attributes' &&
576
+ matchesTarget(record.target, reg) &&
577
+ (reg.attrs === '*' || reg.attrs.has(record.attributeName))
578
+ ));
579
+ }
580
+
581
+ if (reg.kind === 'kids') {
582
+ return records.filter(record => (
583
+ record.type === 'childList' &&
584
+ (reg.deep ? matchesTargetOrSubtree(record.target, reg) : matchesTarget(record.target, reg))
585
+ ));
586
+ }
587
+
588
+ if (reg.kind === 'text') {
589
+ return records.filter(record => {
590
+ if (record.type !== 'characterData') return false;
591
+ return matchesTargetOrSubtree(record.target.parentElement, reg);
592
+ });
593
+ }
594
+
595
+ return [];
596
+ }
597
+
598
+ function callWatchHandler(records, reg) {
599
+ if (reg.kind === 'tree') {
600
+ const target = reg.targets[0] || null;
601
+ reg.handler(records, target);
602
+ return;
603
+ }
604
+
605
+ for (const record of records) {
606
+ if (reg.kind === 'attr') {
607
+ reg.handler(
608
+ record.attributeName,
609
+ record.target.getAttribute(record.attributeName),
610
+ record.oldValue,
611
+ record.target
612
+ );
613
+ } else if (reg.kind === 'kids') {
614
+ reg.handler({
615
+ added: Array.from(record.addedNodes),
616
+ removed: Array.from(record.removedNodes)
617
+ }, record.target);
618
+ } else if (reg.kind === 'text') {
619
+ const element = record.target.parentElement;
620
+ reg.handler(element?.textContent ?? '', record.oldValue, element);
621
+ }
622
+ }
623
+ }
624
+
625
+ export function createComponentContext({ el, shadowRoot, ctrl, descriptor, internals }) {
626
+ const tags = new TagsCache(shadowRoot);
627
+ const refs = createRefs(shadowRoot, descriptor);
628
+ const on = createEventDelegator(shadowRoot, ctrl?.signal);
629
+ const watch = createMutationWatcher(shadowRoot, ctrl?.signal);
630
+ const disposeInvalidationHooks = installInvalidationHooks(shadowRoot, tags);
631
+
632
+ prewarmTags(tags, refs, descriptor);
633
+ ctrl?.signal?.addEventListener('abort', disposeInvalidationHooks, { once: true });
634
+
635
+ return Object.freeze({
636
+ el,
637
+ ctrl,
638
+ tags,
639
+ on,
640
+ refs,
641
+ watch,
642
+ internals
643
+ });
644
+ }
@@ -0,0 +1,6 @@
1
+ export const specRegistry = new Map();
2
+ export const assetCache = new Map();
3
+ export const internalsMap = new WeakMap();
4
+ export const initializedMap = new WeakMap();
5
+ export const pendingUpdatesMap = new WeakMap();
6
+ export const updateScheduledMap = new WeakMap();