@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,390 @@
1
+ import { BaseElement } from '../base.js';
2
+ import { scheduleFrame } from '../schedule.js';
3
+ import { router } from '../../router/index.js';
4
+ import { specRegistry, internalsMap, initializedMap, pendingUpdatesMap, updateScheduledMap } from './state.js';
5
+ import { preloadResources } from './utils.js';
6
+ import { createComponentContext } from './proxy.js';
7
+
8
+ // Platform lifecycle method names that must never be overridden by spec.methods.
9
+ const RESERVED = new Set([
10
+ 'connectedCallback', 'disconnectedCallback', 'attributeChangedCallback',
11
+ 'adoptedCallback', 'formAssociatedCallback', 'formDisabledCallback',
12
+ 'formResetCallback', 'formStateRestoreCallback',
13
+ 'mount', 'unmount', 'constructor'
14
+ ]);
15
+
16
+ /**
17
+ * High-performance declarative element factory.
18
+ */
19
+ export function element(tag, spec, base) {
20
+ if (typeof customElements === 'undefined') return;
21
+ if (customElements.get(tag)) {
22
+ console.warn(`Declarative Element "${tag}" is already defined. Skipping.`);
23
+ return;
24
+ }
25
+
26
+ // Normalize prop shorthand (e.g. `count: 0`) into full configs before use.
27
+ if (spec.props) {
28
+ spec.props = normalizeProps(spec.props);
29
+ }
30
+
31
+ // Cache element spec for automated layout orchestration and diffing
32
+ specRegistry.set(tag.toLowerCase(), spec);
33
+
34
+ // Automatically register elements with the route matcher if a url pattern is specified
35
+ if (spec.url) {
36
+ const meta = { ...spec.meta, container: spec.container };
37
+ router.register(spec.url, tag, meta);
38
+ }
39
+
40
+ // Define properties to watch and their Symbol backing store keys (R-01)
41
+ const propKeys = spec.props ? Object.keys(spec.props) : [];
42
+ const observedAttrs = propKeys.map(k => k.toLowerCase());
43
+ const store = {};
44
+ for (const key of propKeys) {
45
+ store[key] = Symbol(key);
46
+ }
47
+
48
+ // Warn loudly when a relative asset is given without a base — otherwise the
49
+ // resource would silently fail to load (U2).
50
+ warnMissingBase(tag, 'style', spec.style, base);
51
+ warnMissingBase(tag, 'template', spec.template, base);
52
+
53
+ // Resolve absolute URLs relative to import.meta.url (base)
54
+ const styleUrl = spec.style && base && (spec.style.endsWith('.css') || spec.style.startsWith('./') || spec.style.startsWith('/'))
55
+ ? new URL(spec.style, base).href
56
+ : null;
57
+ const templateUrl = spec.template && base && (spec.template.endsWith('.html') || spec.template.startsWith('./') || spec.template.startsWith('/'))
58
+ ? new URL(spec.template, base).href
59
+ : null;
60
+
61
+ // Initiate resource fetching exactly once per component registration (R-06)
62
+ let resolved = null;
63
+ const resourcesPromise = preloadResources(tag, styleUrl, templateUrl, spec.template, spec.style).then(res => {
64
+ resolved = res;
65
+ return res;
66
+ });
67
+
68
+ // Handle hot reloading of constructable stylesheets (one global listener per unique styleUrl - R-05)
69
+ if (styleUrl && typeof window !== 'undefined') {
70
+ if (!window.__native_hmr_listeners__) {
71
+ window.__native_hmr_listeners__ = new Set();
72
+ }
73
+ if (!window.__native_hmr_listeners__.has(styleUrl)) {
74
+ window.__native_hmr_listeners__.add(styleUrl);
75
+ const hmrHandler = async (e) => {
76
+ const { path: changedPath, css } = e.detail;
77
+ const absoluteChangedUrl = new URL(changedPath, window.location.origin).href;
78
+
79
+ if (styleUrl === absoluteChangedUrl || styleUrl.endsWith(changedPath)) {
80
+ const res = await resourcesPromise;
81
+ if (res.stylesheet) {
82
+ res.stylesheet.replaceSync(css);
83
+ console.log(`[HMR] Shared AdoptedStyleSheet hot-swapped for <${tag}>`);
84
+ }
85
+ }
86
+ };
87
+ window.addEventListener('native:hmr:css', hmrHandler);
88
+ }
89
+ }
90
+
91
+ class DeclarativeElement extends BaseElement {
92
+ static observedAttributes = observedAttrs;
93
+
94
+ constructor() {
95
+ super();
96
+ this.attachShadow({ mode: spec.mode || 'open' });
97
+
98
+ initializedMap.set(this, false);
99
+ pendingUpdatesMap.set(this, new Map());
100
+ updateScheduledMap.set(this, false);
101
+
102
+ if (spec.form) {
103
+ const internals = this.attachInternals();
104
+ internalsMap.set(this, internals);
105
+ }
106
+
107
+ // Initialize default properties backing store dynamically (R-01)
108
+ if (spec.props) {
109
+ for (const [key, config] of Object.entries(spec.props)) {
110
+ const sym = store[key];
111
+ const attrName = key.toLowerCase();
112
+ let initial = config.default;
113
+ if (config.type === Boolean) {
114
+ initial = this.hasAttribute(attrName);
115
+ } else {
116
+ const attrVal = this.getAttribute(attrName);
117
+ if (attrVal !== null) {
118
+ initial = config.type === Number ? Number(attrVal) : attrVal;
119
+ }
120
+ }
121
+ this[sym] = initial ?? (config.type === Boolean ? false : null);
122
+ }
123
+ }
124
+ }
125
+
126
+ async connectedCallback() {
127
+ // AbortController bootstrap inside BaseElement
128
+ super.connectedCallback();
129
+
130
+ // Wait for resolved resources to compile (synchronously if already cached)
131
+ let res = resolved;
132
+ if (!res) {
133
+ res = await resourcesPromise;
134
+ }
135
+ const { templateNode, stylesheet, cssText, tagsDescriptor } = res;
136
+
137
+ if (!this.ctrl || this.ctrl.signal.aborted || !this.isConnected) {
138
+ return;
139
+ }
140
+
141
+ if (templateNode && this.shadowRoot.childNodes.length === 0) {
142
+ this.shadowRoot.appendChild(templateNode.cloneNode(true));
143
+ }
144
+
145
+ if (stylesheet) {
146
+ // Constructable stylesheets path
147
+ this.shadowRoot.adoptedStyleSheets = [stylesheet];
148
+ } else if (cssText) {
149
+ // Fallback: inject <style> for browsers without adoptedStyleSheets
150
+ const style = document.createElement('style');
151
+ style.textContent = cssText;
152
+ this.shadowRoot.prepend(style);
153
+ }
154
+
155
+ const context = createComponentContext({
156
+ el: this,
157
+ shadowRoot: this.shadowRoot,
158
+ ctrl: this.ctrl,
159
+ descriptor: tagsDescriptor,
160
+ internals: internalsMap.get(this)
161
+ });
162
+
163
+ this._ctx = context;
164
+ this._tags = context.tags;
165
+ this._on = context.on;
166
+ this._refs = context.refs;
167
+ this._watch = context.watch;
168
+
169
+ initializedMap.set(this, true);
170
+
171
+ // Mount hook trigger passed with unified AbortController signal
172
+ if (spec.mount) {
173
+ try {
174
+ const res = spec.mount(context);
175
+ if (res instanceof Promise) {
176
+ res.catch((err) => {
177
+ console.error('[Native UI] mount failed:', err);
178
+ });
179
+ }
180
+ } catch (err) {
181
+ console.error('[Native UI] mount failed:', err);
182
+ }
183
+ }
184
+ }
185
+
186
+ disconnectedCallback() {
187
+ if (spec.unmount) {
188
+ spec.unmount({
189
+ el: this,
190
+ tags: this._tags,
191
+ refs: this._refs,
192
+ watch: this._watch,
193
+ internals: internalsMap.get(this)
194
+ });
195
+ }
196
+ super.disconnectedCallback();
197
+ }
198
+
199
+ attributeChangedCallback(name, oldVal, newVal) {
200
+ if (oldVal === newVal) return;
201
+
202
+ // Find the camelCase property matching lowercase attribute name
203
+ const key = propKeys.find(k => k.toLowerCase() === name);
204
+ if (!key) return;
205
+
206
+ const config = spec.props[key];
207
+ let castedVal = newVal;
208
+
209
+ if (config.type === Boolean) {
210
+ castedVal = newVal !== null;
211
+ } else if (config.type === Number) {
212
+ castedVal = newVal !== null ? Number(newVal) : (config.default ?? 0);
213
+ }
214
+
215
+ // Sync property to trigger update callback and schedule updates
216
+ if (this[key] !== castedVal) {
217
+ this[key] = castedVal;
218
+ }
219
+ }
220
+
221
+ // Form-associated lifecycle callbacks — mapped from short spec hook names
222
+ formAssociatedCallback(form) {
223
+ spec.associated?.call(this, form);
224
+ }
225
+
226
+ formDisabledCallback(value) {
227
+ spec.disabled?.call(this, value);
228
+ }
229
+
230
+ formResetCallback() {
231
+ spec.reset?.call(this);
232
+ }
233
+
234
+ formStateRestoreCallback(state, mode) {
235
+ spec.restore?.call(this, state, mode);
236
+ }
237
+ }
238
+
239
+ // 1. Install spec.methods onto the prototype before define (§1.1)
240
+ if (spec.methods) {
241
+ for (const [name, fn] of Object.entries(spec.methods)) {
242
+ if (RESERVED.has(name) || name in DeclarativeElement.prototype) {
243
+ console.warn(`[Native UI] Method "${name}" conflicts with an existing or reserved name on <${tag}>. Skipping.`);
244
+ continue;
245
+ }
246
+
247
+ Object.defineProperty(DeclarativeElement.prototype, name, {
248
+ value: fn,
249
+ writable: false,
250
+ configurable: true,
251
+ enumerable: false
252
+ });
253
+ }
254
+ }
255
+
256
+ // 2. Generate type-safe getter/setters dynamically on class prototype (R-01)
257
+ for (const key of propKeys) {
258
+ const config = spec.props[key];
259
+ const attrName = key.toLowerCase();
260
+ const sym = store[key];
261
+ // reflect defaults to true for back-compatibility (§1.2)
262
+ const reflect = config.reflect !== false;
263
+
264
+ Object.defineProperty(DeclarativeElement.prototype, key, {
265
+ get() {
266
+ return this[sym];
267
+ },
268
+ set(val) {
269
+ const oldVal = this[sym];
270
+ if (oldVal === val) return;
271
+
272
+ this[sym] = val;
273
+
274
+ // Attribute reflection — only when reflect !== false (§1.2)
275
+ if (reflect) {
276
+ if (config.type === Boolean) {
277
+ if (val) {
278
+ if (!this.hasAttribute(attrName)) this.setAttribute(attrName, '');
279
+ } else {
280
+ if (this.hasAttribute(attrName)) this.removeAttribute(attrName);
281
+ }
282
+ } else if (val === null || val === undefined) {
283
+ if (this.hasAttribute(attrName)) this.removeAttribute(attrName);
284
+ } else {
285
+ const strVal = String(val);
286
+ if (this.getAttribute(attrName) !== strVal) {
287
+ this.setAttribute(attrName, strVal);
288
+ }
289
+ }
290
+ }
291
+
292
+ // Custom state synchronization (:state(name))
293
+ const internals = internalsMap.get(this);
294
+ if (config.state && internals?.states) {
295
+ if (val) {
296
+ internals.states.add(key);
297
+ } else {
298
+ internals.states.delete(key);
299
+ }
300
+ }
301
+
302
+ // Schedule batched updates via cooperative microtask scheduling
303
+ const initialized = initializedMap.get(this);
304
+ if (initialized && spec.update) {
305
+ const pendingUpdates = pendingUpdatesMap.get(this);
306
+ pendingUpdates.set(key, { val, old: oldVal });
307
+ if (!updateScheduledMap.get(this)) {
308
+ updateScheduledMap.set(this, true);
309
+
310
+ // Choose queueMicrotask or scheduleFrame depending on update.visual hint (R-03)
311
+ const flush = (spec.update.visual === true)
312
+ ? (fn) => scheduleFrame(fn)
313
+ : (fn) => queueMicrotask(fn);
314
+
315
+ flush(() => {
316
+ if (!this.ctrl || this.ctrl.signal.aborted || !this.isConnected) {
317
+ pendingUpdates.clear();
318
+ updateScheduledMap.set(this, false);
319
+ return;
320
+ }
321
+
322
+ const changes = Array.from(pendingUpdates.entries());
323
+ pendingUpdates.clear();
324
+ updateScheduledMap.set(this, false);
325
+
326
+ for (const [name, { val: v, old: o }] of changes) {
327
+ spec.update({
328
+ el: this,
329
+ ctrl: this.ctrl,
330
+ tags: this._tags,
331
+ on: this._on,
332
+ refs: this._refs,
333
+ watch: this._watch,
334
+ name,
335
+ val: v,
336
+ old: o,
337
+ prev: o
338
+ });
339
+ }
340
+ });
341
+ }
342
+ }
343
+ }
344
+ });
345
+ }
346
+
347
+ // 3. Form association
348
+ if (spec.form) {
349
+ Object.defineProperty(DeclarativeElement, 'formAssociated', {
350
+ value: true,
351
+ writable: false
352
+ });
353
+ }
354
+
355
+ // Define element globally
356
+ customElements.define(tag, DeclarativeElement);
357
+ }
358
+
359
+ /**
360
+ * Normalizes prop definitions, expanding shorthand literals into full configs.
361
+ * `count: 0` -> `count: { type: Number, default: 0 }`. Object configs that
362
+ * already declare `type` are passed through unchanged.
363
+ */
364
+ function normalizeProps(props) {
365
+ const out = {};
366
+ for (const [key, value] of Object.entries(props)) {
367
+ if (value && typeof value === 'object' && 'type' in value) {
368
+ out[key] = value;
369
+ continue;
370
+ }
371
+ let type = String;
372
+ if (typeof value === 'boolean') type = Boolean;
373
+ else if (typeof value === 'number') type = Number;
374
+ out[key] = { type, default: value };
375
+ }
376
+ return out;
377
+ }
378
+
379
+ /**
380
+ * Warns when a relative `template`/`style` is supplied without a base URL,
381
+ * which would otherwise silently fail to resolve.
382
+ */
383
+ function warnMissingBase(tag, field, value, base) {
384
+ if (!base && typeof value === 'string' && (value.startsWith('./') || value.startsWith('../'))) {
385
+ console.error(
386
+ `[Native UI] <${tag}> ${field} "${value}" is relative but no base was given. ` +
387
+ `Pass import.meta.url as the third argument to ui.element(...).`
388
+ );
389
+ }
390
+ }
@@ -0,0 +1,9 @@
1
+ import { define } from './define.js';
2
+ import { element } from './element.js';
3
+ import { container } from './container.js';
4
+ import { initOrchestrator } from './orchestrator.js';
5
+
6
+ // Initialize the global routing orchestrator
7
+ initOrchestrator();
8
+
9
+ export { define, element, container };
@@ -0,0 +1,105 @@
1
+ import { router } from '../../router/index.js';
2
+ import { specRegistry } from './state.js';
3
+
4
+ let dispose = null; // One-word module-level disposer variable (RT-11)
5
+
6
+ /**
7
+ * Initializes the global routing orchestrator.
8
+ * Listens for navigation found events and dynamically updates layout containers.
9
+ */
10
+ export function initOrchestrator() {
11
+ if (typeof window !== 'undefined') {
12
+ dispose?.();
13
+ dispose = router.on('found', async ({ tag, params, query, hash, chain, direction }) => {
14
+ // Resolve the top-level layout element in the chain
15
+ const topTag = chain && chain.length > 0 ? chain[0].tag : tag;
16
+ const topParams = chain && chain.length > 0 ? chain[0].params : params;
17
+
18
+ // RT-13: Async hydration gate — await custom element definition before mounting
19
+ if (typeof customElements !== 'undefined' && topTag.includes('-') && !customElements.get(topTag)) {
20
+ await customElements.whenDefined(topTag);
21
+ }
22
+
23
+ const spec = specRegistry.get(topTag.toLowerCase());
24
+ if (!spec || !spec.container) return;
25
+
26
+ // Use Advanced Container Registry lookup instead of blind DOM query
27
+ const containerEl = router.getContainer(spec.container);
28
+ if (!containerEl) {
29
+ console.warn(`Target container "${spec.container}" not found in DOM for element <${topTag}>`);
30
+ return;
31
+ }
32
+
33
+ const props = {};
34
+
35
+ // 1. Cast params
36
+ for (const [key, val] of Object.entries(topParams)) {
37
+ let casted = val;
38
+ if (spec.props && spec.props[key]) {
39
+ const type = spec.props[key].type;
40
+ if (type === Boolean) {
41
+ casted = val === 'true' || val === '1' || val === '';
42
+ } else if (type === Number) {
43
+ const num = Number(val);
44
+ casted = isNaN(num) ? 0 : num;
45
+ }
46
+ }
47
+ props[key] = casted;
48
+ }
49
+
50
+ // 2. Map query params
51
+ if (spec.query && Array.isArray(spec.query) && query) {
52
+ for (const key of spec.query) {
53
+ const val = query[key];
54
+ if (val !== undefined) {
55
+ let casted = val;
56
+ if (spec.props && spec.props[key]) {
57
+ const type = spec.props[key].type;
58
+ if (type === Boolean) {
59
+ casted = val === 'true' || val === '1' || val === '';
60
+ } else if (type === Number) {
61
+ const num = Number(val);
62
+ casted = isNaN(num) ? 0 : num;
63
+ }
64
+ }
65
+ props[key] = casted;
66
+ }
67
+ }
68
+ }
69
+
70
+ // 3. Map hash property
71
+ if (spec.props && spec.props.hash && hash !== undefined) {
72
+ props.hash = hash;
73
+ }
74
+
75
+ // Layout-preserving diffing: Sync parameters reactively if the element is already mounted
76
+ const currentChild = containerEl.querySelector('.page-content');
77
+ if (currentChild && currentChild.tagName.toLowerCase() === topTag.toLowerCase()) {
78
+ for (const [key, value] of Object.entries(props)) {
79
+ currentChild[key] = value;
80
+ }
81
+ return;
82
+ }
83
+
84
+ // Instantiate the new declarative page element
85
+ const pageEl = document.createElement(topTag);
86
+ pageEl.classList.add('page-content');
87
+ for (const [key, value] of Object.entries(props)) {
88
+ pageEl[key] = value;
89
+ }
90
+
91
+ // Delegated UI Swap: If the container implements swapView, let it handle the DOM transitions
92
+ if (typeof containerEl.swapView === 'function') {
93
+ await containerEl.swapView(pageEl, { params: props, direction });
94
+ } else {
95
+ // Fallback to standard atomic replace
96
+ containerEl.replaceChildren(pageEl);
97
+ }
98
+ });
99
+ }
100
+ }
101
+
102
+ export function destroyOrchestrator() {
103
+ dispose?.();
104
+ dispose = null;
105
+ }