@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,247 @@
1
+ /**
2
+ * src/sw/strategies.js
3
+ *
4
+ * Caching Strategies.
5
+ * Implements standard service worker request interception strategies:
6
+ * CacheFirst, NetworkFirst, StaleRevalidate, CacheThenNetwork, NetworkOnly, CacheOnly,
7
+ * and OfflineFallback, featuring configurable timeouts and dynamic response TTLs.
8
+ *
9
+ * Source: doc 13 — Offline and Background §3
10
+ */
11
+
12
+ /**
13
+ * Clones a response, modifying or appending a custom TTL expires header if requested.
14
+ */
15
+ async function cloneWithExpiry(response, ttl) {
16
+ if (!ttl) return response;
17
+
18
+ // Read body as blob to prevent stream lockups
19
+ const blob = await response.clone().blob();
20
+ const headers = new Headers(response.headers);
21
+ headers.set('x-expires-at', (Date.now() + ttl).toString());
22
+
23
+ return new Response(blob, {
24
+ status: response.status,
25
+ statusText: response.statusText,
26
+ headers
27
+ });
28
+ }
29
+
30
+ /**
31
+ * Serves shell assets eagerly from cache; falls back to network on miss.
32
+ */
33
+ export class CacheFirst {
34
+ constructor(cacheName, options = {}) {
35
+ this.cacheName = cacheName;
36
+ this.options = options;
37
+ }
38
+
39
+ async handle(request) {
40
+ const cache = await caches.open(this.cacheName);
41
+ const cachedResponse = await cache.match(request);
42
+
43
+ if (cachedResponse) {
44
+ const expiresAt = parseInt(cachedResponse.headers.get('x-expires-at') ?? '0', 10);
45
+ if (!expiresAt || expiresAt > Date.now()) {
46
+ return cachedResponse;
47
+ }
48
+ }
49
+
50
+ try {
51
+ const response = await fetch(request);
52
+ if (response && response.ok) {
53
+ const storedRes = await cloneWithExpiry(response, this.options.ttl);
54
+ await cache.put(request, storedRes);
55
+ }
56
+ return response;
57
+ } catch (err) {
58
+ if (cachedResponse) return cachedResponse; // Return expired fallback if offline
59
+ throw err;
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Consults network first; falls back to cache on failure or configurable timeout.
66
+ */
67
+ export class NetworkFirst {
68
+ constructor(cacheName, options = {}) {
69
+ this.cacheName = cacheName;
70
+ this.options = options;
71
+ this.timeout = options.timeout ?? 4000; // default 4-second timeout
72
+ }
73
+
74
+ async handle(request) {
75
+ const cache = await caches.open(this.cacheName);
76
+ const cachedResponse = await cache.match(request);
77
+
78
+ const timeoutPromise = new Promise((resolve) => {
79
+ setTimeout(() => resolve(null), this.timeout);
80
+ });
81
+
82
+ try {
83
+ const networkResponse = await Promise.race([
84
+ fetch(request),
85
+ timeoutPromise
86
+ ]);
87
+
88
+ if (networkResponse && networkResponse.ok) {
89
+ const storedRes = await cloneWithExpiry(networkResponse, this.options.ttl);
90
+ await cache.put(request, storedRes);
91
+ return networkResponse;
92
+ }
93
+ } catch (err) {
94
+ // Fall through to cache on direct network failure
95
+ }
96
+
97
+ if (cachedResponse) {
98
+ return cachedResponse;
99
+ }
100
+
101
+ // Global navigation fallback page check
102
+ if (request.mode === 'navigate' && this.options.fallbackUrl) {
103
+ const fallback = await caches.match(this.options.fallbackUrl);
104
+ if (fallback) return fallback;
105
+ }
106
+
107
+ throw new Error(`NetworkFirst: failed to fetch "${request.url}" and no cached copy was available.`);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Serves cache instantly, executing a parallel background revalidation.
113
+ */
114
+ export class StaleRevalidate {
115
+ constructor(cacheName, options = {}) {
116
+ this.cacheName = cacheName;
117
+ this.options = options;
118
+ }
119
+
120
+ async handle(request) {
121
+ const cache = await caches.open(this.cacheName);
122
+ const cachedResponse = await cache.match(request);
123
+
124
+ const fetchPromise = (async () => {
125
+ try {
126
+ const response = await fetch(request);
127
+ if (response && response.ok) {
128
+ const storedRes = await cloneWithExpiry(response, this.options.ttl);
129
+ await cache.put(request, storedRes);
130
+ }
131
+ return response;
132
+ } catch (err) {
133
+ console.warn(`StaleRevalidate background fetch failed for "${request.url}":`, err);
134
+ throw err;
135
+ }
136
+ })();
137
+
138
+ if (cachedResponse) {
139
+ // Catch error in background to prevent unhandled rejection crashes
140
+ fetchPromise.catch(() => {});
141
+ return cachedResponse;
142
+ }
143
+
144
+ return fetchPromise;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * Two-pass caching strategy. Returns cache instantly, background fetches the network update,
150
+ * and posts the new payload to open window contexts via postMessage.
151
+ */
152
+ export class CacheThenNetwork {
153
+ constructor(cacheName, options = {}) {
154
+ this.cacheName = cacheName;
155
+ this.options = options;
156
+ }
157
+
158
+ async handle(request) {
159
+ const cache = await caches.open(this.cacheName);
160
+ const cachedResponse = await cache.match(request);
161
+
162
+ const fetchPromise = (async () => {
163
+ try {
164
+ const response = await fetch(request);
165
+ if (response && response.ok) {
166
+ const storedRes = await cloneWithExpiry(response, this.options.ttl);
167
+ await cache.put(request, storedRes);
168
+
169
+ // Dispatch update event to all controlled clients
170
+ const clientsList = await self.clients.matchAll();
171
+ if (clientsList.length > 0) {
172
+ let payload = null;
173
+ const contentType = response.headers.get('content-type') ?? '';
174
+ if (contentType.includes('application/json')) {
175
+ try {
176
+ payload = await response.clone().json();
177
+ } catch {}
178
+ }
179
+
180
+ for (const client of clientsList) {
181
+ client.postMessage({
182
+ type: 'cache-update',
183
+ url: request.url,
184
+ payload
185
+ });
186
+ }
187
+ }
188
+ }
189
+ return response;
190
+ } catch (err) {
191
+ console.warn(`CacheThenNetwork background fetch failed for "${request.url}":`, err);
192
+ throw err;
193
+ }
194
+ })();
195
+
196
+ if (cachedResponse) {
197
+ fetchPromise.catch(() => {});
198
+ return cachedResponse;
199
+ }
200
+
201
+ return fetchPromise;
202
+ }
203
+ }
204
+
205
+ /**
206
+ * Bypasses caches, direct network execution.
207
+ */
208
+ export class NetworkOnly {
209
+ async handle(request) {
210
+ return fetch(request);
211
+ }
212
+ }
213
+
214
+ /**
215
+ * Restricts queries purely to cache entries.
216
+ */
217
+ export class CacheOnly {
218
+ constructor(cacheName) {
219
+ this.cacheName = cacheName;
220
+ }
221
+
222
+ async handle(request) {
223
+ const cache = await caches.open(this.cacheName);
224
+ const cachedResponse = await cache.match(request);
225
+ if (!cachedResponse) {
226
+ throw new Error(`CacheOnly: response for "${request.url}" not found in cache "${this.cacheName}"`);
227
+ }
228
+ return cachedResponse;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Intercepts failing navigations, returning a pre-cached fallback webpage.
234
+ */
235
+ export class OfflineFallback {
236
+ constructor(fallbackUrl) {
237
+ this.fallbackUrl = fallbackUrl;
238
+ }
239
+
240
+ async handle(request, error) {
241
+ if (request.mode === 'navigate') {
242
+ const fallback = await caches.match(this.fallbackUrl);
243
+ if (fallback) return fallback;
244
+ }
245
+ throw error || new Error('Request failed and offline fallback page was not available.');
246
+ }
247
+ }
package/src/sw/sync.js ADDED
@@ -0,0 +1,80 @@
1
+ /**
2
+ * src/sw/sync.js
3
+ *
4
+ * Background Sync Queue Replayer.
5
+ * Replays persistent IndexedDB tasks chronologically, re-executes serialized Requests,
6
+ * coordinates retries, handles dead-letter limits, and broadcasts results.
7
+ *
8
+ * Source: doc 13 — Offline and Background §5
9
+ */
10
+
11
+ import { queue } from '../core/offline/queue.js';
12
+ import { deserializeRequest } from './queue.js';
13
+
14
+ /**
15
+ * Replays all queued offline tasks chronologically in FIFO order.
16
+ */
17
+ export async function replayQueue() {
18
+ const list = await queue.list();
19
+
20
+ for (const entry of list) {
21
+ if (entry.failed) continue; // Skip permanently failed items in the DLQ
22
+
23
+ try {
24
+ const request = deserializeRequest(entry.payload);
25
+ const response = await fetch(request);
26
+
27
+ if (response && response.ok) {
28
+ // Success: evict from queue
29
+ await queue.delete(entry.id);
30
+
31
+ // Broadcast success to all controlled clients
32
+ const clientsList = await self.clients.matchAll();
33
+ for (const client of clientsList) {
34
+ client.postMessage({
35
+ type: 'sync-success',
36
+ id: entry.id,
37
+ task: entry.task
38
+ });
39
+ }
40
+ } else {
41
+ // Server returned a failure (e.g. 5xx)
42
+ await requeueFailed(entry, new Error(`Server returned HTTP status ${response.status}`));
43
+ }
44
+ } catch (err) {
45
+ // Network failure (device went offline again or timeout)
46
+ await requeueFailed(entry, err);
47
+ // Halt execution immediately to preserve causal ordering of subsequent actions
48
+ throw err;
49
+ }
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Increments task retry counts and transitions them to dead-letter states at the limit.
55
+ */
56
+ export async function requeueFailed(entry, error) {
57
+ entry.retries = (entry.retries ?? 0) + 1;
58
+ const maxRetries = entry.maxRetries ?? 5;
59
+
60
+ if (entry.retries >= maxRetries) {
61
+ // Dead Letter Queue transition
62
+ entry.failed = true;
63
+ entry.error = error?.message || 'Maximum sync retries exceeded';
64
+ await queue.update(entry);
65
+
66
+ // Broadcast permanent failure to controlled clients
67
+ const clientsList = await self.clients.matchAll();
68
+ for (const client of clientsList) {
69
+ client.postMessage({
70
+ type: 'sync-failed',
71
+ id: entry.id,
72
+ task: entry.task,
73
+ error: entry.error
74
+ });
75
+ }
76
+ } else {
77
+ // Update incremented retries
78
+ await queue.update(entry);
79
+ }
80
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * tokens/index.css
3
+ *
4
+ * Master design token system import entry.
5
+ * Strict cascade loading order: Primitives -> Registered -> Semantic
6
+ * Source: doc 26 §7
7
+ */
8
+
9
+ /* 1. Primitives (Fixed Scale Decision Scales) */
10
+ @import './primitives/colors.css';
11
+ @import './primitives/spacing.css';
12
+ @import './primitives/typography.css';
13
+ @import './primitives/motion.css';
14
+ @import './primitives/radius.css';
15
+ @import './primitives/shadow.css';
16
+ @import './primitives/zindex.css';
17
+
18
+ /* 2. Registered (Houdini Typed Properties for Animation) */
19
+ @import './registered/colors.css';
20
+ @import './registered/dimensions.css';
21
+
22
+ /* 3. Semantic (Meaning and Intent-based Mappings) */
23
+ @import './semantic/light.css';
24
+ @import './semantic/dark.css';
25
+ @import './semantic/contrast.css';
26
+ @import './semantic/components.css';
@@ -0,0 +1,54 @@
1
+ /**
2
+ * tokens/primitives/colors.css
3
+ *
4
+ * Perceptually uniform brand, neutral, and status color scales using OKLCH.
5
+ * Equal numerical differences correspond to equal visual differences.
6
+ * Source: doc 26 §3
7
+ */
8
+
9
+ :root {
10
+ /* Brand palette — 12-step perceptual scale in OKLCH */
11
+ --color-brand-50: oklch(97% 0.05 250);
12
+ --color-brand-100: oklch(92% 0.08 250);
13
+ --color-brand-200: oklch(85% 0.12 250);
14
+ --color-brand-300: oklch(76% 0.16 250);
15
+ --color-brand-400: oklch(66% 0.20 250);
16
+ --color-brand-500: oklch(55% 0.22 250);
17
+ --color-brand-600: oklch(46% 0.20 250);
18
+ --color-brand-700: oklch(38% 0.18 250);
19
+ --color-brand-800: oklch(30% 0.14 250);
20
+ --color-brand-900: oklch(22% 0.10 250);
21
+ --color-brand-950: oklch(14% 0.06 250);
22
+
23
+ /* Neutral palette */
24
+ --color-neutral-0: oklch(100% 0 0);
25
+ --color-neutral-50: oklch(97% 0 0);
26
+ --color-neutral-100: oklch(94% 0 0);
27
+ --color-neutral-200: oklch(88% 0 0);
28
+ --color-neutral-300: oklch(78% 0 0);
29
+ --color-neutral-400: oklch(64% 0 0);
30
+ --color-neutral-500: oklch(50% 0 0);
31
+ --color-neutral-600: oklch(38% 0 0);
32
+ --color-neutral-700: oklch(27% 0 0);
33
+ --color-neutral-800: oklch(18% 0 0);
34
+ --color-neutral-900: oklch(10% 0 0);
35
+ --color-neutral-950: oklch(6% 0 0);
36
+ --color-neutral-1000: oklch(0% 0 0);
37
+
38
+ /* Status palette */
39
+ --color-success-50: oklch(97% 0.02 145);
40
+ --color-success-500: oklch(63% 0.19 145);
41
+ --color-success-600: oklch(53% 0.17 145);
42
+
43
+ --color-warning-50: oklch(98% 0.02 65);
44
+ --color-warning-500: oklch(75% 0.18 65);
45
+ --color-warning-600: oklch(67% 0.16 65);
46
+
47
+ --color-error-50: oklch(96% 0.02 25);
48
+ --color-error-500: oklch(58% 0.22 25);
49
+ --color-error-600: oklch(48% 0.20 25);
50
+
51
+ --color-info-50: oklch(97% 0.02 230);
52
+ --color-info-500: oklch(60% 0.18 230);
53
+ --color-info-600: oklch(50% 0.16 230);
54
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * tokens/primitives/motion.css
3
+ *
4
+ * Durations and easing curves for transitions and animations.
5
+ * Features central global prefers-reduced-motion token-layer overrides.
6
+ * Source: doc 26 §4
7
+ */
8
+
9
+ :root {
10
+ /* Durations */
11
+ --duration-instant: 50ms;
12
+ --duration-fast: 100ms;
13
+ --duration-normal: 200ms;
14
+ --duration-slow: 300ms;
15
+ --duration-slower: 500ms;
16
+
17
+ /* Easing curves */
18
+ --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
19
+ --ease-in: cubic-bezier(0.4, 0, 1, 1);
20
+ --ease-out: cubic-bezier(0, 0, 0.2, 1);
21
+ --ease-in-out: cubic-bezier(0.4, 0, 0.6, 1);
22
+ --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
23
+ --ease-linear: linear;
24
+ }
25
+
26
+ @media (prefers-reduced-motion: reduce) {
27
+ :root {
28
+ --duration-instant: 0ms;
29
+ --duration-fast: 0ms;
30
+ --duration-normal: 0ms;
31
+ --duration-slow: 0ms;
32
+ --duration-slower: 0ms;
33
+ }
34
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * tokens/primitives/radius.css
3
+ *
4
+ * Border radius scale from none to full pill/circle shapes.
5
+ * Source: doc 26 §4
6
+ */
7
+
8
+ :root {
9
+ --radius-none: 0px;
10
+ --radius-sm: 0.125rem; /* 2px */
11
+ --radius-md: 0.375rem; /* 6px */
12
+ --radius-lg: 0.5rem; /* 8px */
13
+ --radius-xl: 0.75rem; /* 12px */
14
+ --radius-2xl: 1rem; /* 16px */
15
+ --radius-full: 9999px; /* Pill / Circle */
16
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * tokens/primitives/shadow.css
3
+ *
4
+ * Elevation shadows using modern ambient + key light OKLCH alpha models.
5
+ * Dark mode shadows automatically increase opacity to counteract low contrast.
6
+ * Source: doc 26 §4
7
+ */
8
+
9
+ :root {
10
+ --shadow-xs: 0 1px 2px 0 oklch(0% 0 0 / 0.05);
11
+ --shadow-sm: 0 1px 3px 0 oklch(0% 0 0 / 0.1), 0 1px 2px -1px oklch(0% 0 0 / 0.1);
12
+ --shadow-md: 0 4px 6px -1px oklch(0% 0 0 / 0.1), 0 2px 4px -2px oklch(0% 0 0 / 0.1);
13
+ --shadow-lg: 0 10px 15px -3px oklch(0% 0 0 / 0.1), 0 4px 6px -4px oklch(0% 0 0 / 0.1);
14
+ --shadow-xl: 0 20px 25px -5px oklch(0% 0 0 / 0.1), 0 8px 10px -6px oklch(0% 0 0 / 0.1);
15
+ --shadow-none: 0 0 #0000;
16
+ }
17
+
18
+ [data-theme="dark"] {
19
+ --shadow-xs: 0 1px 2px 0 oklch(0% 0 0 / 0.15);
20
+ --shadow-sm: 0 1px 3px 0 oklch(0% 0 0 / 0.3), 0 1px 2px -1px oklch(0% 0 0 / 0.3);
21
+ --shadow-md: 0 4px 6px -1px oklch(0% 0 0 / 0.3), 0 2px 4px -2px oklch(0% 0 0 / 0.3);
22
+ --shadow-lg: 0 10px 15px -3px oklch(0% 0 0 / 0.3), 0 4px 6px -4px oklch(0% 0 0 / 0.3);
23
+ --shadow-xl: 0 20px 25px -5px oklch(0% 0 0 / 0.3), 0 8px 10px -6px oklch(0% 0 0 / 0.3);
24
+ }
25
+
26
+ @media (prefers-color-scheme: dark) {
27
+ :root:not([data-theme="light"]) {
28
+ --shadow-xs: 0 1px 2px 0 oklch(0% 0 0 / 0.15);
29
+ --shadow-sm: 0 1px 3px 0 oklch(0% 0 0 / 0.3), 0 1px 2px -1px oklch(0% 0 0 / 0.3);
30
+ --shadow-md: 0 4px 6px -1px oklch(0% 0 0 / 0.3), 0 2px 4px -2px oklch(0% 0 0 / 0.3);
31
+ --shadow-lg: 0 10px 15px -3px oklch(0% 0 0 / 0.3), 0 4px 6px -4px oklch(0% 0 0 / 0.3);
32
+ --shadow-xl: 0 20px 25px -5px oklch(0% 0 0 / 0.3), 0 8px 10px -6px oklch(0% 0 0 / 0.3);
33
+ }
34
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * tokens/primitives/spacing.css
3
+ *
4
+ * Fixed spacing scale using rem units based on a 4px increment system.
5
+ * Source: doc 26 §4
6
+ */
7
+
8
+ :root {
9
+ --space-px: 1px;
10
+ --space-0: 0px;
11
+ --space-0-5: 0.125rem; /* 2px */
12
+ --space-1: 0.25rem; /* 4px */
13
+ --space-1-5: 0.375rem; /* 6px */
14
+ --space-2: 0.5rem; /* 8px */
15
+ --space-2-5: 0.625rem; /* 10px */
16
+ --space-3: 0.75rem; /* 12px */
17
+ --space-4: 1rem; /* 16px */
18
+ --space-5: 1.25rem; /* 20px */
19
+ --space-6: 1.5rem; /* 24px */
20
+ --space-8: 2rem; /* 32px */
21
+ --space-10: 2.5rem; /* 40px */
22
+ --space-12: 3rem; /* 48px */
23
+ --space-16: 4rem; /* 64px */
24
+ --space-20: 5rem; /* 80px */
25
+ --space-24: 6rem; /* 96px */
26
+ --space-32: 8rem; /* 128px */
27
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * tokens/primitives/typography.css
3
+ *
4
+ * Typography scales, weights, line-heights, letter-spacings, and font stacks.
5
+ * Source: doc 26 §4
6
+ */
7
+
8
+ :root {
9
+ /* Modular type scale (1.25 ratio) */
10
+ --font-size-xs: 0.75rem; /* 12px */
11
+ --font-size-sm: 0.875rem; /* 14px */
12
+ --font-size-base: 1rem; /* 16px */
13
+ --font-size-md: 1.125rem; /* 18px */
14
+ --font-size-lg: 1.25rem; /* 20px */
15
+ --font-size-xl: 1.5rem; /* 24px */
16
+ --font-size-2xl: 1.875rem; /* 30px */
17
+ --font-size-3xl: 2.25rem; /* 36px */
18
+ --font-size-4xl: 3rem; /* 48px */
19
+ --font-size-5xl: 3.75rem; /* 60px */
20
+
21
+ /* Font weights */
22
+ --font-weight-light: 300;
23
+ --font-weight-regular: 400;
24
+ --font-weight-medium: 500;
25
+ --font-weight-semibold: 600;
26
+ --font-weight-bold: 700;
27
+
28
+ /* Line heights */
29
+ --line-height-tight: 1.25;
30
+ --line-height-snug: 1.375;
31
+ --line-height-normal: 1.5;
32
+ --line-height-relaxed: 1.625;
33
+ --line-height-loose: 2;
34
+
35
+ /* Letter spacing */
36
+ --letter-spacing-tight: -0.025em;
37
+ --letter-spacing-normal: 0;
38
+ --letter-spacing-wide: 0.025em;
39
+ --letter-spacing-wider: 0.05em;
40
+ --letter-spacing-widest: 0.1em;
41
+
42
+ /* Font families */
43
+ --font-family-sans: 'Outfit', 'Inter', system-ui, -apple-system, sans-serif;
44
+ --font-family-serif: 'Georgia', 'Times New Roman', serif;
45
+ --font-family-mono: 'JetBrains Mono', 'Fira Code', monospace;
46
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * tokens/primitives/zindex.css
3
+ *
4
+ * Explicit layer indices to eliminate z-index escalation bugs.
5
+ * Source: doc 26 §4
6
+ */
7
+
8
+ :root {
9
+ --z-index-base: 0;
10
+ --z-index-raised: 10;
11
+ --z-index-dropdown: 100;
12
+ --z-index-sticky: 200;
13
+ --z-index-overlay: 300;
14
+ --z-index-modal: 400;
15
+ --z-index-popover: 500;
16
+ --z-index-toast: 600;
17
+ --z-index-tooltip: 700;
18
+ }