@livenetworks/ashlar 1.6.1 → 2.0.1

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 (559) hide show
  1. package/LICENSE +641 -0
  2. package/README.md +190 -86
  3. package/{js → components}/COMPONENTS.md +1116 -1108
  4. package/{js → components}/index.js +81 -58
  5. package/{js → components}/ln-accordion/README.md +7 -6
  6. package/components/ln-accordion/ln-accordion-dev.scss +8 -0
  7. package/components/ln-accordion/ln-accordion.js +1 -0
  8. package/components/ln-accordion/ln-accordion.schema.json +30 -0
  9. package/components/ln-accordion/ln-accordion.scss +8 -0
  10. package/{js → components}/ln-accordion/src/ln-accordion.js +8 -2
  11. package/{js → components}/ln-ajax/README.md +171 -160
  12. package/components/ln-ajax/ln-ajax-dev.scss +8 -0
  13. package/components/ln-ajax/ln-ajax.js +1 -0
  14. package/components/ln-ajax/ln-ajax.schema.json +30 -0
  15. package/{js → components}/ln-ajax/src/ln-ajax.js +65 -30
  16. package/{js → components}/ln-api-connector/README.md +27 -5
  17. package/components/ln-api-connector/ln-api-connector.js +1 -0
  18. package/components/ln-api-connector/ln-api-connector.schema.json +96 -0
  19. package/components/ln-api-connector/src/connector-core.js +97 -0
  20. package/components/ln-api-connector/src/ln-api-connector.js +447 -0
  21. package/{js → components}/ln-api-queue/README.md +12 -9
  22. package/components/ln-api-queue/ln-api-queue.js +1 -0
  23. package/components/ln-api-queue/ln-api-queue.schema.json +34 -0
  24. package/{js → components}/ln-api-queue/src/ln-api-queue.js +33 -14
  25. package/{js → components}/ln-api-queue/src/queue-storage.js +7 -3
  26. package/{js → components}/ln-autoresize/README.md +129 -127
  27. package/components/ln-autoresize/ln-autoresize-dev.scss +8 -0
  28. package/components/ln-autoresize/ln-autoresize.js +1 -0
  29. package/components/ln-autoresize/ln-autoresize.schema.json +22 -0
  30. package/{js → components}/ln-autoresize/src/ln-autoresize.js +8 -1
  31. package/{js → components}/ln-autosave/README.md +127 -124
  32. package/components/ln-autosave/ln-autosave-dev.scss +13 -0
  33. package/components/ln-autosave/ln-autosave.js +1 -0
  34. package/components/ln-autosave/ln-autosave.schema.json +48 -0
  35. package/components/ln-autosave/src/autosave-model.js +34 -0
  36. package/{js → components}/ln-autosave/src/ln-autosave.js +65 -58
  37. package/components/ln-chart/README.md +145 -0
  38. package/components/ln-chart/ln-chart-dev.scss +20 -0
  39. package/components/ln-chart/ln-chart.js +1 -0
  40. package/components/ln-chart/ln-chart.schema.json +146 -0
  41. package/components/ln-chart/ln-chart.scss +8 -0
  42. package/components/ln-chart/src/chart-model.js +154 -0
  43. package/components/ln-chart/src/ln-chart.js +176 -0
  44. package/{js → components}/ln-circular-progress/README.md +15 -14
  45. package/components/ln-circular-progress/ln-circular-progress-dev.scss +8 -0
  46. package/components/ln-circular-progress/ln-circular-progress.js +1 -0
  47. package/components/ln-circular-progress/ln-circular-progress.schema.json +42 -0
  48. package/{js → components}/ln-circular-progress/src/ln-circular-progress.js +32 -50
  49. package/{js → components}/ln-confirm/README.md +69 -18
  50. package/components/ln-confirm/ln-confirm-dev.scss +24 -0
  51. package/components/ln-confirm/ln-confirm.js +1 -0
  52. package/components/ln-confirm/ln-confirm.schema.json +66 -0
  53. package/components/ln-confirm/ln-confirm.scss +24 -0
  54. package/{js → components}/ln-confirm/src/ln-confirm.js +180 -171
  55. package/{js → components}/ln-core/README.md +374 -149
  56. package/components/ln-core/attrs.js +273 -0
  57. package/components/ln-core/compare.js +40 -0
  58. package/components/ln-core/crypto.js +265 -0
  59. package/components/ln-core/date.js +50 -0
  60. package/components/ln-core/dom.js +115 -0
  61. package/components/ln-core/events.js +122 -0
  62. package/components/ln-core/form.js +136 -0
  63. package/{js → components}/ln-core/hash.js +149 -92
  64. package/components/ln-core/helpers.js +33 -0
  65. package/components/ln-core/http.js +48 -0
  66. package/components/ln-core/index.js +11 -0
  67. package/components/ln-core/lifecycle.js +403 -0
  68. package/components/ln-core/ln-core.schema.json +109 -0
  69. package/components/ln-core/locale.js +55 -0
  70. package/components/ln-core/matching.js +78 -0
  71. package/components/ln-core/number.js +125 -0
  72. package/components/ln-core/progress.js +30 -0
  73. package/components/ln-core/template.js +206 -0
  74. package/{js → components}/ln-core/window-cache.js +247 -213
  75. package/{js → components}/ln-couchdb-connector/README.md +7 -5
  76. package/components/ln-couchdb-connector/ln-couchdb-connector.js +1 -0
  77. package/components/ln-couchdb-connector/ln-couchdb-connector.schema.json +50 -0
  78. package/{js → components}/ln-couchdb-connector/src/ln-couchdb-connector.js +29 -43
  79. package/{js → components}/ln-data-coordinator/README.md +109 -73
  80. package/components/ln-data-coordinator/ln-data-coordinator-dev.scss +8 -0
  81. package/components/ln-data-coordinator/ln-data-coordinator.js +1 -0
  82. package/components/ln-data-coordinator/ln-data-coordinator.schema.json +196 -0
  83. package/components/ln-data-coordinator/src/data-read-policy.js +41 -0
  84. package/{js → components}/ln-data-coordinator/src/ln-data-coordinator.js +411 -154
  85. package/{js → components}/ln-data-coordinator/src/mutation-receipts.js +0 -5
  86. package/{js → components}/ln-data-store/README.md +42 -17
  87. package/components/ln-data-store/ln-data-store-dev.scss +13 -0
  88. package/components/ln-data-store/ln-data-store.js +1 -0
  89. package/components/ln-data-store/ln-data-store.schema.json +84 -0
  90. package/components/ln-data-store/src/data-store-model.js +155 -0
  91. package/{js → components}/ln-data-store/src/ln-data-store.js +407 -105
  92. package/components/ln-data-store/src/window-index.js +119 -0
  93. package/{js → components}/ln-date/README.md +17 -7
  94. package/{js → components}/ln-date/ln-date-dev.scss +14 -14
  95. package/components/ln-date/ln-date.js +1 -0
  96. package/components/ln-date/ln-date.schema.json +92 -0
  97. package/components/ln-date/src/date-model.js +186 -0
  98. package/components/ln-date/src/ln-date.js +486 -0
  99. package/components/ln-debug/README.md +132 -0
  100. package/components/ln-debug/ln-debug.js +1 -0
  101. package/components/ln-debug/ln-debug.schema.json +28 -0
  102. package/components/ln-debug/src/console-sink.js +19 -0
  103. package/components/ln-debug/src/debug-verifier.js +331 -0
  104. package/components/ln-debug/src/gate.js +95 -0
  105. package/components/ln-debug/src/generated-attributes.js +299 -0
  106. package/components/ln-debug/src/ln-debug.js +60 -0
  107. package/{js → components}/ln-dropdown/README.md +150 -140
  108. package/components/ln-dropdown/ln-dropdown-dev.scss +13 -0
  109. package/components/ln-dropdown/ln-dropdown.js +1 -0
  110. package/components/ln-dropdown/ln-dropdown.schema.json +90 -0
  111. package/components/ln-dropdown/ln-dropdown.scss +18 -0
  112. package/{js → components}/ln-dropdown/src/ln-dropdown.js +114 -11
  113. package/{js → components}/ln-editor/README.md +11 -9
  114. package/components/ln-editor/ln-editor-dev.scss +18 -0
  115. package/components/ln-editor/ln-editor.js +1 -0
  116. package/components/ln-editor/ln-editor.schema.json +58 -0
  117. package/components/ln-editor/ln-editor.scss +13 -0
  118. package/{js → components}/ln-editor/src/ln-editor.js +59 -11
  119. package/{js → components}/ln-external-links/README.md +10 -8
  120. package/components/ln-external-links/ln-external-links.js +1 -0
  121. package/components/ln-external-links/ln-external-links.schema.json +18 -0
  122. package/{js → components}/ln-external-links/src/ln-external-links.js +13 -11
  123. package/{js → components}/ln-fill/README.md +6 -8
  124. package/components/ln-fill/ln-fill.js +1 -0
  125. package/components/ln-fill/ln-fill.schema.json +23 -0
  126. package/{js → components}/ln-fill/src/ln-fill.js +7 -1
  127. package/components/ln-filter/README.md +131 -0
  128. package/components/ln-filter/ln-filter-dev.scss +13 -0
  129. package/components/ln-filter/ln-filter.js +1 -0
  130. package/components/ln-filter/ln-filter.schema.json +153 -0
  131. package/components/ln-filter/src/filter-model.js +185 -0
  132. package/components/ln-filter/src/ln-filter.js +435 -0
  133. package/{js → components}/ln-form/README.md +260 -264
  134. package/components/ln-form/ln-form-dev.scss +8 -0
  135. package/components/ln-form/ln-form.js +1 -0
  136. package/components/ln-form/ln-form.schema.json +45 -0
  137. package/{js → components}/ln-form/src/ln-form.js +107 -99
  138. package/{js → components}/ln-http/README.md +11 -6
  139. package/components/ln-http/ln-http.js +1 -0
  140. package/components/ln-http/ln-http.schema.json +6 -0
  141. package/components/ln-http/src/http-core.js +51 -0
  142. package/{js → components}/ln-http/src/ln-http.js +28 -33
  143. package/{js → components}/ln-icon/README.md +16 -9
  144. package/components/ln-icon/ln-icon.schema.json +6 -0
  145. package/components/ln-include/README.md +75 -0
  146. package/components/ln-include/ln-include-dev.scss +13 -0
  147. package/components/ln-include/ln-include.js +1 -0
  148. package/components/ln-include/ln-include.schema.json +23 -0
  149. package/components/ln-include/src/ln-include.js +111 -0
  150. package/components/ln-key/README.md +130 -0
  151. package/components/ln-key/ln-key-dev.scss +18 -0
  152. package/components/ln-key/ln-key.js +1 -0
  153. package/components/ln-key/ln-key.schema.json +56 -0
  154. package/components/ln-key/src/key-model.js +146 -0
  155. package/components/ln-key/src/ln-key.js +201 -0
  156. package/{js → components}/ln-link/README.md +7 -9
  157. package/components/ln-link/ln-link-dev.scss +8 -0
  158. package/components/ln-link/ln-link.js +1 -0
  159. package/components/ln-link/ln-link.schema.json +22 -0
  160. package/{js → components}/ln-link/src/ln-link.js +16 -11
  161. package/components/ln-list/README.md +152 -0
  162. package/components/ln-list/ln-list-dev.scss +8 -0
  163. package/components/ln-list/ln-list.js +1 -0
  164. package/components/ln-list/ln-list.schema.json +166 -0
  165. package/components/ln-list/ln-list.scss +25 -0
  166. package/{js → components}/ln-list/src/ln-list.js +1301 -1216
  167. package/{js → components}/ln-modal/README.md +13 -10
  168. package/components/ln-modal/ln-modal-dev.scss +18 -0
  169. package/components/ln-modal/ln-modal.js +1 -0
  170. package/components/ln-modal/ln-modal.schema.json +54 -0
  171. package/components/ln-modal/ln-modal.scss +51 -0
  172. package/{js → components}/ln-modal/src/ln-modal.js +164 -137
  173. package/{js → components}/ln-nav/README.md +11 -11
  174. package/components/ln-nav/ln-nav.js +1 -0
  175. package/components/ln-nav/ln-nav.schema.json +25 -0
  176. package/{js → components}/ln-nav/src/ln-nav.js +25 -14
  177. package/components/ln-number/README.md +205 -0
  178. package/{js → components}/ln-number/ln-number-dev.scss +13 -13
  179. package/components/ln-number/ln-number.js +1 -0
  180. package/components/ln-number/ln-number.schema.json +63 -0
  181. package/components/ln-number/src/ln-number.js +400 -0
  182. package/components/ln-number/src/number-model.js +31 -0
  183. package/components/ln-obfuscator/README.md +60 -0
  184. package/components/ln-obfuscator/ln-obfuscator.js +1 -0
  185. package/components/ln-obfuscator/ln-obfuscator.schema.json +49 -0
  186. package/components/ln-obfuscator/src/ln-obfuscator.js +159 -0
  187. package/components/ln-obfuscator/src/obfuscator-model.js +250 -0
  188. package/{js → components}/ln-options/README.md +3 -1
  189. package/components/ln-options/ln-options-dev.scss +13 -0
  190. package/components/ln-options/ln-options.js +1 -0
  191. package/components/ln-options/ln-options.schema.json +41 -0
  192. package/{js → components}/ln-options/src/ln-options.js +13 -5
  193. package/components/ln-persist/README.md +151 -0
  194. package/components/ln-persist/ln-persist.js +1 -0
  195. package/components/ln-persist/ln-persist.schema.json +28 -0
  196. package/components/ln-persist/src/ln-persist.js +83 -0
  197. package/components/ln-picklist/README.md +230 -0
  198. package/components/ln-picklist/ln-picklist-dev.scss +22 -0
  199. package/components/ln-picklist/ln-picklist.js +1 -0
  200. package/components/ln-picklist/ln-picklist.schema.json +49 -0
  201. package/components/ln-picklist/src/ln-picklist.js +228 -0
  202. package/{js → components}/ln-popover/README.md +164 -162
  203. package/components/ln-popover/ln-popover-dev.scss +13 -0
  204. package/components/ln-popover/ln-popover.js +1 -0
  205. package/components/ln-popover/ln-popover.schema.json +82 -0
  206. package/components/ln-popover/ln-popover.scss +18 -0
  207. package/{js → components}/ln-popover/src/ln-popover.js +42 -36
  208. package/{js → components}/ln-progress/README.md +410 -413
  209. package/components/ln-progress/ln-progress-dev.scss +8 -0
  210. package/components/ln-progress/ln-progress.js +1 -0
  211. package/components/ln-progress/ln-progress.schema.json +40 -0
  212. package/components/ln-progress/src/ln-progress.js +91 -0
  213. package/components/ln-progress/src/progress-model.js +20 -0
  214. package/{js → components}/ln-router/README.md +443 -432
  215. package/components/ln-router/ln-router-dev.scss +13 -0
  216. package/components/ln-router/ln-router.js +1 -0
  217. package/components/ln-router/ln-router.schema.json +68 -0
  218. package/{js → components}/ln-router/src/ln-router.js +585 -539
  219. package/components/ln-router/src/router-model.js +50 -0
  220. package/components/ln-scroll/README.md +45 -0
  221. package/components/ln-scroll/ln-scroll.js +1 -0
  222. package/components/ln-scroll/ln-scroll.schema.json +85 -0
  223. package/components/ln-scroll/src/ln-scroll.js +150 -0
  224. package/components/ln-search/README.md +188 -0
  225. package/components/ln-search/ln-search-dev.scss +13 -0
  226. package/components/ln-search/ln-search.js +1 -0
  227. package/components/ln-search/ln-search.schema.json +103 -0
  228. package/components/ln-search/src/ln-search.js +330 -0
  229. package/{js → components}/ln-slug/README.md +6 -3
  230. package/components/ln-slug/ln-slug-dev.scss +18 -0
  231. package/components/ln-slug/ln-slug.js +1 -0
  232. package/components/ln-slug/ln-slug.schema.json +23 -0
  233. package/{js → components}/ln-slug/src/ln-slug.js +20 -13
  234. package/components/ln-slug/src/slug-model.js +21 -0
  235. package/{js → components}/ln-sort/README.md +178 -170
  236. package/{js → components}/ln-sort/ln-sort-dev.scss +8 -8
  237. package/components/ln-sort/ln-sort.js +1 -0
  238. package/components/ln-sort/ln-sort.schema.json +104 -0
  239. package/components/ln-sort/src/ln-sort.js +321 -0
  240. package/components/ln-sort/src/sort-model.js +117 -0
  241. package/{js → components}/ln-sortable/README.md +7 -11
  242. package/components/ln-sortable/ln-sortable-dev.scss +13 -0
  243. package/components/ln-sortable/ln-sortable.js +1 -0
  244. package/components/ln-sortable/ln-sortable.schema.json +36 -0
  245. package/{js → components}/ln-sortable/src/ln-sortable.js +8 -13
  246. package/{js → components}/ln-stat/README.md +3 -1
  247. package/components/ln-stat/ln-stat-dev.scss +8 -0
  248. package/components/ln-stat/ln-stat.js +1 -0
  249. package/components/ln-stat/ln-stat.schema.json +32 -0
  250. package/{js → components}/ln-stat/src/ln-stat.js +14 -18
  251. package/components/ln-stat/src/stat-model.js +28 -0
  252. package/{js → components}/ln-table/README.md +37 -24
  253. package/{js → components}/ln-table/ln-table-dev.scss +17 -17
  254. package/components/ln-table/ln-table.js +1 -0
  255. package/components/ln-table/ln-table.schema.json +211 -0
  256. package/components/ln-table/ln-table.scss +15 -0
  257. package/{js → components}/ln-table/src/ln-table.js +1365 -1493
  258. package/components/ln-table/src/table-model.js +98 -0
  259. package/components/ln-table-coordinator/README.md +61 -0
  260. package/components/ln-table-coordinator/ln-table-coordinator.js +1 -0
  261. package/components/ln-table-coordinator/ln-table-coordinator.schema.json +93 -0
  262. package/components/ln-table-coordinator/src/ln-table-coordinator.js +193 -0
  263. package/{js → components}/ln-tabs/README.md +32 -14
  264. package/components/ln-tabs/ln-tabs-dev.scss +24 -0
  265. package/components/ln-tabs/ln-tabs.js +1 -0
  266. package/components/ln-tabs/ln-tabs.schema.json +75 -0
  267. package/components/ln-tabs/ln-tabs.scss +8 -0
  268. package/components/ln-tabs/src/ln-tabs.js +207 -0
  269. package/components/ln-tabs/src/tabs-model.js +82 -0
  270. package/{js → components}/ln-time/README.md +96 -95
  271. package/components/ln-time/ln-time-dev.scss +13 -0
  272. package/components/ln-time/ln-time.js +1 -0
  273. package/components/ln-time/ln-time.schema.json +38 -0
  274. package/{js → components}/ln-time/src/ln-time.js +206 -223
  275. package/components/ln-time/src/time-model.js +64 -0
  276. package/{js → components}/ln-toast/README.md +23 -5
  277. package/components/ln-toast/ln-toast-dev.scss +8 -0
  278. package/components/ln-toast/ln-toast.js +1 -0
  279. package/components/ln-toast/ln-toast.schema.json +65 -0
  280. package/components/ln-toast/ln-toast.scss +59 -0
  281. package/{js → components}/ln-toast/src/ln-toast.js +233 -232
  282. package/{js → components}/ln-toggle/README.md +198 -196
  283. package/{js → components}/ln-toggle/ln-toggle-dev.scss +13 -13
  284. package/components/ln-toggle/ln-toggle.js +1 -0
  285. package/components/ln-toggle/ln-toggle.schema.json +52 -0
  286. package/components/ln-toggle/ln-toggle.scss +37 -0
  287. package/{js → components}/ln-toggle/src/ln-toggle.js +61 -46
  288. package/{js → components}/ln-tooltip/README.md +6 -6
  289. package/{js → components}/ln-tooltip/ln-tooltip-dev.scss +9 -9
  290. package/components/ln-tooltip/ln-tooltip.js +1 -0
  291. package/components/ln-tooltip/ln-tooltip.schema.json +86 -0
  292. package/components/ln-tooltip/ln-tooltip.scss +9 -0
  293. package/{js → components}/ln-tooltip/src/ln-tooltip.js +14 -3
  294. package/{js → components}/ln-translations/README.md +3 -1
  295. package/components/ln-translations/ln-translations-dev.scss +13 -0
  296. package/components/ln-translations/ln-translations.js +1 -0
  297. package/components/ln-translations/ln-translations.schema.json +120 -0
  298. package/{js → components}/ln-translations/src/ln-translations.js +25 -10
  299. package/components/ln-ui-coordinator/README.md +101 -0
  300. package/components/ln-ui-coordinator/ln-ui-coordinator-dev.scss +8 -0
  301. package/components/ln-ui-coordinator/ln-ui-coordinator.js +1 -0
  302. package/components/ln-ui-coordinator/ln-ui-coordinator.schema.json +60 -0
  303. package/{js/ln-modal-coordinator/src/ln-modal-coordinator.js → components/ln-ui-coordinator/src/ln-ui-coordinator.js} +403 -345
  304. package/components/ln-upload/README.md +133 -0
  305. package/components/ln-upload/ln-upload-dev.scss +18 -0
  306. package/components/ln-upload/ln-upload.js +1 -0
  307. package/components/ln-upload/ln-upload.schema.json +193 -0
  308. package/components/ln-upload/ln-upload.scss +20 -0
  309. package/components/ln-upload/src/ln-upload.js +652 -0
  310. package/components/ln-upload/src/upload-model.js +78 -0
  311. package/{js → components}/ln-validate/README.md +182 -180
  312. package/{js → components}/ln-validate/ln-validate-dev.scss +15 -15
  313. package/components/ln-validate/ln-validate.js +1 -0
  314. package/components/ln-validate/ln-validate.schema.json +50 -0
  315. package/components/ln-validate/ln-validate.scss +7 -0
  316. package/{js → components}/ln-validate/src/ln-validate.js +23 -45
  317. package/components/ln-validate/src/validate-model.js +64 -0
  318. package/components/ln-websocket-connector/README.md +166 -0
  319. package/components/ln-websocket-connector/ln-websocket-connector.js +1 -0
  320. package/components/ln-websocket-connector/ln-websocket-connector.schema.json +29 -0
  321. package/components/ln-websocket-connector/src/ln-websocket-connector.js +279 -0
  322. package/ln-ashlar.html-data.json +1796 -0
  323. package/package.json +84 -61
  324. package/{scss → theme}/base/_global.scss +1 -2
  325. package/{scss → theme}/base/_reset.scss +1 -1
  326. package/{scss → theme}/base/_typography.scss +2 -0
  327. package/theme/brand.css +73 -0
  328. package/{scss → theme}/components/_app-shell.scss +8 -0
  329. package/theme/components/_button.scss +24 -0
  330. package/theme/components/_chart.scss +5 -0
  331. package/theme/components/_confirm.scss +6 -0
  332. package/{scss → theme}/components/_date.scss +30 -22
  333. package/{scss → theme}/components/_dropdown.scss +6 -1
  334. package/theme/components/_form.scss +140 -0
  335. package/{scss → theme}/components/_layout.scss +6 -0
  336. package/{scss → theme}/components/_ln-table.scss +1 -6
  337. package/theme/components/_picklist.scss +3 -0
  338. package/{scss → theme}/components/_sort.scss +10 -10
  339. package/theme/components/_status-badge.scss +6 -0
  340. package/{scss → theme}/components/_table.scss +16 -0
  341. package/{scss → theme}/components/_tabs.scss +0 -4
  342. package/theme/components/_toast.scss +48 -0
  343. package/{scss → theme}/components/_toggle.scss +1 -1
  344. package/theme/components/_upload.scss +45 -0
  345. package/{scss → theme}/config/_breakpoints.scss +1 -1
  346. package/theme/config/_contrast.scss +128 -0
  347. package/{scss → theme}/config/_density.scss +191 -205
  348. package/theme/config/_palette.scss +274 -0
  349. package/theme/config/_theme.scss +293 -0
  350. package/{scss → theme}/config/_tokens.scss +285 -441
  351. package/{scss → theme}/config/mixins/_accordion.scss +1 -1
  352. package/{scss → theme}/config/mixins/_alert.scss +1 -0
  353. package/{scss → theme}/config/mixins/_app-shell.scss +4 -2
  354. package/{scss → theme}/config/mixins/_borders.scss +2 -0
  355. package/theme/config/mixins/_btn.scss +175 -0
  356. package/{scss → theme}/config/mixins/_card.scss +55 -46
  357. package/theme/config/mixins/_chart.scss +82 -0
  358. package/{scss → theme}/config/mixins/_collapsible.scss +2 -1
  359. package/{scss → theme}/config/mixins/_colors.scss +8 -14
  360. package/{scss → theme}/config/mixins/_diagnostics.scss +48 -48
  361. package/theme/config/mixins/_dropdown.scss +169 -0
  362. package/{scss → theme}/config/mixins/_editor.scss +0 -7
  363. package/{scss → theme}/config/mixins/_filter.scss +47 -47
  364. package/{scss → theme}/config/mixins/_footer.scss +1 -1
  365. package/{scss → theme}/config/mixins/_form.scss +49 -18
  366. package/{scss → theme}/config/mixins/_index.scss +62 -61
  367. package/{scss → theme}/config/mixins/_layout.scss +6 -2
  368. package/{scss → theme}/config/mixins/_ln-table.scss +31 -10
  369. package/{scss → theme}/config/mixins/_loader.scss +1 -1
  370. package/{scss → theme}/config/mixins/_modal.scss +145 -149
  371. package/{scss → theme}/config/mixins/_motion.scss +13 -0
  372. package/{scss → theme}/config/mixins/_nav.scss +7 -7
  373. package/theme/config/mixins/_picklist.scss +66 -0
  374. package/theme/config/mixins/_popover.scss +96 -0
  375. package/{scss → theme}/config/mixins/_sidebar.scss +7 -4
  376. package/{scss → theme}/config/mixins/_sort.scss +43 -42
  377. package/{scss → theme}/config/mixins/_status-badge.scss +15 -2
  378. package/{scss → theme}/config/mixins/_table.scss +25 -14
  379. package/{scss → theme}/config/mixins/_tabs.scss +1 -1
  380. package/{scss → theme}/config/mixins/_toast.scss +174 -179
  381. package/{scss → theme}/config/mixins/_tooltip.scss +2 -2
  382. package/{scss → theme}/config/mixins/_typography.scss +16 -5
  383. package/{scss → theme}/config/mixins/_upload.scss +8 -6
  384. package/theme/ln-ashlar-core.scss +24 -0
  385. package/theme/ln-ashlar-dev.scss +66 -0
  386. package/theme/ln-ashlar-landing.scss +56 -0
  387. package/{scss/ln-ashlar.scss → theme/ln-ashlar-theme.scss} +63 -69
  388. package/theme/ln-ashlar.scss +8 -0
  389. package/theme/presentation/_bento.scss +151 -0
  390. package/theme/presentation/_cta.scss +111 -0
  391. package/theme/presentation/_effects.scss +216 -0
  392. package/theme/presentation/_features.scss +108 -0
  393. package/theme/presentation/_footer.scss +129 -0
  394. package/theme/presentation/_hero.scss +158 -0
  395. package/theme/presentation/_layout.scss +170 -0
  396. package/theme/presentation/_navbar.scss +140 -0
  397. package/theme/presentation/_pricing.scss +262 -0
  398. package/theme/presentation/_stats.scss +87 -0
  399. package/theme/presentation/_testimonials.scss +124 -0
  400. package/theme/presentation/_tokens.scss +39 -0
  401. package/theme/presentation/_typography.scss +163 -0
  402. package/{scss → theme}/utilities/_animations.scss +38 -0
  403. package/{scss → theme}/utilities/_utilities.scss +6 -0
  404. package/web-types.json +2312 -0
  405. package/js/ln-accordion/ln-accordion.js +0 -1
  406. package/js/ln-ajax/ln-ajax.js +0 -1
  407. package/js/ln-api-connector/ln-api-connector.js +0 -1
  408. package/js/ln-api-connector/src/ln-api-connector.js +0 -368
  409. package/js/ln-api-queue/ln-api-queue.js +0 -1
  410. package/js/ln-autoresize/ln-autoresize.js +0 -1
  411. package/js/ln-autosave/ln-autosave.js +0 -1
  412. package/js/ln-circular-progress/ln-circular-progress.js +0 -1
  413. package/js/ln-confirm/_ln-confirm.scss +0 -24
  414. package/js/ln-confirm/ln-confirm.js +0 -1
  415. package/js/ln-core/crypto.js +0 -83
  416. package/js/ln-core/helpers.js +0 -788
  417. package/js/ln-core/index.js +0 -7
  418. package/js/ln-core/persist.js +0 -97
  419. package/js/ln-couchdb-connector/ln-couchdb-connector.js +0 -1
  420. package/js/ln-data-coordinator/ln-data-coordinator.js +0 -1
  421. package/js/ln-data-coordinator/src/data-read-policy.js +0 -18
  422. package/js/ln-data-store/ln-data-store.js +0 -1
  423. package/js/ln-date/ln-date.js +0 -1
  424. package/js/ln-date/src/ln-date.js +0 -603
  425. package/js/ln-debug/README.md +0 -58
  426. package/js/ln-debug/ln-debug.js +0 -1
  427. package/js/ln-debug/src/ln-debug.js +0 -19
  428. package/js/ln-dropdown/ln-dropdown.js +0 -1
  429. package/js/ln-dropdown/ln-dropdown.scss +0 -15
  430. package/js/ln-editor/ln-editor.js +0 -1
  431. package/js/ln-editor/ln-editor.scss +0 -43
  432. package/js/ln-external-links/ln-external-links.js +0 -1
  433. package/js/ln-fill/ln-fill.js +0 -1
  434. package/js/ln-filter/README.md +0 -189
  435. package/js/ln-filter/ln-filter-dev.scss +0 -8
  436. package/js/ln-filter/ln-filter.js +0 -1
  437. package/js/ln-filter/src/ln-filter.js +0 -419
  438. package/js/ln-form/ln-form.js +0 -1
  439. package/js/ln-http/ln-http.js +0 -1
  440. package/js/ln-link/ln-link.js +0 -1
  441. package/js/ln-list/README.md +0 -129
  442. package/js/ln-list/ln-list.js +0 -1
  443. package/js/ln-list/ln-list.scss +0 -28
  444. package/js/ln-modal/ln-modal-dev.scss +0 -13
  445. package/js/ln-modal/ln-modal.js +0 -1
  446. package/js/ln-modal/ln-modal.scss +0 -35
  447. package/js/ln-modal-coordinator/README.md +0 -99
  448. package/js/ln-modal-coordinator/ln-modal-coordinator-dev.scss +0 -8
  449. package/js/ln-modal-coordinator/ln-modal-coordinator.js +0 -1
  450. package/js/ln-nav/ln-nav.js +0 -1
  451. package/js/ln-number/README.md +0 -149
  452. package/js/ln-number/ln-number.js +0 -1
  453. package/js/ln-number/src/ln-number.js +0 -443
  454. package/js/ln-options/ln-options.js +0 -1
  455. package/js/ln-popover/ln-popover.js +0 -1
  456. package/js/ln-progress/ln-progress.js +0 -1
  457. package/js/ln-progress/src/ln-progress.js +0 -98
  458. package/js/ln-router/ln-router.js +0 -1
  459. package/js/ln-search/README.md +0 -158
  460. package/js/ln-search/ln-search-dev.scss +0 -8
  461. package/js/ln-search/ln-search.js +0 -1
  462. package/js/ln-search/src/ln-search.js +0 -117
  463. package/js/ln-slug/ln-slug.js +0 -1
  464. package/js/ln-sort/ln-sort.js +0 -1
  465. package/js/ln-sort/src/ln-sort.js +0 -178
  466. package/js/ln-sortable/ln-sortable.js +0 -1
  467. package/js/ln-stat/ln-stat.js +0 -1
  468. package/js/ln-table/ln-table.js +0 -1
  469. package/js/ln-table/ln-table.scss +0 -11
  470. package/js/ln-table-coordinator/README.md +0 -73
  471. package/js/ln-table-coordinator/ln-table-coordinator.js +0 -1
  472. package/js/ln-table-coordinator/src/ln-table-coordinator.js +0 -160
  473. package/js/ln-tabs/ln-tabs-dev.scss +0 -18
  474. package/js/ln-tabs/ln-tabs.js +0 -1
  475. package/js/ln-tabs/src/ln-tabs.js +0 -188
  476. package/js/ln-time/ln-time.js +0 -1
  477. package/js/ln-toast/ln-toast.js +0 -1
  478. package/js/ln-toggle/ln-toggle.js +0 -1
  479. package/js/ln-tooltip/ln-tooltip.js +0 -1
  480. package/js/ln-tooltip/ln-tooltip.scss +0 -9
  481. package/js/ln-translations/ln-translations.js +0 -1
  482. package/js/ln-upload/README.md +0 -255
  483. package/js/ln-upload/ln-upload.js +0 -1
  484. package/js/ln-upload/ln-upload.scss +0 -20
  485. package/js/ln-upload/src/ln-upload.js +0 -423
  486. package/js/ln-validate/ln-validate.js +0 -1
  487. package/scss/components/_button.scss +0 -20
  488. package/scss/components/_confirm.scss +0 -5
  489. package/scss/components/_form.scss +0 -107
  490. package/scss/components/_status-badge.scss +0 -4
  491. package/scss/components/_toast.scss +0 -68
  492. package/scss/components/_upload.scss +0 -51
  493. package/scss/config/_theme.scss +0 -216
  494. package/scss/config/mixins/_btn.scss +0 -179
  495. package/scss/config/mixins/_confirm.scss +0 -31
  496. package/scss/config/mixins/_dropdown.scss +0 -141
  497. package/scss/config/mixins/_popover.scss +0 -64
  498. package/scss/ln-ashlar-dev.scss +0 -39
  499. /package/{js → components}/ln-core/positioning.js +0 -0
  500. /package/{js → components}/ln-core/reactive.js +0 -0
  501. /package/{js → components}/ln-filter/ln-filter.scss +0 -0
  502. /package/{js → components}/ln-icon/ln-icon.js +0 -0
  503. /package/{js → components}/ln-icon/src/ln-icon.js +0 -0
  504. /package/{js → components}/ln-search/ln-search.scss +0 -0
  505. /package/{js → components}/ln-toast/template.html +0 -0
  506. /package/{scss → theme}/components/_accordion.scss +0 -0
  507. /package/{scss → theme}/components/_ajax.scss +0 -0
  508. /package/{scss → theme}/components/_alert.scss +0 -0
  509. /package/{scss → theme}/components/_avatar.scss +0 -0
  510. /package/{scss → theme}/components/_breadcrumbs.scss +0 -0
  511. /package/{scss → theme}/components/_card.scss +0 -0
  512. /package/{scss → theme}/components/_chip.scss +0 -0
  513. /package/{scss → theme}/components/_circular-progress.scss +0 -0
  514. /package/{scss → theme}/components/_editor.scss +0 -0
  515. /package/{scss → theme}/components/_empty-state.scss +0 -0
  516. /package/{scss → theme}/components/_filter.scss +0 -0
  517. /package/{scss → theme}/components/_link.scss +0 -0
  518. /package/{scss → theme}/components/_loader.scss +0 -0
  519. /package/{scss → theme}/components/_modal.scss +0 -0
  520. /package/{scss → theme}/components/_nav.scss +0 -0
  521. /package/{scss → theme}/components/_page-header.scss +0 -0
  522. /package/{scss → theme}/components/_popover.scss +0 -0
  523. /package/{scss → theme}/components/_progress.scss +0 -0
  524. /package/{scss → theme}/components/_prose.scss +0 -0
  525. /package/{scss → theme}/components/_scrollbar.scss +0 -0
  526. /package/{scss → theme}/components/_sections.scss +0 -0
  527. /package/{scss → theme}/components/_sidebar.scss +0 -0
  528. /package/{scss → theme}/components/_stat-card.scss +0 -0
  529. /package/{scss → theme}/components/_stepper.scss +0 -0
  530. /package/{scss → theme}/components/_timeline.scss +0 -0
  531. /package/{scss → theme}/components/_tooltip.scss +0 -0
  532. /package/{scss → theme}/components/_translations.scss +0 -0
  533. /package/{scss → theme}/config/_icons.scss +0 -0
  534. /package/{scss → theme}/config/_mixins.scss +0 -0
  535. /package/{scss → theme}/config/mixins/_ajax.scss +0 -0
  536. /package/{scss → theme}/config/mixins/_avatar.scss +0 -0
  537. /package/{scss → theme}/config/mixins/_breadcrumbs.scss +0 -0
  538. /package/{scss → theme}/config/mixins/_breakpoints.scss +0 -0
  539. /package/{scss → theme}/config/mixins/_chip.scss +0 -0
  540. /package/{scss → theme}/config/mixins/_circular-progress.scss +0 -0
  541. /package/{scss → theme}/config/mixins/_display.scss +0 -0
  542. /package/{scss → theme}/config/mixins/_empty-state.scss +0 -0
  543. /package/{scss → theme}/config/mixins/_focus.scss +0 -0
  544. /package/{scss → theme}/config/mixins/_interaction.scss +0 -0
  545. /package/{scss → theme}/config/mixins/_kbd.scss +0 -0
  546. /package/{scss → theme}/config/mixins/_link.scss +0 -0
  547. /package/{scss → theme}/config/mixins/_page-header.scss +0 -0
  548. /package/{scss → theme}/config/mixins/_position.scss +0 -0
  549. /package/{scss → theme}/config/mixins/_progress.scss +0 -0
  550. /package/{scss → theme}/config/mixins/_prose.scss +0 -0
  551. /package/{scss → theme}/config/mixins/_shadows.scss +0 -0
  552. /package/{scss → theme}/config/mixins/_sizing.scss +0 -0
  553. /package/{scss → theme}/config/mixins/_spacing.scss +0 -0
  554. /package/{scss → theme}/config/mixins/_stat-card.scss +0 -0
  555. /package/{scss → theme}/config/mixins/_stepper.scss +0 -0
  556. /package/{scss → theme}/config/mixins/_timeline.scss +0 -0
  557. /package/{scss → theme}/config/mixins/_transitions.scss +0 -0
  558. /package/{scss → theme}/config/mixins/_translations.scss +0 -0
  559. /package/{scss → theme}/tabler-icons.txt +0 -0
@@ -4,11 +4,6 @@ export class MutationReceipts {
4
4
  }
5
5
 
6
6
  wait(requestId) {
7
- if (!requestId) return Promise.reject(new Error('Mutation requestId is required'));
8
- if (this._pending.has(requestId)) {
9
- return Promise.reject(new Error(`Duplicate mutation requestId: ${requestId}`));
10
- }
11
-
12
7
  return new Promise((resolve, reject) => {
13
8
  this._pending.set(requestId, { resolve, reject });
14
9
  });
@@ -1,19 +1,22 @@
1
1
  # ln-data-store
2
2
 
3
- A zero-dependency, local-first **Database Cache Store** backed by standard browser `IndexedDB`. It acts as a pure client-side database cache, maintaining records locally, executing high-performance querying in-memory, and applying optimistic mutations directly — a record is a record, with no pending state, snapshots, or rollback machinery.
4
-
5
- It possesses no visual interface and is **completely blind to the network** (no fetch, status codes, paths, or URLs). Instead, it communicates strictly via custom DOM events, allowing the parent **Data Coordinator** to orchestrate syncs and mutations.
3
+ > Applied to a hidden element via `data-ln-data-store id="name"`. It initializes and queries an IndexedDB database
4
+ > (`ln_app_cache`) with automatic schema upgrades and optional AES-GCM encryption. It is completely blind to the network:
5
+ > listening for request CustomEvents (`ln-data-store:request-query`, `request-save`, `request-delete`), resolving results
6
+ > against local records, and emitting `ln-data-store:stale` when cached data exceeds its configured timeout.
6
7
 
7
8
  ---
8
9
 
9
10
  ## 📦 Declarative Setup in HTML
10
11
 
11
12
  ```html
12
- <div data-ln-data-store="documents"
13
- data-ln-data-store-stale="300"
14
- data-ln-data-store-indexes="status,department,updated_at"
15
- data-ln-data-store-search-fields="title,owner">
16
- </div>
13
+ <ul id="documents"
14
+ data-ln-data-store
15
+ data-ln-data-store-stale="300"
16
+ data-ln-data-store-indexes="status,department,updated_at"
17
+ data-ln-data-store-search-fields="title,owner"
18
+ hidden>
19
+ </ul>
17
20
  ```
18
21
 
19
22
  ---
@@ -22,10 +25,15 @@ It possesses no visual interface and is **completely blind to the network** (no
22
25
 
23
26
  | Attribute | Description | Default |
24
27
  |---|---|---|
25
- | `data-ln-data-store="name"` | Declares the store name. | *Required* |
28
+ | `id="name"` | Unique identifier of the store (the store name). | *Required* |
29
+ | `data-ln-data-store` | Marker attribute to declare the component. | *Required* |
26
30
  | `data-ln-data-store-stale="N"` | Seconds before cache is considered stale. Set to `never` or `-1` to disable staleness. | `300` |
27
31
  | `data-ln-data-store-indexes="…"` | Comma-separated list of IndexedDB index fields. | `""` |
28
32
  | `data-ln-data-store-search-fields="…"` | Comma-separated list of text-search fields for local query search matching. | `""` |
33
+ | `data-ln-data-store-window="N"` | Enables windowed residency mode. Caps how many records stay resident — both the LRU position index and the records held in IndexedDB. | *Disabled* |
34
+ | `data-ln-data-store-window-page="N"` | Page size of slices requested from the server. | `200` |
35
+ | `data-ln-data-store-no-local-query` | Leave queries to the server: the store stops answering reads from its own records. Read live, so it can be flipped per situation. | *Absent* |
36
+ | `data-ln-data-store-frozen` | **Runtime-written, dev-only.** Set by the component itself when `data-ln-data-store` or `-indexes` is edited after init; surfaced as a dev-only diagnostic banner (`[data-ln-debug]`). Not authored. | *Absent* |
29
37
 
30
38
  ---
31
39
 
@@ -35,7 +43,7 @@ It possesses no visual interface and is **completely blind to the network** (no
35
43
 
36
44
  - **Dynamic Schema Discovery:** Upon initialization, the component scans the document for all `[data-ln-data-store]` elements. It collects their store names and index definitions.
37
45
  - **Auto Upgrade:** If new stores or indexes are declared in the HTML markup, the component closes the active connection, increments the database version, and dynamically creates the missing stores and indexes in `onupgradeneeded`.
38
- - **Memory Fallback:** If the browser does not support IndexedDB, or if database open/upgrade operations fail/are blocked, the component transparently falls back to an in-memory data store.
46
+ - **Initialization Failure:** If the browser does not support IndexedDB, or if database open/upgrade operations fail/are blocked, the component fails initialization, sets `initializationError`, and dispatches `ln-data-store:initialization-error`. There is no in-memory fallback; consumers are responsible for handling storage unavailability.
39
47
 
40
48
  ---
41
49
 
@@ -54,7 +62,7 @@ The store supports local record encryption to secure cached data in IndexedDB:
54
62
  Access the database layer directly via the `lnDataStore` property on the store container element:
55
63
 
56
64
  ```javascript
57
- const storeEl = document.querySelector('[data-ln-data-store="documents"]');
65
+ const storeEl = document.getElementById('documents');
58
66
  const store = storeEl.lnDataStore;
59
67
  ```
60
68
 
@@ -89,10 +97,11 @@ store.setPresenters({
89
97
  });
90
98
  ```
91
99
 
92
- ### 3. Public Methods
100
+ ### 3. Public Properties & Methods
93
101
 
94
- | Method | Return Type | Description |
102
+ | Property/Method | Type / Return Type | Description |
95
103
  |---|---|---|
104
+ | `query` | `Object` | Active query filters and search terms: `{ filters: {}, search: '' }`. |
96
105
  | `getAll(options)` | `Promise<Object>` | Retrieves records. Options support `sort` (`{ field, direction }`), `filters` (`{ field: Array }`), `search` (`String`), `offset`, `limit`. Returns `{ data, total, filtered }`. |
97
106
  | `getById(id)` | `Promise<Object\|null>` | Returns a single record (decorated with presenters) or `null`. |
98
107
  | `count(filters)` | `Promise<Number>` | Returns the total count of records. If filters are provided, returns the filtered count. |
@@ -103,11 +112,26 @@ store.setPresenters({
103
112
  | `fullReload()` | `Promise<void>` | Clears the IndexedDB store, resets sync metadata, and triggers a sync. |
104
113
  | `destroy()` | `void` | Cleans up the instance, removes event listeners, and deletes the DOM reference. |
105
114
 
115
+ ### 4. Windowed Residency (Virtualization)
116
+
117
+ When `data-ln-data-store-window` is present, the store operates in windowed residency mode. The server is the authority on record order: the store keeps a memory index of logical positions to record IDs (`Map<position, id>`) capped at the window size. A view's range request is resolved through that index and materialised from IndexedDB; a missing page dispatches `ln-data-store:request-page`. Records the index evicts are deleted from IndexedDB too, so the window bounds what is stored, not only what is positioned. Only IDs the index itself placed can be evicted, so a locally created record the server has not positioned is never dropped.
118
+
119
+ A query change resets the index — positions are query-relative and become meaningless. Until the server's first page for the new query lands, the store answers from the records it already holds and marks the answer **provisional**: rows only, no totals. Counts and scroll geometry hold at whatever the last authoritative answer established, and the server's answer supersedes the provisional one at the next generation. The local pass stops as soon as the requested slice is full, so its cost tracks the viewport rather than the store.
120
+
121
+ `data-ln-data-store-no-local-query` turns that off: the store reports the window as unresolved and the view waits for the server. Already-positioned rows are still served from cache — those are the server's own answer, only materialised locally.
122
+
123
+ Windowed residency and coordinator autosync are mutually exclusive in practice: a full sync inserts records the index never positioned, which the window cannot bound.
124
+
106
125
  ---
107
126
 
108
127
  ## ⚡ DOM Events
109
128
 
110
- ### Commands (Listened to by the Store)
129
+ ### Commands & Query Events (Listened to by the Store)
130
+
131
+ | Event | `e.detail` payload | Description |
132
+ |---|---|---|
133
+ | `ln-search:change` | `{ term }` | Updates the active `query.search` term and dispatches `ln-data-store:query-changed`. |
134
+ | `ln-filter:change` | `{ key, values, targetId }` | Updates the active `query.filters[key]` and dispatches `ln-data-store:query-changed`. |
111
135
 
112
136
  All mutations must be routed via DOM events. **Never invoke write methods directly.** The caller is responsible for supplying a `tempId` (e.g. `'_temp_' + crypto.randomUUID()`) when creating records:
113
137
 
@@ -158,6 +182,8 @@ These events bubble up and can be listened to by coordinators or rendering views
158
182
 
159
183
  | Event | `e.detail` payload | Description |
160
184
  |---|---|---|
185
+ | `ln-data-store:request-page` | `{ store, offset, limit, query, queryGen }` | Dispatched in windowed residency when pages within the sliding window are missing and must be fetched from the server. |
186
+ | `ln-data-store:query-changed` | `{ store, query }` | Emitted when store search query or column filters change. |
161
187
  | `ln-data-store:initialized` | `{ store, hasCache, lastSyncedAt, count }` | Emitted once after IndexedDB connection opens. The instance's `ready` promise resolves after this state is committed. |
162
188
  | `ln-data-store:initialization-error` | `{ store, error }` | IndexedDB could not be initialized. `ready` still resolves after `initializationError` is set, allowing a coordinator to route reads to its connector. |
163
189
  | `ln-data-store:ready` | `{ store, count, source }` | Emitted when data is ready. `source` is `'cache'` (init) or `'server'` (first sync). |
@@ -167,7 +193,6 @@ These events bubble up and can be listened to by coordinators or rendering views
167
193
  | `ln-data-store:deleted` | `{ store, id \| ids, requestId? }` | Emitted after optimistic delete or bulk delete. |
168
194
  | `ln-data-store:synced` | `{ store, added, deleted, changed }` | Emitted after subsequent delta sync merges. |
169
195
  | `ln-data-store:sync-error` | `{ store, error, status }` | A connector sync failed; `isSyncing` has been cleared so online/visibility retry can proceed. |
170
- | `ln-data-store:destroyed` | `{ store }` | Emitted when the store instance is destroyed. |
171
196
  | `ln-data-store:mutation-error` | `{ store, action, requestId, error }` | A serialized local mutation failed. `requestId` lets a coordinator correlate reconciliation failures. |
172
197
 
173
198
  ### Global System Events
@@ -189,7 +214,7 @@ These events bubble up and can be listened to by coordinators or rendering views
189
214
 
190
215
  ## 🔧 Internals
191
216
 
192
- Source: `js/ln-data-store/ln-data-store.js`.
217
+ Source: `components/ln-data-store/ln-data-store.js`.
193
218
 
194
219
  ### Shared IndexedDB connection
195
220
 
@@ -234,7 +259,7 @@ The store renders nothing. Its only DOM-side effects: attaches `request-*` liste
234
259
 
235
260
  ### Mutation error reconciliation
236
261
 
237
- `ln-data-store` has no concept of "confirm" or "revert" — a `request-update`/`request-delete` is applied unconditionally to the local cache. Reconciling server-side mutation errors (retry, drop, conflict-server-wins, auth-pause) is `ln-data-coordinator`'s job — see [`js/ln-data-coordinator/README.md`](../ln-data-coordinator/README.md) §Error reconciliation policy.
262
+ `ln-data-store` has no concept of "confirm" or "revert" — a `request-update`/`request-delete` is applied unconditionally to the local cache. Reconciling server-side mutation errors (retry, drop, conflict-server-wins, auth-pause) is `ln-data-coordinator`'s job — see [`components/ln-data-coordinator/README.md`](../ln-data-coordinator/README.md) §Error reconciliation policy.
238
263
 
239
264
  ### At-rest encryption reference
240
265
 
@@ -0,0 +1,13 @@
1
+ @use '../../theme/config/mixins' as *;
2
+
3
+ [data-ln-debug] {
4
+ // 1. Store Table Name Check: data-ln-data-store requires an ID to serve as the store table name
5
+ [data-ln-data-store]:not([id]) {
6
+ @include dev-dom-error("[data-ln-data-store] requires a unique 'id' attribute as store table name.");
7
+ }
8
+
9
+ // 2. Frozen Attribute Edit: the IndexedDB schema and store name are fixed at init time
10
+ [data-ln-data-store][data-ln-data-store-frozen] {
11
+ @include dev-dom-error("data-ln-data-store / -indexes are read once at init — the IndexedDB schema can only change in a versionchange transaction. Reload to apply.");
12
+ }
13
+ }
@@ -0,0 +1 @@
1
+ (function(){"use strict";function ie(){return typeof window>"u"?!1:window.lnDebug===!0||window.lnCore&&window.lnCore._debugSink?!0:typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug")||document.body.querySelector("[data-ln-debug]")!==null:!1}function Z(e){if(typeof window>"u")return!1;if(window.lnDebug===!0)return!0;if(window.lnCore&&window.lnCore._debugIsContained)return e?window.lnCore._debugIsContained(e):window.lnCore._debugSink!==null;if(e&&e.closest){const n=e.closest("[data-ln-debug]");return!(!n||typeof document<"u"&&n===document.documentElement)}return typeof document<"u"&&document.body?document.body.hasAttribute("data-ln-debug"):!1}if(typeof window<"u"&&(window.lnCore=window.lnCore||{},!window.lnCore._warnBound)){window.lnCore._warnBound=!0;const e=console.warn;console.warn=function(...n){if(typeof n[0]=="string"&&(n[0].startsWith("[ln-")||n[0].startsWith("[lnCore"))){let i=null;for(let a=1;a<n.length;a++)if(n[a]&&n[a].nodeType===1){i=n[a];break}if(!Z(i))return}e.apply(console,n)}}function v(e,n,o){const i=o||{};window.lnCore._debugSink&&window.lnCore._debugSink("event",n,e,i),e.dispatchEvent(new CustomEvent(n,{bubbles:!0,detail:i}))}function G(e,n){if(!document.body){document.addEventListener("DOMContentLoaded",function(){G(e,n)}),console.warn("["+n+'] Script loaded before <body> — add "defer" to your <script> tag');return}e()}const ae={};function qe(e,n){ae[e]=n}function Le(e){return ae[e]||{ingress:n=>n,egress:n=>n}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerDataMapper=qe,window.lnCore.getDataMapper=Le);function Ne(){typeof window>"u"||(window.lnCore=window.lnCore||{},!window.lnCore._localeObserverBound&&(window.lnCore._localeObserverBound=!0,G(function(){new MutationObserver(function(){v(document,"ln-core:locale-change",{})}).observe(document.documentElement,{attributes:!0,attributeFilter:["lang"],subtree:!0})},"ln-core")))}const se={};function De(e,n){if(!e||typeof n!="object")return;const o=e.toLowerCase().split("-")[0];se[o]=n}function Te(e){if(!e)return null;const n=e.toLowerCase().split("-")[0];return se[n]||null}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.registerLocaleFallback=De,window.lnCore.getLocaleFallback=Te,window.lnCore.ensureLocaleObserver=Ne);const X={};function ze(e,n){X[e]||(X[e]=document.querySelector('[data-ln-template="'+e+'"]'));const o=X[e];return o?o.content.cloneNode(!0):(console.warn("["+(n||"ln-core")+'] Template "'+e+'" not found'),null)}function le(e,n){if(!e||!n)return e;const o=e.querySelectorAll("[data-ln-field]");for(let c=0;c<o.length;c++){const f=o[c],h=f.getAttribute("data-ln-field");n[h]!=null&&(f.textContent=n[h])}const i=e.querySelectorAll("[data-ln-attr]");for(let c=0;c<i.length;c++){const f=i[c],h=f.getAttribute("data-ln-attr").split(",");for(let p=0;p<h.length;p++){const b=h[p].trim().split(":");if(b.length!==2)continue;const _=b[0].trim(),k=b[1].trim();n[k]!=null&&f.setAttribute(_,n[k])}}const a=e.querySelectorAll("[data-ln-show]");for(let c=0;c<a.length;c++){const f=a[c],h=f.getAttribute("data-ln-show");h in n&&f.classList.toggle("hidden",!n[h])}const s=e.querySelectorAll("[data-ln-class]");for(let c=0;c<s.length;c++){const f=s[c],h=f.getAttribute("data-ln-class").split(",");for(let p=0;p<h.length;p++){const b=h[p].trim().split(":");if(b.length!==2)continue;const _=b[0].trim(),k=b[1].trim();k in n&&f.classList.toggle(_,!!n[k])}}return e}function Oe(e,n){e.matches&&e.matches("[data-ln-form], [data-ln-fillable]")&&(window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",e,n??null),e.dispatchEvent(new CustomEvent("ln-fill",{detail:n??null,bubbles:!0})));const o=e.querySelectorAll("[data-ln-form], [data-ln-fillable]");for(let i=0;i<o.length;i++)window.lnCore._debugSink&&window.lnCore._debugSink("event","ln-fill",o[i],n??null),o[i].dispatchEvent(new CustomEvent("ln-fill",{detail:n??null,bubbles:!0}));return e}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._fillBound||(window.lnCore._fillBound=!0,document.addEventListener("ln-fill",function(e){if(!(!e.target.matches||!e.target.matches("[data-ln-fillable]")))if(e.detail)le(e.target,e.detail);else{const n=e.target.querySelectorAll("[data-ln-field]");for(let o=0;o<n.length;o++)n[o].textContent=""}})));function ce(e,n){if(!e||!n)return e;const o=document.createTreeWalker(e,NodeFilter.SHOW_TEXT);for(;o.nextNode();){const s=o.currentNode;s.textContent.indexOf("{{")!==-1&&(s.textContent=s.textContent.replace(/\{\{\s*(\w+)\s*\}\}/g,function(c,f){return n[f]!==void 0?n[f]:""}))}const i=function(s,c){return n[c]!==void 0?n[c]:""},a=Array.from(e.querySelectorAll("*"));e.nodeType===1&&a.push(e);for(let s=0;s<a.length;s++){const c=a[s],f=c.attributes;for(let h=0;h<f.length;h++){const p=f[h];p.value.indexOf("{{")!==-1&&c.setAttribute(p.name,p.value.replace(/\{\{\s*(\w+)\s*\}\}/g,i))}}return e}function Re(e,n,o,i,a,s){const c={};for(let h=0;h<e.children.length;h++){const p=e.children[h],b=p.getAttribute("data-ln-render-key");b&&(c[b]=p)}const f=document.createDocumentFragment();for(let h=0;h<n.length;h++){const p=n[h],b=String(i(p));let _=c[b];if(_)a(_,p,h);else{const k=ze(o,s);if(!k||(ce(k,p),_=k.firstElementChild,!_))continue;_.setAttribute("data-ln-render-key",b),a(_,p,h)}f.appendChild(_)}e.textContent="",e.appendChild(f)}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.fillTemplate=ce,window.lnCore.fill=le,window.lnCore.lnFill=Oe,window.lnCore.renderList=Re);function ue(e,n,o){const i=e.getAttribute(n);return i===null?o:i}function Y(e,n,o){const i=parseInt(e.getAttribute(n),10);return isNaN(i)?o:i}function de(e,n,o=!1){const i=e.getAttribute(n);if(i===null)return!!o;const a=i.trim().toLowerCase();return!(a==="false"||a==="0")}function fe(e,n){return(e.getAttribute(n)||"").split(",").map(o=>o.trim()).filter(Boolean)}const ee=new Map;function Pe(e,n){const o=(e?e.join("|"):"")+"::"+n;if(ee.has(o))return ee.get(o);const i=new Set(e||[]),a=function(s,c){const f=s.getAttribute(c);return f!==null&&i.has(f)?f:n};return ee.set(o,a),a}function Be(e,n,o){const i=parseFloat(e.getAttribute(n));return isNaN(i)?o:i}function Me(e,n,o){const i=e.getAttribute(n);if(!i)return o;try{return JSON.parse(i)}catch{return o}}function he(e,n,o,i,a){if(!e||n===null)return!0;const s=i||"ln-component",c=e.type;if(c==="trigger"||c==="marker"||c==="string"||c==="list"||!c||n===""&&e.fallback!==void 0)return!0;const f=h=>{a?console.warn(h,a):console.warn(h)};if(c==="boolean"){const h=n.trim().toLowerCase();return h!==""&&h!=="true"&&h!=="false"&&h!=="1"&&h!=="0"?(f(`[${s}] Invalid value "${n}" for boolean attribute "${o}". Allowed: "true", "false", or presence-only.`),!1):!0}if(c==="enum"){const h=e.values||[];return h.includes(n)?!0:(f(`[${s}] Invalid value "${n}" for attribute "${o}". Allowed: ${h.join(", ")}. Fallback: "${e.fallback}".`),!1)}if(c==="integer"){if(!/^-?\d+$/.test(n))return f(`[${s}] Invalid integer "${n}" for attribute "${o}". Fallback: ${e.fallback}.`),!1;const h=parseInt(n,10);return e.min!==void 0&&h<e.min?(f(`[${s}] Value ${h} for attribute "${o}" is less than min (${e.min}).`),!1):e.max!==void 0&&h>e.max?(f(`[${s}] Value ${h} for attribute "${o}" is greater than max (${e.max}).`),!1):!0}if(c==="float")return isNaN(Number(n))?(f(`[${s}] Invalid float "${n}" for attribute "${o}". Fallback: ${e.fallback}.`),!1):!0;if(c==="json")try{return JSON.parse(n),!0}catch(h){return f(`[${s}] Invalid JSON for attribute "${o}": ${h.message}. Fallback: ${e.fallback}.`),!1}return!0}function Fe(e,n,o){for(const i in o){const[a,s,c]=o[i];Object.defineProperty(e,i,{get:function(){return a(n,s,c)},enumerable:!0,configurable:!0})}return e}function je(e){const n={};for(const o in e){const i=e[o];if(!i||!i.prop)continue;let a=i.read;if(!a&&i.type)switch(i.type){case"enum":a=Pe(i.values,i.fallback);break;case"integer":a=Y;break;case"float":a=Be;break;case"boolean":a=de;break;case"list":a=fe;break;case"json":a=Me;break;case"string":default:a=ue;break}a||(a=ue),n[i.prop]=[a,o,i.fallback]}return n}function $e(e){const n={};for(const o in e){const i=e[o];i&&i.effect&&(n[o]=i.effect)}return Object.keys(n).length?n:null}function Ke(e){if(e.onAttrChange&&!e.declared)return null;const n=new Set;if(e.effects)for(const o in e.effects)n.add(o);if(e.onAttrChange&&e.declared)for(const o of e.declared)n.add(o);return n}function te(e,n,o,i){if(e.nodeType!==1)return;const s=n.indexOf("[")!==-1||n.indexOf(".")!==-1||n.indexOf("#")!==-1?n:"["+n+"]",c=Array.from(e.querySelectorAll(s));e.matches&&e.matches(s)&&c.push(e);for(const f of c)if(!f[o]){window.lnCore._persistSink&&f.hasAttribute("data-ln-persist")&&window.lnCore._persistSink(f,n);try{f[o]=new i(f)}catch(h){console.error("["+o+"] init failed",f,h)}}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[]);function Ve(){return typeof window<"u"&&window.lnCore&&window.lnCore._bootHolds||0}function Ge(e){typeof window<"u"?(window.lnCore=window.lnCore||{},window.lnCore._bootHolds=window.lnCore._bootHolds||0,window.lnCore._bootQueue=window.lnCore._bootQueue||[],window.lnCore._bootHolds>0?window.lnCore._bootQueue.push(e):setTimeout(e,0)):e()}function we(){window.lnCore=window.lnCore||{};const e=window.lnCore._attrRegistry=window.lnCore._attrRegistry||{byAttr:new Map,reactive:[],persist:[]};return e.byReactive=e.byReactive||new Map,e.reactiveWildcard=e.reactiveWildcard||[],e}function He(e){const n=we(),o=e.observed||[];for(let i=0;i<o.length;i++){const a=o[i];n.byAttr.has(a)||n.byAttr.set(a,[]),n.byAttr.get(a).push(e)}if(n.byDeclaredAttr=n.byDeclaredAttr||new Map,e.attributes)for(const i in e.attributes){n.byDeclaredAttr.has(i)||n.byDeclaredAttr.set(i,[]);const a=n.byDeclaredAttr.get(i);a.some(s=>s.componentTag===e.componentTag)||a.push({spec:e.attributes[i],componentTag:e.componentTag})}if(e.onAttrChange||e.effects){n.reactive.push(e);const i=Ke(e);if(i===null)n.reactiveWildcard.push(e);else for(const a of i)n.byReactive.has(a)||n.byReactive.set(a,[]),n.byReactive.get(a).push(e)}e.persist&&n.persist.push(e)}function ye(e,n,o,i){for(let a=0;a<e.length;a++){const s=e[a];if(!n[s.attribute])continue;const c=s.effects&&s.effects[o];c?c(n,o,i):s.onAttrChange&&(!s.declared||s.declared.has(o))&&s.onAttrChange(n,o,i)}}function We(e){const n=e.target,o=e.attributeName;if(e.oldValue===n.getAttribute(o))return;const i=we(),a=i.byAttr.get(o);if(ie()&&i.byDeclaredAttr&&i.byDeclaredAttr.has(o)&&Z(n)){const s=i.byDeclaredAttr.get(o),c=n.getAttribute(o);for(let f=0;f<s.length;f++)he(s[f].spec,c,o,s[f].componentTag,n)}if(window.lnCore._debugSink&&(o.indexOf("data-ln-")===0||a)&&window.lnCore._debugSink("attr",o,n,{oldValue:e.oldValue,newValue:n.getAttribute(o)}),o.indexOf("data-ln-")===0){const s=i.byReactive.get(o);s&&ye(s,n,o,e.oldValue),i.reactiveWildcard.length&&ye(i.reactiveWildcard,n,o,e.oldValue)}if(a)for(let s=0;s<a.length;s++){const c=a[s];if(c.handler){c.handler(n,o,e.oldValue);continue}c.onAttributeChange&&n[c.attribute]?c.onAttributeChange(n,o):(te(n,c.selector,c.attribute,c.ComponentFn),c.onInit&&c.onInit(n))}}function Ue(){window.lnCore=window.lnCore||{},!window.lnCore._attrObserverBound&&(window.lnCore._attrObserverBound=!0,G(function(){new MutationObserver(function(n){for(let o=0;o<n.length;o++)try{We(n[o])}catch(i){console.error("[ln-core] mutation handler failed",n[o].target,i)}}).observe(document.body,{attributes:!0,subtree:!0,attributeOldValue:!0})},"ln-core"))}function pe(){return window.lnCore=window.lnCore||{},window.lnCore._lifecycleRegistry=window.lnCore._lifecycleRegistry||[]}function Qe(e){const n=pe();if(n.length){if(e.target)for(let o=0;o<n.length;o++){const i=n[o];if(i.onSubtreeChange){const a=i.query,s=e.target.nodeType===1?e.target.matches(a)?e.target:e.target.closest(a):e.target.parentElement?e.target.parentElement.closest(a):null;s&&i.onSubtreeChange(s,e)}}for(let o=0;o<e.addedNodes.length;o++){const i=e.addedNodes[o];if(i.nodeType===1)for(let a=0;a<n.length;a++){const s=n[a];te(i,s.selector,s.attribute,s.ComponentFn),s.onInit&&s.onInit(i)}}for(let o=0;o<e.removedNodes.length;o++){const i=e.removedNodes[o];if(i.nodeType===1)for(let a=0;a<n.length;a++){const s=n[a],c=s.query,f=Array.from(i.querySelectorAll(c));i.matches&&i.matches(c)&&f.push(i);for(let h=0;h<f.length;h++){const p=f[h];if(!document.contains(p)){const b=p[s.attribute];if(b&&typeof b.destroy=="function")try{b.destroy()}catch(_){console.error("["+s.attribute+"] destroy failed",p,_)}delete p[s.attribute]}}}}}}function Je(){window.lnCore=window.lnCore||{},!window.lnCore._lifecycleObserverBound&&(window.lnCore._lifecycleObserverBound=!0,G(function(){new MutationObserver(function(n){for(let o=0;o<n.length;o++){const i=n[o];if(i.type==="childList")try{Qe(i)}catch(a){console.error("[ln-core] lifecycle handler failed",i.target,a)}}}).observe(document.body,{childList:!0,subtree:!0})},"ln-core"))}function Ze(e,n,o,i,a={}){const s=a.extraAttributes||[],c=a.onAttributeChange||null,f=a.onSubtreeChange||null,h=a.onInit||null,p=a.onAttrChange||null,b=a.effects||null,_=a.attributes||null,k=_?$e(_):b,R=_?new Set(Object.keys(_)):null,P=a.persist||null;function g(E){const I=E||document.body;if(te(I,e,n,o),_&&ie())for(const B in _){const H=_[B],$=Array.from(I.querySelectorAll("["+B+"]"));I.matches&&I.matches("["+B+"]")&&$.push(I);for(let F=0;F<$.length;F++){const O=$[F];Z(O)&&he(H,O.getAttribute(B),B,i,O)}}h&&h(I)}const S=[];if(e.indexOf("[")!==-1){const E=/\[([\w-]+)/g;let I;for(;(I=E.exec(e))!==null;)S.push(I[1])}else S.push(e);He({selector:e,attribute:n,componentTag:i,attributes:_,ComponentFn:o,onInit:h,observed:S.concat(s),onAttributeChange:c,onAttrChange:p,effects:k,declared:R,persist:P}),Ue();const q=e.indexOf("[")!==-1||e.indexOf(".")!==-1||e.indexOf("#")!==-1?e:"["+e+"]";pe().push({selector:e,attribute:n,ComponentFn:o,onInit:h,onSubtreeChange:f,query:q}),Je(),window[n]=g;function A(){Ve()>0?Ge(function(){g(document.body)}):g(document.body)}return document.readyState==="loading"?document.addEventListener("DOMContentLoaded",A):A(),g}function Xe(e){let n=!1;for(let o=0;o<e.length;o++){const i=e[o];if(!(i===""||i==null)&&(n=!0,!Number.isFinite(Number(i))))return"string"}return n?"number":"string"}function Ye(e,n,o,i){if(o==="number"){const c=parseFloat(e),f=parseFloat(n);return(isNaN(c)?0:c)-(isNaN(f)?0:f)}const a=e!=null?String(e):"",s=n!=null?String(n):"";return i?i.compare(a,s):a<s?-1:a>s?1:0}function ge(e){return(e||"").replace(/^#/,"")}function ne(e){const n=e===void 0?location.hash:e,o={},i=ge(n);if(!i)return o;const a=i.split("&");for(let s=0;s<a.length;s++){const c=a[s];if(!c)continue;const f=c.indexOf(":"),h=f>-1?c.slice(0,f):c,p=f>-1?c.slice(f+1):"";if(h)try{o[h]=decodeURIComponent(p)}catch{o[h]=p}}return o}function et(e){if(!e)return null;const n=ne();return e in n?n[e]:null}function tt(e,n){if(!e)return;const o=ne();n==null?delete o[e]:o[e]=String(n);const a=Object.keys(o).map(function(s){const c=o[s];return c===""?s:s+":"+encodeURIComponent(c)}).join("&");ge(location.hash)!==a&&(location.hash=a)}function nt(e){return e.button===1||e.ctrlKey||e.metaKey||e.shiftKey?!1:(e.preventDefault(),!0)}function rt(e,n){if(!e||!e.hasAttribute("data-ln-hash"))return null;const o=e.getAttribute("data-ln-hash");if(o&&o.trim()!=="")return o.trim();const i=e.getAttribute("data-ln-sort")||e.getAttribute("data-ln-search-for")||e.getAttribute("data-ln-search")||e.getAttribute("data-ln-filter")||e.id;return i?n?i+"-"+n:i:n||null}function ot(e,n){return!n||n==="none"||e===null||e===void 0?null:String(e)+"."+n}function it(e){return!e||typeof e!="string"?null:e.endsWith(".asc")?{fieldOrColumn:e.slice(0,-4),direction:"asc"}:e.endsWith(".desc")?{fieldOrColumn:e.slice(0,-5),direction:"desc"}:null}function at(e,n){return!e||!Array.isArray(n)||n.length===0?null:e+":"+n.map(encodeURIComponent).join(",")}function st(e){if(!e||typeof e!="string")return null;const n=e.indexOf(":");if(n===-1)return null;const o=e.slice(0,n),i=e.slice(n+1),a=i?i.split(",").map(function(s){try{return decodeURIComponent(s)}catch{return s}}).filter(Boolean):[];return{key:o,values:a}}typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.hashParse=ne,window.lnCore.hashGet=et,window.lnCore.hashSet=tt,window.lnCore.hashLinkClick=nt,window.lnCore.resolveHashNamespace=rt,window.lnCore.hashSortEncode=ot,window.lnCore.hashSortDecode=it,window.lnCore.hashFilterEncode=at,window.lnCore.hashFilterDecode=st);let j=null;const lt="ln-ashlar:storage-salt:v1",me=32768;function be(e){let n="";const o=e.byteLength;for(let i=0;i<o;i+=me)n+=String.fromCharCode.apply(null,e.subarray(i,Math.min(i+me,o)));return btoa(n)}function _e(e){const n=atob(e),o=n.length,i=new Uint8Array(o);for(let a=0;a<o;a++)i[a]=n.charCodeAt(a);return i}function Ce(e,n){let o=j,i={};return typeof CryptoKey<"u"&&e instanceof CryptoKey?o=e:e&&typeof e=="object"&&(i=e,typeof CryptoKey<"u"&&i.key instanceof CryptoKey&&(o=i.key)),{key:o,options:i}}async function ct(e,n={}){if(!e)throw new Error("[ln-crypto] Key derivation failed: Secret string is required");const o=n.method||"pbkdf2",i=new TextEncoder;if(o==="sha256"){const h=await crypto.subtle.digest("SHA-256",i.encode(e));return crypto.subtle.importKey("raw",h,{name:"AES-GCM"},!1,["encrypt","decrypt"])}const a=n.salt||lt,s=typeof a=="string"?i.encode(a):a,c=n.iterations||1e5,f=await crypto.subtle.importKey("raw",i.encode(e),"PBKDF2",!1,["deriveKey"]);return crypto.subtle.deriveKey({name:"PBKDF2",salt:s,iterations:c,hash:"SHA-256"},f,{name:"AES-GCM",length:256},!1,["encrypt","decrypt"])}async function Se(e,n={}){if(!e){j=null;return}try{const o=n.method||"sha256";j=await ct(e,{...n,method:o})}catch(o){throw console.error("[ln-core/crypto] Key derivation failed:",o),j=null,o}}function z(){return j}async function ut(e,n,o){const{key:i}=Ce(n);if(e==null)return e;if(!i)throw new Error("[ln-crypto] Encryption failed: No active cryptographic key provided");try{const a=new TextEncoder,s=crypto.getRandomValues(new Uint8Array(12)),c=typeof e=="string"?e:JSON.stringify(e),f=await crypto.subtle.encrypt({name:"AES-GCM",iv:s},i,a.encode(c));return{v:1,alg:"AES-GCM",encrypted:!0,iv:be(s),data:be(new Uint8Array(f))}}catch(a){throw console.error("[ln-core/crypto] Encryption failed:",a),new Error("[ln-crypto] Encryption failed: "+(a&&a.message?a.message:String(a)))}}async function dt(e,n,o){const{key:i,options:a}=Ce(n),s=a.silent===!0;if(!e||!e.encrypted)return e;if(!i){if(s)return{...e,decryptionError:!0};throw new Error("[ln-crypto] Decryption failed: No active cryptographic key provided")}if(!e.iv||!e.data){if(s)return{...e,decryptionError:!0};throw new Error("[ln-crypto] Decryption failed: Malformed envelope (missing iv or data)")}try{const c=new TextDecoder,f=_e(e.iv),h=_e(e.data),p=await crypto.subtle.decrypt({name:"AES-GCM",iv:f},i,h),b=c.decode(p);try{return JSON.parse(b)}catch{return b}}catch(c){if(s)return{...e,decryptionError:!0};throw console.error("[ln-core/crypto] Decryption failed. Key may be incorrect or payload tampered:",c),new Error("[ln-crypto] Decryption failed. Key may be incorrect or payload tampered: "+(c&&c.message?c.message:String(c)))}}function ft(e){return String(e||"").trim().toLowerCase()}function ht(e){const n=ft(e);return n?n.split(/\s+/).filter(Boolean):[]}function wt(e,n){if(!n||n.length===0)return!0;if(!e)return!1;const o=String(e).toLowerCase();for(let i=0;i<n.length;i++)if(o.indexOf(n[i])===-1)return!1;return!0}function yt(e,n){if(!n||n.length===0)return!0;if(e==null)return!1;const o=String(e).trim().toLowerCase();for(let i=0;i<n.length;i++)if(String(n[i]).trim().toLowerCase()===o)return!0;return!1}function pt(e={}){let n=e.windowSize>0?e.windowSize:1e3,o=e.pageSize>0?e.pageSize:200,i=e.fetchDebounce!=null?e.fetchDebounce:120;const a=typeof e.requestPage=="function"?e.requestPage:()=>{},s=new Map,c=new Set;let f=0,h=0,p=0,b=!1,_=null;function k(g,S){s.delete(g),s.set(g,S)}function R(){if(s.size<=n)return[];const g=[];for(;s.size>n;){const L=s.keys().next().value;g.push(s.get(L)),s.delete(L)}const S=new Set(s.values());return g.filter(L=>!S.has(L))}function P(g,S){c.add(g),clearTimeout(_),_=setTimeout(()=>a(g,o,S),i)}return{get logicalTotal(){return f},set logicalTotal(g){f=g},get grandTotal(){return h},set grandTotal(g){h=g},get queryGen(){return p},set queryGen(g){p=g},get size(){return s.size},get hasLoaded(){return b},getId:g=>{if(!s.has(g))return;const S=s.get(g);return k(g,S),S},ensure:(g,S,L)=>{if(!b&&!c.has(0))return P(0,L);if(f<=0)return;const q=Math.max(0,g),A=Math.min(f,S);for(let E=q;E<A;E++)if(!s.has(E)){const I=Math.floor(E/o)*o;if(!c.has(I))return P(I,L)}},ingest:(g,S,L,q,A)=>{if(A!=null&&A!==p)return[];b=!0,L!=null&&(h=L),q!=null&&(f=q);for(let E=0;E<S.length;E++)k(g+E,S[E]);return c.delete(g),R()},reset:function(){p++,this.clear()},clear:()=>{b=!1,s.clear(),c.clear(),clearTimeout(_)},configure:(g={})=>{let S=[];return g.windowSize>0&&g.windowSize!==n&&(n=g.windowSize,S=R()),g.pageSize>0&&(o=g.pageSize),g.fetchDebounce>=0&&(i=g.fetchDebounce),S}}}function gt(e,n,o){if(!Array.isArray(e)||!n||!n.field)return e;const{field:i,direction:a}=n,s=a==="desc",c=e.map(h=>h?h[i]:void 0),f=Xe(c);return[...e].sort((h,p)=>{const b=h?h[i]:void 0,_=p?p[i]:void 0,k=Ye(b,_,f,o);return s?-k:k})}function Ae(e,n){if(!Array.isArray(e)||!n||typeof n!="object")return e;const o=Object.keys(n).filter(i=>Array.isArray(n[i])&&n[i].length>0);return o.length?e.filter(i=>i?o.every(a=>yt(i[a],n[a])):!1):e}function mt(e,n,o){if(!Array.isArray(e)||!n||!o||!o.length)return e;const i=ht(n);return i.length?e.filter(a=>a?i.every(s=>o.some(c=>{const f=a[c];return f!=null&&wt(String(f),[s])})):!1):e}function bt(e,n,o){if(!Array.isArray(e)||!e.length)return 0;if(o==="count")return e.length;const i=e.map(s=>s&&s[n]!=null?parseFloat(s[n]):NaN).filter(s=>Number.isFinite(s)),a=i.reduce((s,c)=>s+c,0);return o==="sum"?a:o==="avg"&&i.length?a/i.length:0}function _t(e,n={},o=[],i){if(!Array.isArray(e))return{records:[],total:0,filtered:0};const a=e.length;let s=e;n.filters&&(s=Ae(s,n.filters)),n.search&&(s=mt(s,n.search,o));const c=s.length;if(n.sort&&(s=gt(s,n.sort,i)),n.offset||n.limit){const f=n.offset||0,h=n.limit||s.length;s=s.slice(f,f+h)}return{records:s,total:a,filtered:c}}function Ct(e,n){return!Array.isArray(e)||!n||typeof n!="object"?e:e.map(o=>{if(!o)return null;const i={...o};for(const[a,s]of Object.entries(n))if(typeof s=="function")try{i[a]=s(o)}catch{i[a]=void 0}return i})}(function(){const e="data-ln-data-store",n="lnDataStore";if(window[n]!==void 0)return;function o(t,r,l){const u=t.getAttribute(r);if(u==="never"||u==="-1")return-1;const d=parseInt(u,10);return isNaN(d)?l:d}const i={"data-ln-data-store":{type:"marker",effect:Ie,description:"Identifies the element as a data-store definition container"},"data-ln-data-store-indexes":{type:"list",effect:Ie,description:"Comma-separated index field names for the IndexedDB store"},"data-ln-data-store-stale":{prop:"_staleThreshold",type:"integer",read:o,fallback:300,description:"Cache staleness threshold in seconds, or -1/never"},"data-ln-data-store-search-fields":{prop:"_searchFields",type:"list",read:fe,description:"Record fields to index for client-side search"},"data-ln-data-store-no-local-query":{prop:"noLocalQuery",type:"boolean",read:de,description:"Bypasses local IndexedDB query resolution, forcing remote fetching"},"data-ln-data-store-window":{prop:"_windowSize",type:"integer",read:Y,fallback:1e3,min:10,effect:jt,description:"Virtual scrolling cache window size in records"},"data-ln-data-store-window-page":{prop:"_windowPageSize",type:"integer",read:Y,fallback:200,min:5,effect:$t,description:"Virtual scrolling slice page size"},"data-ln-data-store-frozen":{type:"marker",description:"Applied at runtime to indicate store schema is locked in IndexedDB"}},a=je(i),s="ln_app_cache",c="_meta",f="1.0";let h=null,p=null;const b={};function _(t){t&&t.name==="QuotaExceededError"&&v(document,"ln-data-store:quota-exceeded",{error:t})}function k(){const t={};for(const r of document.querySelectorAll(`[${e}]`)){const l=r.id;if(l){const u=r.getAttribute("data-ln-data-store-indexes")||"";t[l]={indexes:u.split(",").map(d=>d.trim()).filter(Boolean)}}}return t}function R(){return p||(p=new Promise(t=>{if(typeof indexedDB>"u")return console.warn("[ln-data-store] IndexedDB not available — falling back to in-memory store"),t(null);const r=k(),l=Object.keys(r),u=indexedDB.open(s);u.onerror=()=>{console.warn("[ln-data-store] IndexedDB open failed — falling back to in-memory store"),t(null)},u.onsuccess=d=>{const w=d.target.result,y=Array.from(w.objectStoreNames);if(!(!y.includes(c)||l.some(T=>!y.includes(T))))return P(w),h=w,t(w);const C=w.version;w.close();const x=indexedDB.open(s,C+1);x.onblocked=()=>{console.warn("[ln-data-store] Database upgrade blocked — waiting for other tabs to close connection")},x.onerror=()=>{console.warn("[ln-data-store] Database upgrade failed"),t(null)},x.onupgradeneeded=T=>{const N=T.target.result;N.objectStoreNames.contains(c)||N.createObjectStore(c,{keyPath:"key"});for(const M of l)if(!N.objectStoreNames.contains(M)){const V=N.createObjectStore(M,{keyPath:"id"});for(const oe of r[M].indexes)V.createIndex(oe,oe,{unique:!1})}},x.onsuccess=T=>{const N=T.target.result;P(N),h=N,t(N)}}}),p)}function P(t){t.onversionchange=()=>{t.close(),h=null,p=null}}function g(){return h?Promise.resolve(h):(p=null,R())}async function S(t){if(!z()||!t)return t;const r={...t},l=r.id,u=await ut(r);return!u||!u.encrypted?t:{...u,id:l}}async function L(t){return!t||!t.encrypted||!z()?t:dt(t,{silent:!0})}const q=(t,r)=>g().then(l=>l?l.transaction(t,r).objectStore(t):null);function A(t){return new Promise((r,l)=>{t.onsuccess=()=>r(t.result),t.onerror=()=>{_(t.error),l(t.error)}})}const E=t=>q(t,"readonly").then(r=>r?A(r.getAll()):[]).then(r=>z()?Promise.all(r.map(l=>L(l))):r),I=(t,r)=>q(t,"readonly").then(l=>l?A(l.get(r)).then(u=>u!==void 0?u:typeof r=="string"&&r.trim()!==""&&!isNaN(Number(r))?A(l.get(Number(r))):typeof r=="number"?A(l.get(String(r))):null):null).then(l=>l?L(l):null),B=(t,r)=>g().then(l=>{if(!l)return[];const d=l.transaction(t,"readonly").objectStore(t),w=r.map(y=>A(d.get(y)).then(m=>m!==void 0?m:typeof y=="string"&&y.trim()!==""&&!isNaN(Number(y))?A(d.get(Number(y))):typeof y=="number"?A(d.get(String(y))):null));return Promise.all(w).then(y=>z()?Promise.all(y.map(m=>m?L(m):null)):y)}),H=(t,r)=>(z()?S(r):Promise.resolve(r)).then(u=>q(t,"readwrite").then(d=>d?A(d.put(u)):null)),$=(t,r)=>q(t,"readwrite").then(l=>l?A(l.delete(r)).then(()=>{if(typeof r=="string"&&r.trim()!==""&&!isNaN(Number(r)))return A(l.delete(Number(r)));if(typeof r=="number")return A(l.delete(String(r)))}):null),F=t=>q(t,"readwrite").then(r=>r?A(r.clear()):null),O=t=>q(t,"readonly").then(r=>r?A(r.count()):0),St=t=>q(c,"readonly").then(r=>r?A(r.get(t)):null),W=(t,r)=>q(c,"readwrite").then(l=>{if(l)return r.key=t,A(l.put(r))});function D(t){return this.dom=t,this._name=t.id,this._name||console.warn("[ln-data-store] missing id — the store cannot be addressed",t),Fe(this,t,a),this._handlers=null,this.isLoaded=!1,this.canServe=!1,this.isInitialized=!1,this.initializationError=null,this.hasCache=!1,this.isSyncing=!1,this.lastSyncedAt=null,this.query={filters:{},search:"",sort:null},t.hasAttribute("data-ln-data-store-window")?this._windowIndex=pt({windowSize:this._windowSize,pageSize:this._windowPageSize,requestPage:(r,l,u)=>{v(this.dom,"ln-data-store:request-page",{store:this._name,offset:r,limit:l,query:u,queryGen:this._windowIndex.queryGen})}}):this._windowIndex=null,this.windowed=this._windowIndex!==null,this.totalCount=0,this.presenters=null,this._mutationChain=Promise.resolve(),b[this._name]=this,At(this),this.ready=qt(this),this}function At(t){t._handlers={create:r=>U(t,"create",r.detail,()=>vt(t,r.detail)),update:r=>U(t,"update",r.detail,()=>xt(t,r.detail)),delete:r=>U(t,"delete",r.detail,()=>kt(t,r.detail)),"bulk-delete":r=>U(t,"bulk-delete",r.detail,()=>Et(t,r.detail)),"sync-failed":r=>{t.isSyncing=!1,v(t.dom,"ln-data-store:sync-error",{store:t._name,error:r.detail&&r.detail.error,status:r.detail&&r.detail.status})}};for(const[r,l]of Object.entries(t._handlers))t.dom.addEventListener(`ln-data-store:request-${r}`,l);t._queryHandlers={"ln-search:change":r=>{r.preventDefault();const l=r.detail&&r.detail.term!=null?r.detail.term:"";l!==t.query.search&&(t.query.search=l,re(t))},"ln-filter:change":r=>{r.preventDefault();const l=r.detail&&r.detail.key;if(!l)return;const u=(r.detail.values||[]).slice(),d=t.query.filters[l];(d?d.length===u.length&&d.every((y,m)=>y===u[m]):!u.length)||(u.length?t.query.filters[l]=u:delete t.query.filters[l],re(t))},"ln-sort:change":r=>{r.preventDefault();const l=r.detail&&r.detail.field,u=r.detail&&r.detail.direction,d=u&&u!=="none"?{field:l,direction:u}:null,w=t.query.sort;!w&&!d||w&&d&&w.field===d.field&&w.direction===d.direction||(t.query.sort=d,re(t))}};for(const[r,l]of Object.entries(t._queryHandlers))t.dom.addEventListener(r,l)}function U(t,r,l,u){const d=l&&l.requestId;return t._mutationChain=t._mutationChain.then(()=>t.ready).then(()=>{if(t.initializationError)throw t.initializationError;return u()}).catch(w=>It(t,r,d,w)),t._mutationChain}function Q(t,r=0){return O(t._name).then(l=>{if(t._windowIndex||t.windowed){const u=t.totalCount!=null?t.totalCount:l;t.totalCount=Math.max(0,u+r)}else t.totalCount=l;return t.hasCache=!0,t.isLoaded=!0,t.canServe=!0,W(t._name,{schema_version:f,last_synced_at:t.lastSyncedAt,has_cache:!0,record_count:t.totalCount})})}function vt(t,{tempId:r,data:l={},requestId:u}={}){const d={...l,id:r};return H(t._name,d).then(()=>Q(t,1)).then(()=>{v(t.dom,"ln-data-store:created",{store:t._name,record:d,tempId:r,requestId:u})})}function xt(t,{id:r,data:l={},requestId:u}={}){return I(t._name,r).then(d=>{if(!d)throw new Error(`Record not found: ${r}`);const w=d.id,y={...d,...l,id:w},m=l.id,C=m!==void 0&&m!==w;return(C?Lt(t._name,w,{...y,id:m}):H(t._name,y)).then(()=>Q(t,0)).then(()=>{v(t.dom,"ln-data-store:updated",{store:t._name,record:C?{...y,id:m}:y,previous:d,requestId:u})})})}function kt(t,{id:r,requestId:l}={}){return I(t._name,r).then(u=>{if(!u){v(t.dom,"ln-data-store:deleted",{store:t._name,id:r,requestId:l,missing:!0});return}const d=u.id;return $(t._name,d).then(()=>Q(t,-1)).then(()=>{v(t.dom,"ln-data-store:deleted",{store:t._name,id:d,requestId:l})})})}function Et(t,{ids:r=[],requestId:l}={}){return r.length?Promise.all(r.map(u=>I(t._name,u))).then(u=>{const d=u.filter(Boolean).map(w=>w.id);return J(t._name,d).then(()=>Q(t,-d.length)).then(()=>{v(t.dom,"ln-data-store:deleted",{store:t._name,ids:d,requestId:l})})}):(v(t.dom,"ln-data-store:deleted",{store:t._name,ids:[],requestId:l}),Promise.resolve())}function It(t,r,l,u){console.error("[ln-data-store] "+r+" failed:",u),v(t.dom,"ln-data-store:mutation-error",{store:t._name,action:r,requestId:l,error:u})}function qt(t){return R().then(r=>{if(!r)throw new Error("IndexedDB is unavailable");return St(t._name)}).then(r=>{if(t.initializationError=null,r&&r.schema_version===f)t.lastSyncedAt=r.last_synced_at||null,t.totalCount=r.record_count||0,t.hasCache=r.has_cache===!0||t.totalCount>0,t.hasCache&&(t.isLoaded=!0,t.canServe=!0,v(t.dom,"ln-data-store:ready",{store:t._name,count:t.totalCount,source:"cache"})),t.isInitialized=!0,v(t.dom,"ln-data-store:initialized",{store:t._name,hasCache:t.hasCache,lastSyncedAt:t.lastSyncedAt,count:t.totalCount});else{if(r&&r.schema_version!==f)return F(t._name).then(()=>W(t._name,{schema_version:f,last_synced_at:null,has_cache:!1,record_count:0})).then(()=>{t.isInitialized=!0,t.hasCache=!1,v(t.dom,"ln-data-store:initialized",{store:t._name,hasCache:!1,lastSyncedAt:null,count:0})});t.isInitialized=!0,t.hasCache=!1,v(t.dom,"ln-data-store:initialized",{store:t._name,hasCache:!1,lastSyncedAt:null,count:0})}}).catch(r=>(t.isInitialized=!0,t.isLoaded=!1,t.canServe=!1,t.hasCache=!1,t.isSyncing=!1,t.initializationError=r,v(t.dom,"ln-data-store:initialization-error",{store:t._name,error:r}),{ok:!1,error:r}))}function ve(t){t.isSyncing=!0,v(t.dom,"ln-data-store:request-remote-sync",{since:t.lastSyncedAt})}function xe(t,r){return g().then(l=>l?(z()?Promise.all(r.map(d=>S(d))):Promise.resolve(r)).then(d=>new Promise((w,y)=>{const m=l.transaction(t,"readwrite"),C=m.objectStore(t);d.forEach(x=>C.put(x)),m.oncomplete=()=>w(),m.onerror=()=>{_(m.error),y(m.error)}})):void 0)}function J(t,r){return g().then(l=>{if(l)return new Promise((u,d)=>{const w=l.transaction(t,"readwrite"),y=w.objectStore(t);r.forEach(m=>{y.delete(m),typeof m=="string"&&m.trim()!==""&&!isNaN(Number(m))?y.delete(Number(m)):typeof m=="number"&&y.delete(String(m))}),w.oncomplete=()=>u(),w.onerror=()=>d(w.error)})})}function Lt(t,r,l){return(z()?S(l):Promise.resolve(l)).then(d=>g().then(w=>{if(w)return new Promise((y,m)=>{const C=w.transaction(t,"readwrite"),x=C.objectStore(t);x.put(d),x.delete(r),C.oncomplete=()=>y(),C.onerror=()=>{_(C.error),m(C.error)}})}))}const Nt=new Intl.Collator(void 0,{numeric:!0,sensitivity:"base"});function Dt(t){return t?Object.keys(t).filter(r=>Array.isArray(t[r])&&t[r].length>0):[]}function Tt(t,r,l){return r.every(u=>l[u].map(String).includes(String(t[u])))}function zt(t){return String(t||"").toLowerCase().split(/\s+/).filter(Boolean)}function Ot(t,r,l){return r.every(u=>l.some(d=>{const w=t[d];return w!=null&&String(w).toLowerCase().includes(u)}))}function Rt(t,r,l){return bt(t,r,l)}function K(t,r){return Ct(r,t.presenters&&t.presenters.computed)}function Pt(t){return!t.sort&&!z()}function Bt(t,r,l){const u=Dt(r.filters),d=r.search?zt(r.search):[],w=t._searchFields,y=d.length>0&&w&&w.length>0;return q(t._name,"readonly").then(m=>m?new Promise((C,x)=>{const T=[],N=m.openCursor();N.onsuccess=()=>{const M=N.result;if(!M||T.length>=l){C(T);return}const V=M.value;(!u.length||Tt(V,u,r.filters))&&(!y||Ot(V,d,w))&&T.push(V),M.continue()},N.onerror=()=>x(N.error)}):[])}function ke(t,r,l){return _t(r,l,t._searchFields,Nt)}function Ee(t,r,l){const u=[];for(let w=r;w<r+l;w++){const y=t._windowIndex.getId(w);u.push(y)}const d=Array.from(new Set(u.filter(w=>w!==void 0)));return B(t._name,d).then(w=>{const y=new Map;for(let C=0;C<w.length;C++){const x=w[C];x&&y.set(String(x.id),x)}const m=[];for(let C=0;C<u.length;C++){const x=u[C];if(x===void 0)m.push(null);else{const T=y.get(String(x));m.push(T||null)}}return{data:K(t,m),total:t._windowIndex.grandTotal,filtered:t._windowIndex.logicalTotal,offset:r,queryGen:t._windowIndex.queryGen}})}D.prototype.getAll=function(t={}){const r=this;if(r._windowIndex){const l=t.offset||0,u=t.limit||200;if(r._windowIndex.ensure(l,l+u,t),!r._windowIndex.hasLoaded&&!r.noLocalQuery){const d=l+u,w=y=>y.length?{data:K(r,y),offset:l,queryGen:r._windowIndex.queryGen,provisional:!0}:Ee(r,l,u);return Pt(t)?Bt(r,t,d).then(y=>w(y.slice(l,d))):E(r._name).then(y=>w(ke(r,y,t).records))}return Ee(r,l,u)}return E(r._name).then(l=>{const u=ke(r,l,t);return{data:K(r,u.records),total:u.total,filtered:u.filtered}})},D.prototype.getById=function(t){return I(this._name,t).then(r=>r?K(this,[r])[0]:null)},D.prototype.count=function(t){return t&&Object.keys(t).length>0?E(this._name).then(l=>Ae(l,t).length):this.totalCount!=null?Promise.resolve(this.totalCount):O(this._name)},D.prototype.aggregate=function(t,r){return E(this._name).then(l=>Rt(l,t,r))},D.prototype.setPresenters=function(t){this.presenters=t},D.prototype.applySync=function(t,r,l,u){u=u||{};const d=this;if(d._windowIndex&&u.queryGen!=null&&u.queryGen!==d._windowIndex.queryGen)return Promise.resolve();t.length>0||r.length>0;let w=Promise.resolve();return t.length>0&&(w=w.then(()=>xe(d._name,t))),r.length>0&&(w=w.then(()=>J(d._name,r))),w.then(()=>{if(d._windowIndex&&(u.offset!=null||u.total!=null)){const y=u.offset!=null?u.offset:0,m=t.map(x=>x.id),C=d._windowIndex.ingest(y,m,u.total,u.filtered,u.queryGen);if(C&&C.length)return J(d._name,C)}}).then(()=>O(d._name)).then(y=>(d.totalCount=u.total!==void 0?u.total:y,d.hasCache=!0,W(d._name,{schema_version:f,last_synced_at:l,has_cache:!0,record_count:d.totalCount}))).then(()=>{const y=!d.isLoaded;d.isLoaded=!0,d.canServe=!0,d.isSyncing=!1,d.lastSyncedAt=l,y?(v(d.dom,"ln-data-store:loaded",{store:d._name,count:d.totalCount,meta:u}),v(d.dom,"ln-data-store:ready",{store:d._name,count:d.totalCount,source:"server",meta:u})):v(d.dom,"ln-data-store:synced",{store:d._name,added:t.length,deleted:r.length,changed:!0,meta:u})}).catch(y=>{d.isSyncing=!1,console.error("[ln-data-store] applySync failed:",y)})},D.prototype.applyQuery=function(t,r){r=r||{};const l=this;let u=Promise.resolve();return t.length>0&&(u=u.then(()=>xe(l._name,t))),u.then(()=>O(l._name)).then(d=>(l.totalCount=r.total!==void 0?r.total:d,t.length>0&&(l.canServe=!0),K(l,t))).catch(d=>(console.error("[ln-data-store] applyQuery failed:",d),[]))},D.prototype.forceSync=function(){this.isSyncing||ve(this)},D.prototype.fullReload=function(){const t=this;return F(t._name).then(()=>W(t._name,{schema_version:f,last_synced_at:null,has_cache:!1,record_count:0})).then(()=>{t.isLoaded=!1,t.hasCache=!1,t.lastSyncedAt=null,t.totalCount=0,ve(t)})},D.prototype.destroy=function(){if(this._windowIndex&&(this._windowIndex.clear(),this._windowIndex=null,this.windowed=!1),this._handlers){for(const[t,r]of Object.entries(this._handlers))this.dom.removeEventListener(`ln-data-store:request-${t}`,r);this._handlers=null}if(this._queryHandlers){for(const[t,r]of Object.entries(this._queryHandlers))this.dom.removeEventListener(t,r);this._queryHandlers=null}delete b[this._name],delete this.dom[n]};function Mt(){return g().then(t=>{if(!t)return;const r=Array.from(t.objectStoreNames);return new Promise((l,u)=>{const d=t.transaction(r,"readwrite");r.forEach(w=>d.objectStore(w).clear()),d.oncomplete=()=>l(),d.onerror=()=>u(d.error)})}).then(()=>{Object.values(b).forEach(t=>{t.isLoaded=!1,t.canServe=!1,t.isInitialized=!1,t.initializationError=null,t.hasCache=!1,t.isSyncing=!1,t.lastSyncedAt=null,t.totalCount=0})})}function re(t){t._windowIndex&&t._windowIndex.reset(),v(t.dom,"ln-data-store:query-changed",{store:t._name,query:{filters:Object.assign({},t.query.filters),search:t.query.search,sort:t.query.sort?Object.assign({},t.query.sort):null}})}const Ft="data-ln-data-store-frozen";function Ie(t,r){t.setAttribute(Ft,r)}function jt(t){const r=t[n];if(!r._windowIndex)return;const l=r._windowIndex.configure({windowSize:r._windowSize});l.length&&J(r._name,l).catch(u=>{console.error("[ln-data-store] window shrink eviction failed:",u)})}function $t(t){const r=t[n];r._windowIndex&&r._windowIndex.configure({pageSize:r._windowPageSize})}Ze(e,n,D,"ln-data-store",{attributes:i}),window[n].clearAll=Mt,window[n].init=window[n],window[n].setStorageKey=Se,typeof window<"u"&&(window.lnCore=window.lnCore||{},window.lnCore.setStorageKey=Se)})()})();
@@ -0,0 +1,84 @@
1
+ {
2
+ "$comment": "This file is partly generated. The $comment, component, generator, and each attribute's direction, sources, type, values, fallback, and description keys are owned by the generator and synchronized from JS source code. All other keys are preserved and safe to hand-author. Regenerate with `npm run sync:ln-schemas`.",
3
+ "component": "ln-data-store",
4
+ "generator": "scripts/sync-ln-schemas.mjs",
5
+ "attributes": {
6
+ "data-ln-data-store": {
7
+ "direction": "author",
8
+ "sources": [
9
+ "ln-data-store-dev.scss",
10
+ "src/ln-data-store.js"
11
+ ],
12
+ "type": "marker",
13
+ "description": "Identifies the element as a data-store definition container"
14
+ },
15
+ "data-ln-data-store-frozen": {
16
+ "direction": "runtime",
17
+ "sources": [
18
+ "ln-data-store-dev.scss",
19
+ "src/ln-data-store.js"
20
+ ],
21
+ "type": "marker",
22
+ "description": "Applied at runtime to indicate store schema is locked in IndexedDB"
23
+ },
24
+ "data-ln-data-store-indexes": {
25
+ "direction": "author",
26
+ "sources": [
27
+ "src/ln-data-store.js"
28
+ ],
29
+ "type": "list",
30
+ "description": "Comma-separated index field names for the IndexedDB store"
31
+ },
32
+ "data-ln-data-store-no-local-query": {
33
+ "direction": null,
34
+ "sources": [
35
+ "src/ln-data-store.js"
36
+ ],
37
+ "type": "boolean",
38
+ "description": "Bypasses local IndexedDB query resolution, forcing remote fetching"
39
+ },
40
+ "data-ln-data-store-search-fields": {
41
+ "direction": null,
42
+ "sources": [
43
+ "src/ln-data-store.js"
44
+ ],
45
+ "type": "list",
46
+ "description": "Record fields to index for client-side search"
47
+ },
48
+ "data-ln-data-store-stale": {
49
+ "direction": null,
50
+ "sources": [
51
+ "src/ln-data-store.js"
52
+ ],
53
+ "type": "integer",
54
+ "fallback": 300,
55
+ "description": "Cache staleness threshold in seconds, or -1/never"
56
+ },
57
+ "data-ln-data-store-window": {
58
+ "direction": "author",
59
+ "sources": [
60
+ "src/ln-data-store.js"
61
+ ],
62
+ "type": "integer",
63
+ "fallback": 1000,
64
+ "min": 10,
65
+ "description": "Virtual scrolling cache window size in records"
66
+ },
67
+ "data-ln-data-store-window-page": {
68
+ "direction": null,
69
+ "sources": [
70
+ "src/ln-data-store.js"
71
+ ],
72
+ "type": "integer",
73
+ "fallback": 200,
74
+ "min": 5,
75
+ "description": "Virtual scrolling slice page size"
76
+ },
77
+ "data-ln-debug": {
78
+ "direction": null,
79
+ "sources": [
80
+ "ln-data-store-dev.scss"
81
+ ]
82
+ }
83
+ }
84
+ }
@@ -0,0 +1,155 @@
1
+ import { compareValues, detectValueType } from '../../ln-core/compare.js';
2
+ import { matchesFilterValues, matchesSearchTokens, tokenizeSearchQuery } from '../../ln-core/matching.js';
3
+
4
+ /**
5
+ * Sorts an array of record objects by field and direction using pure core comparison.
6
+ * @param {Array<Record<string, any>>} records
7
+ * @param {{ field?: string, direction?: string } | null} [sort]
8
+ * @param {Intl.Collator} [collator]
9
+ * @returns {Array<Record<string, any>>}
10
+ */
11
+ export function sortRecords(records, sort, collator) {
12
+ if (!Array.isArray(records) || !sort || !sort.field) return records;
13
+ const { field, direction } = sort;
14
+ const desc = direction === 'desc';
15
+
16
+ // Lock the value type once for the entire sort operation (per Doctrinal / compare.js rules)
17
+ const sampleValues = records.map(r => (r ? r[field] : undefined));
18
+ const valueType = detectValueType(sampleValues);
19
+
20
+ return [...records].sort((a, b) => {
21
+ const va = a ? a[field] : undefined;
22
+ const vb = b ? b[field] : undefined;
23
+
24
+ const result = compareValues(va, vb, valueType, collator);
25
+ return desc ? -result : result;
26
+ });
27
+ }
28
+
29
+ /**
30
+ * Filters an array of records against a dictionary of active filter arrays.
31
+ * Reuses matchesFilterValues from ln-core/matching (AND across fields, OR within field).
32
+ * @param {Array<Record<string, any>>} records
33
+ * @param {Record<string, Array<any>>} filters
34
+ * @returns {Array<Record<string, any>>}
35
+ */
36
+ export function filterRecords(records, filters) {
37
+ if (!Array.isArray(records) || !filters || typeof filters !== 'object') return records;
38
+
39
+ const keys = Object.keys(filters).filter(k => Array.isArray(filters[k]) && filters[k].length > 0);
40
+ if (!keys.length) return records;
41
+
42
+ return records.filter(record => {
43
+ if (!record) return false;
44
+ return keys.every(field => matchesFilterValues(record[field], filters[field]));
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Searches records across specified fields using ln-core/matching tokenization and matching.
50
+ * @param {Array<Record<string, any>>} records
51
+ * @param {string} query
52
+ * @param {string[]} searchFields
53
+ * @returns {Array<Record<string, any>>}
54
+ */
55
+ export function searchRecords(records, query, searchFields) {
56
+ if (!Array.isArray(records) || !query || !searchFields || !searchFields.length) return records;
57
+
58
+ const tokens = tokenizeSearchQuery(query);
59
+ if (!tokens.length) return records;
60
+
61
+ return records.filter(record => {
62
+ if (!record) return false;
63
+ return tokens.every(token =>
64
+ searchFields.some(field => {
65
+ const val = record[field];
66
+ return val != null && matchesSearchTokens(String(val), [token]);
67
+ })
68
+ );
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Computes aggregates (count, sum, avg) on a specific field across records.
74
+ * @param {Array<Record<string, any>>} records
75
+ * @param {string} field
76
+ * @param {'count' | 'sum' | 'avg'} fn
77
+ * @returns {number}
78
+ */
79
+ export function aggregateRecords(records, field, fn) {
80
+ if (!Array.isArray(records) || !records.length) return 0;
81
+ if (fn === 'count') return records.length;
82
+
83
+ const numbers = records
84
+ .map(r => (r && r[field] != null ? parseFloat(r[field]) : NaN))
85
+ .filter(v => Number.isFinite(v));
86
+
87
+ const sum = numbers.reduce((a, b) => a + b, 0);
88
+
89
+ if (fn === 'sum') return sum;
90
+ if (fn === 'avg') return numbers.length ? sum / numbers.length : 0;
91
+ return 0;
92
+ }
93
+
94
+ /**
95
+ * Executes full in-memory query evaluation: filter -> search -> sort -> slice.
96
+ * @param {Array<Record<string, any>>} records
97
+ * @param {{ filters?: Record<string, any[]>, search?: string, sort?: { field?: string, direction?: string }, offset?: number, limit?: number }} [options={}]
98
+ * @param {string[]} [searchFields=[]]
99
+ * @param {Intl.Collator} [collator]
100
+ * @returns {{ records: Array<Record<string, any>>, total: number, filtered: number }}
101
+ */
102
+ export function queryRecords(records, options = {}, searchFields = [], collator) {
103
+ if (!Array.isArray(records)) {
104
+ return { records: [], total: 0, filtered: 0 };
105
+ }
106
+
107
+ const total = records.length;
108
+ let result = records;
109
+
110
+ if (options.filters) {
111
+ result = filterRecords(result, options.filters);
112
+ }
113
+ if (options.search) {
114
+ result = searchRecords(result, options.search, searchFields);
115
+ }
116
+
117
+ const filtered = result.length;
118
+
119
+ if (options.sort) {
120
+ result = sortRecords(result, options.sort, collator);
121
+ }
122
+
123
+ if (options.offset || options.limit) {
124
+ const offset = options.offset || 0;
125
+ const limit = options.limit || result.length;
126
+ result = result.slice(offset, offset + limit);
127
+ }
128
+
129
+ return { records: result, total, filtered };
130
+ }
131
+
132
+ /**
133
+ * Decorates records with computed fields (pure, no console side-effects).
134
+ * @param {Array<Record<string, any>>} records
135
+ * @param {Record<string, (record: any) => any>} [computed]
136
+ * @returns {Array<Record<string, any>>}
137
+ */
138
+ export function decorateRecords(records, computed) {
139
+ if (!Array.isArray(records) || !computed || typeof computed !== 'object') return records;
140
+
141
+ return records.map(record => {
142
+ if (!record) return null;
143
+ const copy = { ...record };
144
+ for (const [fieldName, fn] of Object.entries(computed)) {
145
+ if (typeof fn === 'function') {
146
+ try {
147
+ copy[fieldName] = fn(record);
148
+ } catch (_) {
149
+ copy[fieldName] = undefined;
150
+ }
151
+ }
152
+ }
153
+ return copy;
154
+ });
155
+ }