@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,283 @@
1
+ # Native Security Usage Guide
2
+
3
+ The Native Security layer provides a unified, zero-dependency browser-native security façade covering Web Cryptography, HTML sanitization, Trusted Types, and the Permissions API. All cryptographic operations delegate to the browser's optimized `SubtleCrypto` thread pools. No third-party libraries are required.
4
+
5
+ Import from the security entry point:
6
+
7
+ ```javascript
8
+ import { security } from '@adukiorg/anza/security';
9
+ ```
10
+
11
+ Or import individual utilities directly:
12
+
13
+ ```javascript
14
+ import { uuid, hash, generateKey, encrypt, decrypt, seal, unseal, sign, verify, sanitize, permission, watchPermission } from '@adukiorg/anza/security';
15
+ ```
16
+
17
+ ---
18
+
19
+ ## 1. Choosing an API
20
+
21
+ | Need | API | Characteristics |
22
+ |---|---|---|
23
+ | Unique correlation ID | `uuid()` | Synchronous, `crypto.randomUUID()`, centralized and mockable |
24
+ | Content hashing | `hash(data, algo)` | `SubtleCrypto.digest`, returns `ArrayBuffer` |
25
+ | AES-GCM encryption | `generateKey`, `encrypt`, `decrypt` | Non-extractable by default, fresh 12-byte IV per encrypt |
26
+ | String encryption (base64) | `seal`, `unseal` | Convenience wrappers over `encrypt`/`decrypt` returning base64 strings |
27
+ | Password-derived key | `deriveKey(pw, salt, iters)` | PBKDF2 with 600,000 iterations, AES-GCM output |
28
+ | Integrity / signing | `sign(key, data)` | HMAC or ECDSA depending on key algorithm |
29
+ | Signature verification | `verify(key, sig, data)` | HMAC or ECDSA depending on key algorithm |
30
+ | Safe HTML output | `sanitize(html)` | DOMParser fallback, Trusted Types when available |
31
+ | Permission query | `permission(name)` | Resolves to `'granted'` \| `'denied'` \| `'prompt'` |
32
+ | Permission watcher | `watchPermission(name, fn, signal)` | AbortSignal-gated change listener |
33
+
34
+ ---
35
+
36
+ ## 2. Unique Identifiers
37
+
38
+ `uuid()` is the centralized source of cryptographically secure UUIDs across all platform modules. Using it instead of calling `crypto.randomUUID()` directly makes the system mockable in tests.
39
+
40
+ ```javascript
41
+ import { uuid } from '@adukiorg/anza/security';
42
+
43
+ const id = uuid(); // e.g. "550e8400-e29b-41d4-a716-446655440000"
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 3. Hashing
49
+
50
+ Compute a SHA-256 (or other algorithm) digest of any data:
51
+
52
+ ```javascript
53
+ import { hash } from '@adukiorg/anza/security';
54
+
55
+ // Hash a string
56
+ const digest = await hash('my-payload');
57
+ console.log(digest instanceof ArrayBuffer); // true — 32 bytes for SHA-256
58
+
59
+ // Hash binary data with SHA-512
60
+ const buf = new TextEncoder().encode('data');
61
+ const sha512 = await hash(buf, 'SHA-512');
62
+ ```
63
+
64
+ ---
65
+
66
+ ## 4. Symmetric Encryption (AES-GCM)
67
+
68
+ Generates a non-extractable 256-bit AES-GCM key, encrypts data with a fresh 12-byte IV, and prepends the IV to the ciphertext for portability:
69
+
70
+ ```javascript
71
+ import { generateKey, encrypt, decrypt } from '@adukiorg/anza/security';
72
+
73
+ // Generate a key (non-extractable, in-memory only)
74
+ const key = await generateKey('AES-GCM');
75
+
76
+ // Encrypt
77
+ const cipher = await encrypt(key, 'Secret message'); // returns ArrayBuffer (IV + ciphertext)
78
+
79
+ // Decrypt
80
+ const plainBuf = await decrypt(key, cipher);
81
+ const plain = new TextDecoder().decode(plainBuf);
82
+ console.log(plain); // 'Secret message'
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 5. String Encryption (Base64 Convenience)
88
+
89
+ `seal` and `unseal` are string-friendly wrappers over `encrypt`/`decrypt` that return and accept base64-encoded payloads. They are ideal for storing encrypted data in JSON, localStorage, or IndexedDB:
90
+
91
+ ```javascript
92
+ import { generateKey, seal, unseal } from '@adukiorg/anza/security';
93
+
94
+ const key = await generateKey('AES-GCM');
95
+
96
+ // Encrypt to a base64 string (IV + ciphertext)
97
+ const sealed = await seal(key, 'Secret message');
98
+ // e.g. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=="
99
+
100
+ // Decrypt from base64 string
101
+ const plain = await unseal(key, sealed);
102
+ console.log(plain); // 'Secret message'
103
+ ```
104
+
105
+ The base64 format is portable and can be safely stored as JSON strings:
106
+
107
+ ```javascript
108
+ await storage.set('secure:session', sealed, 'idb');
109
+ const retrieved = await storage.get('secure:session', 'idb');
110
+ const plain = await unseal(key, retrieved);
111
+ ```
112
+
113
+ ---
114
+
115
+ ## 6. Password-Based Key Derivation (PBKDF2)
116
+
117
+ Derive an AES-GCM key from a user password and a salt using 600,000 PBKDF2 iterations (NIST-recommended):
118
+
119
+ ```javascript
120
+ import { deriveKey, encrypt, decrypt } from '@adukiorg/anza/security';
121
+
122
+ const key = await deriveKey('UserPassword!', 'unique-salt-string');
123
+
124
+ // Use the derived key normally
125
+ const cipher = await encrypt(key, 'protected data');
126
+ const buf = await decrypt(key, cipher);
127
+ const text = new TextDecoder().decode(buf);
128
+ ```
129
+
130
+ > [!IMPORTANT]
131
+ > The salt must be unique per user or credential and stored alongside the ciphertext. Never reuse salts across different passwords.
132
+
133
+ For fast test execution, reduce iterations:
134
+
135
+ ```javascript
136
+ const key = await deriveKey('pw', 'salt', 1000); // for tests only
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 7. Signing and Verification
142
+
143
+ ### HMAC (Symmetric Integrity)
144
+
145
+ ```javascript
146
+ import { generateKey, sign, verify } from '@adukiorg/anza/security';
147
+
148
+ const key = await generateKey('HMAC', ['sign', 'verify']);
149
+ const data = 'message to protect';
150
+
151
+ const sig = await sign(key, data); // ArrayBuffer
152
+ const valid = await verify(key, sig, data); // true
153
+
154
+ const tampered = await verify(key, sig, data + '!'); // false
155
+ ```
156
+
157
+ ### ECDSA (Asymmetric Signing)
158
+
159
+ ```javascript
160
+ // Generate ECDSA key pair using SubtleCrypto directly
161
+ const keyPair = await crypto.subtle.generateKey(
162
+ { name: 'ECDSA', namedCurve: 'P-256' },
163
+ false,
164
+ ['sign', 'verify']
165
+ );
166
+
167
+ const data = 'document-to-sign';
168
+ const sig = await sign(keyPair.privateKey, data);
169
+ const valid = await verify(keyPair.publicKey, sig, data); // true
170
+ ```
171
+
172
+ ---
173
+
174
+ ## 8. HTML Sanitization (Trusted Types)
175
+
176
+ `sanitize()` strips all disallowed tags, event handler attributes, and `javascript:` href links. When the browser supports the Trusted Types API, it wraps the output in a named `TrustedHTML` object (`core-sanitize` policy) to satisfy strict CSP directives:
177
+
178
+ ```javascript
179
+ import { sanitize } from '@adukiorg/anza/security';
180
+
181
+ // Safe input passes through cleanly
182
+ const safe = String(sanitize('<p class="note"><strong>Hello</strong></p>'));
183
+ // '<p class="note"><strong>Hello</strong></p>'
184
+
185
+ // Malicious input is cleaned
186
+ const unsafe = String(sanitize('<div><script>alert("xss")</script><img onerror="hack()"></div>'));
187
+ // '<div></div>'
188
+
189
+ // javascript: href is stripped
190
+ const link = String(sanitize('<a href="javascript:void(0)">click</a>'));
191
+ // '<a>click</a>'
192
+ ```
193
+
194
+ > [!NOTE]
195
+ > The return type is `TrustedHTML | string`. Use `String(sanitize(...))` when you only need the raw string. Pass the raw result directly to DOM sinks that accept `TrustedHTML` under a Trusted Types policy.
196
+
197
+ > [!WARNING]
198
+ > `sanitize()` is client-side only. It returns the input unchanged when called outside a browser (e.g. Node.js SSR).
199
+
200
+ ---
201
+
202
+ ## 9. Permissions API
203
+
204
+ ### Query Current State
205
+
206
+ ```javascript
207
+ import { permission } from '@adukiorg/anza/security';
208
+
209
+ const state = await permission('geolocation');
210
+ // 'granted' | 'denied' | 'prompt'
211
+
212
+ const cam = await permission('camera');
213
+ ```
214
+
215
+ Returns `'denied'` safely for any unrecognized or unsupported permission name.
216
+
217
+ ### Watch for Changes
218
+
219
+ ```javascript
220
+ import { watchPermission } from '@adukiorg/anza/security';
221
+
222
+ const ctrl = new AbortController();
223
+
224
+ const dispose = watchPermission('geolocation', (state) => {
225
+ console.log('Permission changed:', state);
226
+ }, ctrl.signal);
227
+
228
+ // Later, tear down cleanly:
229
+ ctrl.abort(); // or dispose();
230
+ ```
231
+
232
+ > [!TIP]
233
+ > Always pass an `AbortSignal` to `watchPermission` inside components or views so the watcher is removed automatically when the component is destroyed.
234
+
235
+ ---
236
+
237
+ ## 9. Integration Patterns
238
+
239
+ ### Lifecycle-Gated Watcher in a Custom Element
240
+
241
+ ```javascript
242
+ class MyLocation extends HTMLElement {
243
+ #ctrl = new AbortController();
244
+
245
+ connectedCallback() {
246
+ watchPermission('geolocation', (state) => {
247
+ this.dataset.geo = state;
248
+ }, this.#ctrl.signal);
249
+ }
250
+
251
+ disconnectedCallback() {
252
+ this.#ctrl.abort();
253
+ }
254
+ }
255
+ ```
256
+
257
+ ### Encrypting and Storing Sensitive Data
258
+
259
+ ```javascript
260
+ import { generateKey, seal, unseal } from '@adukiorg/anza/security';
261
+ import { storage } from '@adukiorg/anza/storage';
262
+
263
+ // Derive and persist key
264
+ const key = await generateKey('AES-GCM');
265
+ const sealed = await seal(key, JSON.stringify({ token: 'secret' }));
266
+ await storage.set('secure:session', sealed, 'idb');
267
+
268
+ // Retrieve and decrypt
269
+ const retrieved = await storage.get('secure:session', 'idb');
270
+ const plain = await unseal(key, retrieved);
271
+ const { token } = JSON.parse(plain);
272
+ ```
273
+
274
+ ### Sanitize Before Setting innerHTML
275
+
276
+ ```javascript
277
+ import { sanitize } from '@adukiorg/anza/security';
278
+
279
+ function render(el, userHtml) {
280
+ // TrustedHTML satisfies Trusted Types sinks
281
+ el.innerHTML = sanitize(userHtml);
282
+ }
283
+ ```
@@ -0,0 +1,117 @@
1
+ /**
2
+ * src/core/state/derived.js
3
+ *
4
+ * Fine-grained reactive derived state (computed nodes).
5
+ * Evaluates computations lazily, memoizes results, and dynamically collects
6
+ * dependency keys by intercepting read accesses during execution.
7
+ *
8
+ * Source: doc 08 — State Management §7, §8
9
+ */
10
+
11
+ import { setActiveSubscriber, getActiveSubscriber } from './store.js';
12
+
13
+ export class DerivedValue {
14
+ #compute;
15
+ #value;
16
+ #dependencies = new Set();
17
+ #dirty = true;
18
+ #listeners = new Set();
19
+ #disposers = [];
20
+
21
+ constructor(compute) {
22
+ if (typeof compute !== 'function') {
23
+ throw new Error('DerivedValue requires a valid compute function');
24
+ }
25
+ this.#compute = compute;
26
+ }
27
+
28
+ /**
29
+ * Retrieves the current computed value (evaluates lazily and memoizes result).
30
+ */
31
+ get value() {
32
+ // Propagate dependency mapping up to parents (for nested derived nodes)
33
+ const outerSubscriber = getActiveSubscriber();
34
+ if (outerSubscriber) {
35
+ for (const dep of this.#dependencies) {
36
+ outerSubscriber.add(dep);
37
+ }
38
+ }
39
+
40
+ if (this.#dirty) {
41
+ this.#recompute();
42
+ }
43
+
44
+ return this.#value;
45
+ }
46
+
47
+ #recompute() {
48
+ // Revoke previous listeners
49
+ for (const dispose of this.#disposers) {
50
+ dispose();
51
+ }
52
+ this.#disposers = [];
53
+ this.#dependencies.clear();
54
+
55
+ // Trap active gets inside execution context
56
+ const previous = getActiveSubscriber();
57
+ const tracked = new Set();
58
+ setActiveSubscriber(tracked);
59
+
60
+ try {
61
+ this.#value = this.#compute();
62
+ this.#dirty = false;
63
+ this.#dependencies = tracked;
64
+
65
+ // Reactively attach to all newly resolved dependency nodes
66
+ for (const dep of this.#dependencies) {
67
+ const dispose = dep.store.subscribe(dep.key, () => {
68
+ this.#markDirty();
69
+ });
70
+ this.#disposers.push(dispose);
71
+ }
72
+ } finally {
73
+ setActiveSubscriber(previous);
74
+ }
75
+ }
76
+
77
+ #markDirty() {
78
+ if (this.#dirty) return;
79
+ this.#dirty = true;
80
+
81
+ for (const callback of this.#listeners) {
82
+ try {
83
+ callback();
84
+ } catch (err) {
85
+ console.error('Error executing derived state change subscription:', err);
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Subscribes to updates on this derived state node.
92
+ */
93
+ subscribe(callback) {
94
+ this.#listeners.add(callback);
95
+ return () => {
96
+ this.#listeners.delete(callback);
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Releases and cleans up active subscriptions.
102
+ */
103
+ dispose() {
104
+ for (const dispose of this.#disposers) {
105
+ dispose();
106
+ }
107
+ this.#disposers = [];
108
+ this.#listeners.clear();
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Creates a memoized derived state node.
114
+ */
115
+ export function derived(compute) {
116
+ return new DerivedValue(compute);
117
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * src/core/state/index.js
3
+ *
4
+ * Public state management namespace.
5
+ * Bundles reactive in-memory stores, lazy derived derivations,
6
+ * cross-tab replications, and transactional IndexedDB persistence.
7
+ *
8
+ * Source: doc 08 — State Management §2, §15
9
+ */
10
+
11
+ import { ReactiveStore, setActiveSubscriber, getActiveSubscriber } from './store.js';
12
+ import { derived } from './derived.js';
13
+ import { sync } from './sync.js';
14
+ import { PlatformStorage, storage } from './persist.js';
15
+
16
+ export const state = {
17
+ create: (initial, options) => new ReactiveStore(initial, options),
18
+ derived,
19
+ sync,
20
+ storage
21
+ };
22
+
23
+ export { ReactiveStore, setActiveSubscriber, getActiveSubscriber, derived, sync, PlatformStorage, storage };
@@ -0,0 +1,165 @@
1
+ # State Missing Support
2
+
3
+ This document tracks remaining support, runtime bugs, type mismatches, and performance optimization work for `src/core/state`. Implemented behavior belongs in `usage.md` (or core documentation); unsupported, untyped, or under-tested behavior belongs here until it is built and verified.
4
+
5
+ ---
6
+
7
+ ## 0. Toolchain and Naming Rules
8
+
9
+ Heavy validation, static analysis, and code generation belong in `tools/`, not in browser runtime modules.
10
+
11
+ Current toolchain state support requirements:
12
+ - Analyze `state` initialization and usage patterns statically.
13
+ - Detect mutations violating the immutability discipline.
14
+ - Generate unified type definitions for state domains.
15
+
16
+ Naming rules for state support:
17
+ - Prefer one-word files: `store.js`, `derived.js`, `persist.js`, `sync.js`, `index.js`.
18
+ - Plural folders: `tests/`, `types/`.
19
+ - Single-word methods: `get`, `set`, `batch`, `sync`, `derived`, `clear`, `reset`, `estimate`, `persist`.
20
+ - Single-word events: `change`, `hydrate`, `quota`, `error`.
21
+ - Avoid compound names where single words carry full meaning in context.
22
+
23
+ ---
24
+
25
+ ## 1. Critical Runtime and Type Gaps
26
+
27
+ ### 1.1. Storage Type Mismatches
28
+ - **Status**: Critical mismatch.
29
+ - **Files**:
30
+ - `types/core/state/index.d.ts`
31
+ - `src/core/state/persist.js`
32
+ - **Problem**:
33
+ The type declaration file defines `storage` with `persist(store, options)` and `hydrate(store, name)`. The actual runtime `storage` export is an instance of `PlatformStorage` exposing CRUD and system queries: `get(store, key)`, `set(store, key, val)`, `delete(store, key)`, `query(store, filter)`, `estimate()`, `persist()`, and `isPersisted()`.
34
+ - **Expected Support**:
35
+ - Align `types/core/state/index.d.ts` to expose the true `PlatformStorage` interface.
36
+ - Implement helper hooks in the store or `persist.js` to simplify store-wide persistence if required, but maintain structural parity.
37
+
38
+ ### 1.2. Writable Store Event and Helper Omissions
39
+ - **Status**: Runtime gap.
40
+ - **Files**:
41
+ - `src/core/state/store.js`
42
+ - `src/core/state/index.js`
43
+ - **Problem**:
44
+ The State Management specification (§15) defines `store.broadcast(channelName)` as a method directly on the store instance for cross-tab replication. Currently, this functionality is decoupled into a standalone `sync(store, keys, channelName)` function. Similarly, `store.derived(keys, compute)` is specified but not present on the store instance class.
45
+ - **Expected Support**:
46
+ - Add a delegate helper `broadcast(channelName, keys?)` to `ReactiveStore` mapping directly to the `sync` utility.
47
+ - Add a delegate helper `derived(keys, compute)` to `ReactiveStore` mapping to `derived(compute)` with key-bound invalidation if preferred, or clean up the API specification.
48
+
49
+ ---
50
+
51
+ ## 2. Persistence Gaps (Storage Quota and Eviction)
52
+
53
+ ### 2.1. Proactive Eviction and Quota Warnings
54
+ - **Status**: Planned but missing.
55
+ - **File**:
56
+ - `src/core/state/persist.js`
57
+ - **Problem**:
58
+ The storage engine does not monitor available quota or execute proactive eviction. Large applications risk throwing unhandled `QuotaExceededError` exceptions when writing data.
59
+ - **Expected Support**:
60
+ - Implement a quota check wrapper on `set()`.
61
+ - Establish a warning threshold (80% of estimated quota).
62
+ - Add proactive eviction stages:
63
+ 1. Prune expired cache records based on TTL metadata.
64
+ 2. Evict low-recency records (LRU) using a `lastAccessed` timestamp.
65
+ - Dispatch a global `quota` warning event if space remains critical.
66
+
67
+ ### 2.2. Write-Through and Write-Ahead Transaction Queues
68
+ - **Status**: Missing support.
69
+ - **File**:
70
+ - `src/core/state/persist.js`
71
+ - **Problem**:
72
+ Mutating state is not atomic. A crash between memory writing and database writing leaves inconsistent schemas.
73
+ - **Expected Support**:
74
+ - Add a transaction-safe queue to handle write-ahead serialization (log first, modify memory on confirmation).
75
+ - Implement retry logic in the write queue on transient storage lockups or quota exceptions.
76
+
77
+ ---
78
+
79
+ ## 3. Reactivity Performance and Deep Store Support
80
+
81
+ ### 3.1. Deep Reactive Stores
82
+ - **Status**: Enhancement.
83
+ - **File**:
84
+ - `src/core/state/store.js`
85
+ - **Problem**:
86
+ `ReactiveStore` only intercepts top-level properties (shallow). Mutating nested objects (e.g., `store.get('user').name = 'alice'`) bypasses reactive traps. Manual structural sharing (e.g., `store.set('user', { ...user, name: 'alice' })`) is error-prone.
87
+ - **Expected Support**:
88
+ - Add opt-in deep reactivity configuration on store instantiation.
89
+ - Recursively wrap accessed nested object fields in reactive `Proxy` instances, returning mapped pathways.
90
+ - Validate that circular references do not crash proxy wrappers.
91
+
92
+ ### 3.2. Optimized Snapshots
93
+ - **Status**: Performance enhancement.
94
+ - **File**:
95
+ - `src/core/state/store.js`
96
+ - **Problem**:
97
+ `store.snapshot()` falls back to `JSON.parse(JSON.stringify(...))` when `structuredClone` is missing or if non-serializable objects (like `Map` or `Set`) are stored. This degrades speed in performance-sensitive contexts.
98
+ - **Expected Support**:
99
+ - Use custom fast-cloning paths for pure data objects.
100
+ - Delegate serialization for custom collection types within the store configuration.
101
+
102
+ ---
103
+
104
+ ## 4. Build-Time Static Analysis
105
+
106
+ ### 4.1. Immutability Violation Scan
107
+ - **Status**: Planned.
108
+ - **Files**:
109
+ - `tools/src/extract/runner.rs`
110
+ - **Problem**:
111
+ In-place mutations of objects retrieved from reactive stores (e.g. `store.get('user').name = 'bob'`) bypasses the `Object.is` check and stops reactive updates. This is hard to debug at runtime.
112
+ - **Expected Support**:
113
+ - Implement a AST-based checker in `anza` to scan JavaScript sources for store retrieval variables followed by mutation assignments.
114
+ - Log lint warnings during `scan` and `dev` runs.
115
+
116
+ ### 4.2. Schema Type Generation
117
+ - **Status**: Enhancement.
118
+ - **Files**:
119
+ - `tools/src/extract/runner.rs`
120
+ - **Expected Support**:
121
+ - Statically analyze store initializations to output a typed state manifest schema (`state.d.ts`).
122
+
123
+ ---
124
+
125
+ ## 5. Test Coverage Gaps
126
+
127
+ ### 5.1. No Persistence Tests
128
+ - **Status**: Missing coverage.
129
+ - **File**:
130
+ - `tests/core/state/persist.test.js`
131
+ - **Needed Coverage**:
132
+ - Database instantiation and migrations.
133
+ - CRUD operations (`get`, `set`, `delete`, `query`).
134
+ - Quota estimate mock and persistence hooks.
135
+ - Eviction stages validation.
136
+
137
+ ### 5.2. Tab Sync Robustness
138
+ - **Status**: Weak coverage.
139
+ - **File**:
140
+ - `tests/core/state/sync.test.js`
141
+ - **Needed Coverage**:
142
+ - Re-entrancy and circular echo avoidance.
143
+ - Proper disposal of broadcast channels.
144
+
145
+ ---
146
+
147
+ ## 6. Suggested Implementation Order
148
+
149
+ 1. Align `types/core/state/index.d.ts` with runtime `PlatformStorage`.
150
+ 2. Add `tests/core/state/persist.test.js` to cover basic IndexedDB CRUD operations.
151
+ 3. Expose `store.broadcast()` and `store.derived()` delegate methods.
152
+ 4. Implement recursive deep proxy wrapper options in `ReactiveStore`.
153
+ 5. Implement quota warning thresholds and basic recency-based eviction.
154
+ 6. Add build-time immutability violation scanner to `anza`.
155
+
156
+ ---
157
+
158
+ ## 7. Done Criteria
159
+
160
+ This missing-support list is complete when:
161
+ - Type declarations for `storage` match runtime methods.
162
+ - Persistent local store has comprehensive tests covering IndexedDB operations.
163
+ - Deep reactive stores are optionally configurable and verified.
164
+ - Static scanning tools warn developers of inline store mutations.
165
+ - Eviction and warning systems prevent silent quota failures.