@eifi1/ui-kit 0.53.0 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (902) hide show
  1. package/README.md +96 -82
  2. package/dist/account/delete-account-setting.d.ts +3 -2
  3. package/dist/account/delete-account-setting.js.map +1 -1
  4. package/dist/admin/plan-change.js +2 -2
  5. package/dist/admin/plan-change.js.map +1 -1
  6. package/dist/admin/user-roster.d.ts +6 -0
  7. package/dist/admin/user-roster.js +4 -0
  8. package/dist/admin/user-roster.js.map +1 -1
  9. package/dist/auth/auth-errors.d.ts +36 -0
  10. package/dist/auth/auth-errors.js +20 -0
  11. package/dist/auth/auth-errors.js.map +1 -1
  12. package/dist/billing/billing-labels.d.ts +26 -0
  13. package/dist/billing/billing-labels.js +9 -0
  14. package/dist/billing/billing-labels.js.map +1 -1
  15. package/dist/billing/subscription-standing.d.ts +61 -0
  16. package/dist/billing/subscription-standing.js +51 -0
  17. package/dist/billing/subscription-standing.js.map +1 -0
  18. package/dist/billing/usage-meter.d.ts +52 -0
  19. package/dist/billing/usage-meter.js +52 -0
  20. package/dist/billing/usage-meter.js.map +1 -0
  21. package/dist/chart.d.ts +1 -0
  22. package/dist/chart.js +4 -0
  23. package/dist/chart.js.map +1 -1
  24. package/dist/components/alert-banner.js +2 -3
  25. package/dist/components/alert-banner.js.map +1 -1
  26. package/dist/components/amount-input.d.ts +2 -1
  27. package/dist/components/amount-input.js +3 -15
  28. package/dist/components/amount-input.js.map +1 -1
  29. package/dist/components/autocomplete.js +2 -2
  30. package/dist/components/autocomplete.js.map +1 -1
  31. package/dist/components/bulk-action-bar.js +2 -1
  32. package/dist/components/bulk-action-bar.js.map +1 -1
  33. package/dist/components/button.d.ts +35 -1
  34. package/dist/components/button.js +5 -9
  35. package/dist/components/button.js.map +1 -1
  36. package/dist/components/calendar-heatmap.js +6 -4
  37. package/dist/components/calendar-heatmap.js.map +1 -1
  38. package/dist/components/camera-button.d.ts +89 -0
  39. package/dist/components/camera-button.js +122 -0
  40. package/dist/components/camera-button.js.map +1 -0
  41. package/dist/components/camera-capture.d.ts +66 -0
  42. package/dist/components/camera-capture.js +190 -0
  43. package/dist/components/camera-capture.js.map +1 -0
  44. package/dist/components/camera-labels.d.ts +20 -0
  45. package/dist/components/camera-labels.js +15 -0
  46. package/dist/components/camera-labels.js.map +1 -0
  47. package/dist/components/chart-edge.d.ts +9 -0
  48. package/dist/components/chart-edge.js +14 -3
  49. package/dist/components/chart-edge.js.map +1 -1
  50. package/dist/components/chart-zoom-core.js +34 -29
  51. package/dist/components/chart-zoom-core.js.map +1 -1
  52. package/dist/components/checkbox-group.js +1 -1
  53. package/dist/components/checkbox-group.js.map +1 -1
  54. package/dist/components/combobox-core.js +3 -2
  55. package/dist/components/combobox-core.js.map +1 -1
  56. package/dist/components/combobox.js +2 -2
  57. package/dist/components/combobox.js.map +1 -1
  58. package/dist/components/consent-again-notice-labels.d.ts +25 -0
  59. package/dist/components/consent-again-notice-labels.js +15 -0
  60. package/dist/components/consent-again-notice-labels.js.map +1 -0
  61. package/dist/components/consent-again-notice.d.ts +85 -0
  62. package/dist/components/consent-again-notice.js +94 -0
  63. package/dist/components/consent-again-notice.js.map +1 -0
  64. package/dist/components/country-select.js +2 -7
  65. package/dist/components/country-select.js.map +1 -1
  66. package/dist/components/currency-option-list.d.ts +2 -1
  67. package/dist/components/currency-option-list.js +10 -3
  68. package/dist/components/currency-option-list.js.map +1 -1
  69. package/dist/components/currency-select.d.ts +3 -1
  70. package/dist/components/currency-select.js +2 -2
  71. package/dist/components/currency-select.js.map +1 -1
  72. package/dist/components/danger-confirm.d.ts +22 -1
  73. package/dist/components/danger-confirm.js +52 -30
  74. package/dist/components/danger-confirm.js.map +1 -1
  75. package/dist/components/data-table-column-settings.js +2 -2
  76. package/dist/components/data-table-column-settings.js.map +1 -1
  77. package/dist/components/data-table-filter-popover.d.ts +2 -1
  78. package/dist/components/data-table-filter-popover.js +3 -2
  79. package/dist/components/data-table-filter-popover.js.map +1 -1
  80. package/dist/components/data-table-filters.d.ts +33 -3
  81. package/dist/components/data-table-filters.js +6 -4
  82. package/dist/components/data-table-filters.js.map +1 -1
  83. package/dist/components/data-table-fit.js.map +1 -1
  84. package/dist/components/data-table-virtual.js +13 -3
  85. package/dist/components/data-table-virtual.js.map +1 -1
  86. package/dist/components/date-field.js +2 -2
  87. package/dist/components/date-field.js.map +1 -1
  88. package/dist/components/date-picker.js +2 -2
  89. package/dist/components/date-picker.js.map +1 -1
  90. package/dist/components/date-range-picker.js +2 -6
  91. package/dist/components/date-range-picker.js.map +1 -1
  92. package/dist/components/dialog-frame.d.ts +5 -1
  93. package/dist/components/dialog-frame.js +6 -2
  94. package/dist/components/dialog-frame.js.map +1 -1
  95. package/dist/components/dropdown.js +7 -3
  96. package/dist/components/dropdown.js.map +1 -1
  97. package/dist/components/entity-combobox.js +2 -2
  98. package/dist/components/entity-combobox.js.map +1 -1
  99. package/dist/components/field-parts.d.ts +108 -6
  100. package/dist/components/field-parts.js +59 -6
  101. package/dist/components/field-parts.js.map +1 -1
  102. package/dist/components/field-suggestion-labels.d.ts +2 -0
  103. package/dist/components/field-suggestion-labels.js +10 -0
  104. package/dist/components/field-suggestion-labels.js.map +1 -0
  105. package/dist/components/field.js +1 -1
  106. package/dist/components/field.js.map +1 -1
  107. package/dist/components/file-button.d.ts +1 -1
  108. package/dist/components/file-button.js.map +1 -1
  109. package/dist/components/floating-panel.d.ts +7 -6
  110. package/dist/components/floating-panel.js +3 -17
  111. package/dist/components/floating-panel.js.map +1 -1
  112. package/dist/components/form-actions.d.ts +3 -2
  113. package/dist/components/form-actions.js.map +1 -1
  114. package/dist/components/hover-menu.js +3 -2
  115. package/dist/components/hover-menu.js.map +1 -1
  116. package/dist/components/icon-button.d.ts +23 -5
  117. package/dist/components/icon-button.js +6 -8
  118. package/dist/components/icon-button.js.map +1 -1
  119. package/dist/components/inline-confirm.js +2 -1
  120. package/dist/components/inline-confirm.js.map +1 -1
  121. package/dist/components/inline-entity-combobox.js +2 -2
  122. package/dist/components/inline-entity-combobox.js.map +1 -1
  123. package/dist/components/input.js +2 -2
  124. package/dist/components/input.js.map +1 -1
  125. package/dist/components/jump-calendar.js +3 -3
  126. package/dist/components/jump-calendar.js.map +1 -1
  127. package/dist/components/legal.d.ts +13 -1
  128. package/dist/components/legal.js +12 -0
  129. package/dist/components/legal.js.map +1 -1
  130. package/dist/components/legended-series-chart.d.ts +80 -0
  131. package/dist/components/legended-series-chart.js +70 -0
  132. package/dist/components/legended-series-chart.js.map +1 -0
  133. package/dist/components/lightbox.js +2 -1
  134. package/dist/components/lightbox.js.map +1 -1
  135. package/dist/components/mini-calendar.js +16 -7
  136. package/dist/components/mini-calendar.js.map +1 -1
  137. package/dist/components/modal.d.ts +48 -0
  138. package/dist/components/modal.js +26 -6
  139. package/dist/components/modal.js.map +1 -1
  140. package/dist/components/month-picker.d.ts +1 -1
  141. package/dist/components/month-picker.js +6 -6
  142. package/dist/components/month-picker.js.map +1 -1
  143. package/dist/components/multi-entity-combobox.js +2 -2
  144. package/dist/components/multi-entity-combobox.js.map +1 -1
  145. package/dist/components/multi-select.js +2 -6
  146. package/dist/components/multi-select.js.map +1 -1
  147. package/dist/components/number-input.js +2 -2
  148. package/dist/components/number-input.js.map +1 -1
  149. package/dist/components/page-header-layout.d.ts +18 -0
  150. package/dist/components/page-header-layout.js +23 -0
  151. package/dist/components/page-header-layout.js.map +1 -0
  152. package/dist/components/page-header-skeleton.d.ts +41 -0
  153. package/dist/components/page-header-skeleton.js +52 -0
  154. package/dist/components/page-header-skeleton.js.map +1 -0
  155. package/dist/components/page-header.js +4 -17
  156. package/dist/components/page-header.js.map +1 -1
  157. package/dist/components/popover.js +7 -2
  158. package/dist/components/popover.js.map +1 -1
  159. package/dist/components/release-notes-labels.d.ts +16 -0
  160. package/dist/components/release-notes-labels.js +18 -0
  161. package/dist/components/release-notes-labels.js.map +1 -0
  162. package/dist/components/release-notes.d.ts +65 -0
  163. package/dist/components/release-notes.js +65 -0
  164. package/dist/components/release-notes.js.map +1 -0
  165. package/dist/components/select.js +2 -2
  166. package/dist/components/select.js.map +1 -1
  167. package/dist/components/series-chart-labels.d.ts +9 -0
  168. package/dist/components/series-chart-labels.js +3 -1
  169. package/dist/components/series-chart-labels.js.map +1 -1
  170. package/dist/components/series-chart.d.ts +5 -0
  171. package/dist/components/series-chart.js +89 -36
  172. package/dist/components/series-chart.js.map +1 -1
  173. package/dist/components/stat-tile.d.ts +21 -2
  174. package/dist/components/stat-tile.js +4 -2
  175. package/dist/components/stat-tile.js.map +1 -1
  176. package/dist/components/toggle-group.js +3 -2
  177. package/dist/components/toggle-group.js.map +1 -1
  178. package/dist/components/treemap-layout.js.map +1 -1
  179. package/dist/components/treemap.d.ts +17 -15
  180. package/dist/components/treemap.js +167 -85
  181. package/dist/components/treemap.js.map +1 -1
  182. package/dist/currencies.d.ts +16 -2
  183. package/dist/currencies.js +2 -1
  184. package/dist/currencies.js.map +1 -1
  185. package/dist/eslint/index.js +1 -1
  186. package/dist/eslint/role-utilities.d.ts +2 -2
  187. package/dist/eslint/role-utilities.js.map +1 -1
  188. package/dist/feedback/feedback-context-labels.js +2 -1
  189. package/dist/feedback/feedback-context-labels.js.map +1 -1
  190. package/dist/feedback/feedback-context.d.ts +19 -3
  191. package/dist/feedback/feedback-context.js +12 -1
  192. package/dist/feedback/feedback-context.js.map +1 -1
  193. package/dist/feedback/feedback-diagnostics.d.ts +17 -0
  194. package/dist/feedback/feedback-diagnostics.js +27 -0
  195. package/dist/feedback/feedback-diagnostics.js.map +1 -0
  196. package/dist/feedback/feedback-dialog.d.ts +10 -0
  197. package/dist/feedback/feedback-dialog.js +6 -4
  198. package/dist/feedback/feedback-dialog.js.map +1 -1
  199. package/dist/feedback/feedback-inbox-page.d.ts +260 -0
  200. package/dist/feedback/feedback-inbox-page.js +207 -0
  201. package/dist/feedback/feedback-inbox-page.js.map +1 -0
  202. package/dist/feedback/feedback-record.d.ts +5 -0
  203. package/dist/feedback/feedback-record.js.map +1 -1
  204. package/dist/feedback/feedback-row-detail-labels.js +1 -0
  205. package/dist/feedback/feedback-row-detail-labels.js.map +1 -1
  206. package/dist/feedback/feedback-row-detail.d.ts +3 -0
  207. package/dist/feedback/feedback-row-detail.js +3 -0
  208. package/dist/feedback/feedback-row-detail.js.map +1 -1
  209. package/dist/feedback/feedback-submit-dialog.d.ts +5 -1
  210. package/dist/feedback/feedback-submit-dialog.js +15 -3
  211. package/dist/feedback/feedback-submit-dialog.js.map +1 -1
  212. package/dist/feedback/feedback-submit.d.ts +30 -2
  213. package/dist/feedback/feedback-submit.js +5 -2
  214. package/dist/feedback/feedback-submit.js.map +1 -1
  215. package/dist/feedback/feedback-vocabulary.js +2 -2
  216. package/dist/feedback/feedback-vocabulary.js.map +1 -1
  217. package/dist/feedback.d.ts +1 -0
  218. package/dist/feedback.js +6 -0
  219. package/dist/feedback.js.map +1 -1
  220. package/dist/hooks/use-debounce.d.ts +32 -5
  221. package/dist/hooks/use-debounce.js +6 -5
  222. package/dist/hooks/use-debounce.js.map +1 -1
  223. package/dist/hooks/use-file-picker.d.ts +3 -1
  224. package/dist/hooks/use-file-picker.js.map +1 -1
  225. package/dist/hooks/use-focus-trap.d.ts +19 -3
  226. package/dist/hooks/use-focus-trap.js +15 -5
  227. package/dist/hooks/use-focus-trap.js.map +1 -1
  228. package/dist/hooks/use-has-camera.d.ts +36 -0
  229. package/dist/hooks/use-has-camera.js +53 -0
  230. package/dist/hooks/use-has-camera.js.map +1 -0
  231. package/dist/hooks/use-hotkey.js +2 -1
  232. package/dist/hooks/use-hotkey.js.map +1 -1
  233. package/dist/hooks/use-overlay-history.d.ts +6 -1
  234. package/dist/hooks/use-overlay-history.js +10 -2
  235. package/dist/hooks/use-overlay-history.js.map +1 -1
  236. package/dist/i18n/defaults.js +11 -1
  237. package/dist/i18n/defaults.js.map +1 -1
  238. package/dist/i18n/german.js +78 -4
  239. package/dist/i18n/german.js.map +1 -1
  240. package/dist/i18n/kit-labels.d.ts +20 -0
  241. package/dist/i18n/kit-labels.js.map +1 -1
  242. package/dist/i18n/locales/en.js +95 -5
  243. package/dist/i18n/locales/en.js.map +3 -3
  244. package/dist/i18n/locales/es.js +74 -4
  245. package/dist/i18n/locales/es.js.map +1 -1
  246. package/dist/i18n/locales/fr.js +75 -3
  247. package/dist/i18n/locales/fr.js.map +1 -1
  248. package/dist/i18n/locales/hu.js +77 -4
  249. package/dist/i18n/locales/hu.js.map +1 -1
  250. package/dist/i18n/locales/it.js +77 -4
  251. package/dist/i18n/locales/it.js.map +1 -1
  252. package/dist/i18n/locales/zh.js +74 -4
  253. package/dist/i18n/locales/zh.js.map +1 -1
  254. package/dist/i18n/review.js +22 -1
  255. package/dist/i18n/review.js.map +1 -1
  256. package/dist/index.d.ts +27 -8
  257. package/dist/index.js +41 -2
  258. package/dist/index.js.map +1 -1
  259. package/dist/landing/landing-labels.d.ts +8 -0
  260. package/dist/landing/landing-labels.js +2 -1
  261. package/dist/landing/landing-labels.js.map +1 -1
  262. package/dist/lib/changelog.d.ts +130 -0
  263. package/dist/lib/changelog.js +137 -0
  264. package/dist/lib/changelog.js.map +1 -0
  265. package/dist/lib/clipboard-minus.js +2 -2
  266. package/dist/lib/clipboard-minus.js.map +1 -1
  267. package/dist/lib/dates.d.ts +36 -4
  268. package/dist/lib/dates.js +49 -13
  269. package/dist/lib/dates.js.map +1 -1
  270. package/dist/lib/editable-content.d.ts +33 -0
  271. package/dist/lib/editable-content.js +15 -0
  272. package/dist/lib/editable-content.js.map +1 -0
  273. package/dist/lib/focus-left.d.ts +47 -1
  274. package/dist/lib/focus-left.js +11 -2
  275. package/dist/lib/focus-left.js.map +1 -1
  276. package/dist/lib/format.d.ts +37 -2
  277. package/dist/lib/format.js +25 -1
  278. package/dist/lib/format.js.map +1 -1
  279. package/dist/lib/intl-cache.d.ts +4 -2
  280. package/dist/lib/intl-cache.js.map +1 -1
  281. package/dist/lib/server-wake.d.ts +49 -9
  282. package/dist/lib/server-wake.js +25 -14
  283. package/dist/lib/server-wake.js.map +1 -1
  284. package/dist/lib/session-refresh.d.ts +53 -0
  285. package/dist/lib/session-refresh.js +27 -1
  286. package/dist/lib/session-refresh.js.map +1 -1
  287. package/dist/lib/translation-review.d.ts +26 -0
  288. package/dist/lib/translation-review.js +14 -0
  289. package/dist/lib/translation-review.js.map +1 -1
  290. package/dist/rhf/fields.js +3 -3
  291. package/dist/rhf/fields.js.map +1 -1
  292. package/dist/shell/app-shell.d.ts +25 -1
  293. package/dist/shell/app-shell.js +3 -1
  294. package/dist/shell/app-shell.js.map +1 -1
  295. package/dist/shell/dev-menu.d.ts +3 -0
  296. package/dist/shell/dev-menu.js +4 -2
  297. package/dist/shell/dev-menu.js.map +1 -1
  298. package/dist/shell/held-changes-banner-labels.d.ts +23 -0
  299. package/dist/shell/held-changes-banner-labels.js +14 -0
  300. package/dist/shell/held-changes-banner-labels.js.map +1 -0
  301. package/dist/shell/held-changes-banner.d.ts +75 -0
  302. package/dist/shell/held-changes-banner.js +106 -0
  303. package/dist/shell/held-changes-banner.js.map +1 -0
  304. package/dist/shell/sync-status-indicator.d.ts +27 -12
  305. package/dist/shell/sync-status-indicator.js +13 -4
  306. package/dist/shell/sync-status-indicator.js.map +1 -1
  307. package/dist/shell/top-bar-parts-labels.d.ts +5 -0
  308. package/dist/shell/top-bar-parts-labels.js +3 -1
  309. package/dist/shell/top-bar-parts-labels.js.map +1 -1
  310. package/dist/shell.d.ts +2 -0
  311. package/dist/shell.js +3 -0
  312. package/dist/shell.js.map +1 -1
  313. package/dist/sync/engine.d.ts +11 -2
  314. package/dist/sync/engine.js +13 -5
  315. package/dist/sync/engine.js.map +1 -1
  316. package/dist/sync/hooks.d.ts +3 -2
  317. package/dist/sync/hooks.js +4 -2
  318. package/dist/sync/hooks.js.map +1 -1
  319. package/dist/testing/css-variables.generated.d.ts +2 -2
  320. package/dist/testing/css-variables.generated.js +1 -1
  321. package/dist/testing/css-variables.generated.js.map +1 -1
  322. package/dist/theme/chart-palette.d.ts +18 -0
  323. package/dist/theme/chart-palette.js +17 -1
  324. package/dist/theme/chart-palette.js.map +1 -1
  325. package/dist/theme/contrast-tokens.d.ts +3 -3
  326. package/dist/theme/contrast-tokens.js.map +1 -1
  327. package/dist/theme/contrast.d.ts +4 -4
  328. package/dist/theme/contrast.js.map +1 -1
  329. package/dist/tour/tour-overlay.js +2 -1
  330. package/dist/tour/tour-overlay.js.map +1 -1
  331. package/package.json +5 -9
  332. package/tokens.css +19 -54
  333. package/src/account/account-parts.ts +0 -47
  334. package/src/account/account-settings-labels.ts +0 -240
  335. package/src/account/account-settings.tsx +0 -698
  336. package/src/account/data-export-setting-labels.ts +0 -20
  337. package/src/account/data-export-setting.tsx +0 -200
  338. package/src/account/delete-account-setting-labels.ts +0 -33
  339. package/src/account/delete-account-setting.tsx +0 -341
  340. package/src/account/email-change-setting-labels.ts +0 -31
  341. package/src/account/email-change-setting.tsx +0 -443
  342. package/src/account/passkeys-setting.tsx +0 -445
  343. package/src/account/sessions-setting-labels.ts +0 -24
  344. package/src/account/sessions-setting.tsx +0 -283
  345. package/src/admin/admin-action-confirm-labels.ts +0 -19
  346. package/src/admin/admin-action-confirm.tsx +0 -509
  347. package/src/admin/admin-action-log-labels.ts +0 -83
  348. package/src/admin/admin-action-log.tsx +0 -464
  349. package/src/admin/admin-area-labels.ts +0 -114
  350. package/src/admin/admin-forbidden.tsx +0 -75
  351. package/src/admin/admin-overview.tsx +0 -258
  352. package/src/admin/admin-parts.ts +0 -68
  353. package/src/admin/admin-sections.tsx +0 -208
  354. package/src/admin/admin-tool-links.tsx +0 -90
  355. package/src/admin/invitations-panel-labels.ts +0 -31
  356. package/src/admin/invitations-panel.tsx +0 -516
  357. package/src/admin/mail-test-card-labels.ts +0 -19
  358. package/src/admin/mail-test-card.tsx +0 -189
  359. package/src/admin/plan-change-labels.ts +0 -5
  360. package/src/admin/plan-change.tsx +0 -474
  361. package/src/admin/reviewer-scope-editor-labels.ts +0 -16
  362. package/src/admin/reviewer-scope-editor.tsx +0 -224
  363. package/src/admin/role-select-labels.ts +0 -16
  364. package/src/admin/role-select.tsx +0 -282
  365. package/src/admin/transfer-ownership-dialog-labels.ts +0 -20
  366. package/src/admin/transfer-ownership-dialog.tsx +0 -162
  367. package/src/admin/user-roster-labels.ts +0 -17
  368. package/src/admin/user-roster.tsx +0 -559
  369. package/src/auth/accept-invitation-labels.ts +0 -21
  370. package/src/auth/accept-invitation.tsx +0 -210
  371. package/src/auth/auth-errors-labels.ts +0 -23
  372. package/src/auth/auth-errors.ts +0 -370
  373. package/src/auth/complete-name-dialog-labels.ts +0 -16
  374. package/src/auth/complete-name-dialog.tsx +0 -253
  375. package/src/auth/email-tag.ts +0 -75
  376. package/src/auth/forgot-password-form-labels.ts +0 -20
  377. package/src/auth/forgot-password-form.tsx +0 -221
  378. package/src/auth/form-rules.ts +0 -40
  379. package/src/auth/new-password-fields.tsx +0 -85
  380. package/src/auth/not-found-page-labels.ts +0 -12
  381. package/src/auth/not-found-page.tsx +0 -95
  382. package/src/auth/register-form-labels.ts +0 -31
  383. package/src/auth/register-form.tsx +0 -456
  384. package/src/auth/reset-password-form-labels.ts +0 -27
  385. package/src/auth/reset-password-form.tsx +0 -341
  386. package/src/auth/sign-in-form-labels.ts +0 -44
  387. package/src/auth/sign-in-form.tsx +0 -858
  388. package/src/auth/status-parts.tsx +0 -132
  389. package/src/auth/verify-email-labels.ts +0 -26
  390. package/src/auth/verify-email.tsx +0 -445
  391. package/src/billing/billing-banner.tsx +0 -258
  392. package/src/billing/billing-labels.ts +0 -381
  393. package/src/billing/billing-lock.ts +0 -108
  394. package/src/billing/billing-overview.ts +0 -157
  395. package/src/billing/billing-standing.ts +0 -80
  396. package/src/billing/cancel-request.ts +0 -97
  397. package/src/billing/cancel-subscription.tsx +0 -98
  398. package/src/billing/checkout-processing.ts +0 -394
  399. package/src/billing/checkout-return.ts +0 -90
  400. package/src/billing/pay-page.ts +0 -330
  401. package/src/billing/plan-card.tsx +0 -562
  402. package/src/billing/plan-change-labels.ts +0 -66
  403. package/src/billing/plan-limit-notice.tsx +0 -148
  404. package/src/billing/plan-limit-toast.tsx +0 -90
  405. package/src/billing/plan-limit.ts +0 -52
  406. package/src/billing/plan-price.ts +0 -98
  407. package/src/billing/plan-switch.tsx +0 -216
  408. package/src/billing/provider-page.ts +0 -84
  409. package/src/billing/subscription-actions.tsx +0 -247
  410. package/src/billing/subscription-status-chip.tsx +0 -95
  411. package/src/chart.ts +0 -103
  412. package/src/components/account-chips-labels.ts +0 -16
  413. package/src/components/account-chips.tsx +0 -474
  414. package/src/components/alert-banner.tsx +0 -670
  415. package/src/components/amount-input.tsx +0 -815
  416. package/src/components/appearance-labels.ts +0 -63
  417. package/src/components/authed-image-labels.ts +0 -11
  418. package/src/components/authed-image.tsx +0 -209
  419. package/src/components/autocomplete.tsx +0 -645
  420. package/src/components/breadcrumbs-labels.ts +0 -10
  421. package/src/components/breadcrumbs.tsx +0 -205
  422. package/src/components/bulk-action-bar-labels.ts +0 -11
  423. package/src/components/bulk-action-bar.tsx +0 -443
  424. package/src/components/button-group.tsx +0 -223
  425. package/src/components/button-look.ts +0 -256
  426. package/src/components/button.tsx +0 -339
  427. package/src/components/calculator.tsx +0 -341
  428. package/src/components/calendar-heatmap-labels.ts +0 -15
  429. package/src/components/calendar-heatmap.tsx +0 -594
  430. package/src/components/card.tsx +0 -377
  431. package/src/components/chart-edge.ts +0 -106
  432. package/src/components/chart-figure.tsx +0 -35
  433. package/src/components/chart-height.ts +0 -30
  434. package/src/components/chart-zoom-core.tsx +0 -627
  435. package/src/components/chart-zoom.tsx +0 -22
  436. package/src/components/chart.tsx +0 -554
  437. package/src/components/checkbox-group.tsx +0 -247
  438. package/src/components/checkbox.tsx +0 -346
  439. package/src/components/chip-input.tsx +0 -370
  440. package/src/components/chip-labels.ts +0 -13
  441. package/src/components/chip.tsx +0 -843
  442. package/src/components/choice-card.tsx +0 -544
  443. package/src/components/column-mapper-labels.ts +0 -38
  444. package/src/components/column-mapper.tsx +0 -655
  445. package/src/components/combobox-core.tsx +0 -1321
  446. package/src/components/combobox-suggestions.tsx +0 -131
  447. package/src/components/combobox.tsx +0 -716
  448. package/src/components/confirm-context.ts +0 -7
  449. package/src/components/confirm-dialog-labels.ts +0 -13
  450. package/src/components/confirm-dialog.tsx +0 -341
  451. package/src/components/copy-button-labels.ts +0 -13
  452. package/src/components/copy-button.tsx +0 -192
  453. package/src/components/country-select-labels.ts +0 -11
  454. package/src/components/country-select.tsx +0 -539
  455. package/src/components/currency-option-list.tsx +0 -180
  456. package/src/components/currency-select.tsx +0 -341
  457. package/src/components/danger-confirm-labels.ts +0 -20
  458. package/src/components/danger-confirm.tsx +0 -934
  459. package/src/components/data-table-cells.tsx +0 -118
  460. package/src/components/data-table-column-settings.tsx +0 -198
  461. package/src/components/data-table-filter-popover.tsx +0 -281
  462. package/src/components/data-table-filters.ts +0 -287
  463. package/src/components/data-table-fit.ts +0 -301
  464. package/src/components/data-table-groups.tsx +0 -288
  465. package/src/components/data-table-head.tsx +0 -280
  466. package/src/components/data-table-labels.ts +0 -198
  467. package/src/components/data-table-leading-row.tsx +0 -58
  468. package/src/components/data-table-mobile.tsx +0 -1117
  469. package/src/components/data-table-pagination.tsx +0 -202
  470. package/src/components/data-table-resize.ts +0 -188
  471. package/src/components/data-table-row-actions.tsx +0 -302
  472. package/src/components/data-table-row.tsx +0 -496
  473. package/src/components/data-table-selection.ts +0 -71
  474. package/src/components/data-table-shared.tsx +0 -287
  475. package/src/components/data-table-sort.ts +0 -163
  476. package/src/components/data-table-virtual.ts +0 -576
  477. package/src/components/data-table.tsx +0 -2598
  478. package/src/components/date-field.tsx +0 -684
  479. package/src/components/date-format-setting.tsx +0 -64
  480. package/src/components/date-picker.tsx +0 -404
  481. package/src/components/date-range-picker.tsx +0 -518
  482. package/src/components/description-list-labels.ts +0 -9
  483. package/src/components/description-list.tsx +0 -386
  484. package/src/components/dialog-frame-labels.ts +0 -7
  485. package/src/components/dialog-frame.tsx +0 -276
  486. package/src/components/disclosure.tsx +0 -476
  487. package/src/components/dropdown.tsx +0 -461
  488. package/src/components/empty-state.tsx +0 -136
  489. package/src/components/entity-combobox.tsx +0 -422
  490. package/src/components/error-boundary-labels.ts +0 -20
  491. package/src/components/error-boundary.tsx +0 -675
  492. package/src/components/facing-pair.ts +0 -93
  493. package/src/components/field-base-labels.ts +0 -20
  494. package/src/components/field-base.tsx +0 -656
  495. package/src/components/field-parts.tsx +0 -825
  496. package/src/components/field-strip.tsx +0 -129
  497. package/src/components/field.tsx +0 -206
  498. package/src/components/file-button.tsx +0 -295
  499. package/src/components/file-dropzone.tsx +0 -559
  500. package/src/components/floating-panel-labels.ts +0 -10
  501. package/src/components/floating-panel.tsx +0 -828
  502. package/src/components/focus-ring.ts +0 -16
  503. package/src/components/form-actions-labels.ts +0 -11
  504. package/src/components/form-actions.tsx +0 -578
  505. package/src/components/full-bleed-dialog.tsx +0 -294
  506. package/src/components/grouped-picker.tsx +0 -184
  507. package/src/components/help-popover.tsx +0 -117
  508. package/src/components/hover-menu.tsx +0 -343
  509. package/src/components/iban-input-labels.ts +0 -14
  510. package/src/components/iban-input.tsx +0 -236
  511. package/src/components/icon-button.tsx +0 -959
  512. package/src/components/icon-picker-labels.ts +0 -13
  513. package/src/components/icon-picker.tsx +0 -232
  514. package/src/components/image-grid-labels.ts +0 -11
  515. package/src/components/image-grid.tsx +0 -202
  516. package/src/components/inline-confirm.tsx +0 -116
  517. package/src/components/inline-edit-field-labels.ts +0 -11
  518. package/src/components/inline-edit-field.tsx +0 -552
  519. package/src/components/inline-entity-combobox.tsx +0 -849
  520. package/src/components/input.tsx +0 -394
  521. package/src/components/jump-calendar.tsx +0 -265
  522. package/src/components/language-select.tsx +0 -93
  523. package/src/components/legal-labels.ts +0 -105
  524. package/src/components/legal-page.tsx +0 -711
  525. package/src/components/legal.tsx +0 -668
  526. package/src/components/lightbox-labels.ts +0 -18
  527. package/src/components/lightbox.tsx +0 -399
  528. package/src/components/line-items-labels.ts +0 -14
  529. package/src/components/line-items.tsx +0 -602
  530. package/src/components/list-labels.ts +0 -12
  531. package/src/components/list.tsx +0 -870
  532. package/src/components/loading-state.tsx +0 -78
  533. package/src/components/measured-grid-labels.ts +0 -25
  534. package/src/components/measured-grid.tsx +0 -837
  535. package/src/components/menu-item.tsx +0 -241
  536. package/src/components/mini-calendar-labels.ts +0 -17
  537. package/src/components/mini-calendar.tsx +0 -789
  538. package/src/components/modal.tsx +0 -505
  539. package/src/components/money-draft.ts +0 -83
  540. package/src/components/money-field.tsx +0 -76
  541. package/src/components/month-grid.tsx +0 -229
  542. package/src/components/month-picker-labels.ts +0 -22
  543. package/src/components/month-picker.tsx +0 -1087
  544. package/src/components/multi-entity-combobox.tsx +0 -369
  545. package/src/components/multi-select.tsx +0 -376
  546. package/src/components/nav-pills.tsx +0 -198
  547. package/src/components/number-field.tsx +0 -327
  548. package/src/components/number-input.tsx +0 -558
  549. package/src/components/numeric-field.ts +0 -169
  550. package/src/components/numpad-sheet.tsx +0 -285
  551. package/src/components/one-time-code-input.tsx +0 -270
  552. package/src/components/page-contents-labels.ts +0 -9
  553. package/src/components/page-contents.tsx +0 -282
  554. package/src/components/page-header-context.ts +0 -8
  555. package/src/components/page-header.tsx +0 -298
  556. package/src/components/password-strength-labels.ts +0 -22
  557. package/src/components/password-strength.tsx +0 -241
  558. package/src/components/phone-input-labels.ts +0 -10
  559. package/src/components/phone-input.tsx +0 -282
  560. package/src/components/picker-sheet.tsx +0 -330
  561. package/src/components/pie-chart-labels.ts +0 -40
  562. package/src/components/pie-chart.tsx +0 -480
  563. package/src/components/popover-labels.ts +0 -14
  564. package/src/components/popover.tsx +0 -268
  565. package/src/components/progress-bar-labels.ts +0 -10
  566. package/src/components/progress-bar.tsx +0 -479
  567. package/src/components/qr-code.tsx +0 -76
  568. package/src/components/reauth-dialog-labels.ts +0 -13
  569. package/src/components/reauth-dialog.tsx +0 -265
  570. package/src/components/row-actions-labels.ts +0 -12
  571. package/src/components/row-actions.tsx +0 -795
  572. package/src/components/scroll-area.tsx +0 -123
  573. package/src/components/search-field.tsx +0 -192
  574. package/src/components/select.tsx +0 -294
  575. package/src/components/separator.tsx +0 -33
  576. package/src/components/series-chart-axes.ts +0 -342
  577. package/src/components/series-chart-budget.ts +0 -150
  578. package/src/components/series-chart-labels.ts +0 -33
  579. package/src/components/series-chart-thin.ts +0 -180
  580. package/src/components/series-chart-ticks.ts +0 -194
  581. package/src/components/series-chart.tsx +0 -1906
  582. package/src/components/server-wake-labels.ts +0 -11
  583. package/src/components/server-wake.tsx +0 -129
  584. package/src/components/settings-fields.tsx +0 -237
  585. package/src/components/share-card-labels.ts +0 -30
  586. package/src/components/share-card.tsx +0 -675
  587. package/src/components/signature-pad-labels.ts +0 -24
  588. package/src/components/signature-pad.tsx +0 -747
  589. package/src/components/signed-amount-labels.ts +0 -10
  590. package/src/components/signed-amount.tsx +0 -336
  591. package/src/components/skeleton.tsx +0 -80
  592. package/src/components/slider.tsx +0 -338
  593. package/src/components/sparkline-labels.ts +0 -14
  594. package/src/components/sparkline.tsx +0 -322
  595. package/src/components/spinner.tsx +0 -81
  596. package/src/components/stat-tile-labels.ts +0 -15
  597. package/src/components/stat-tile.tsx +0 -725
  598. package/src/components/status-dot.tsx +0 -170
  599. package/src/components/swatch-picker-labels.ts +0 -10
  600. package/src/components/swatch-picker.tsx +0 -283
  601. package/src/components/swipeable-row.tsx +0 -386
  602. package/src/components/switch.tsx +0 -273
  603. package/src/components/table-skeleton.tsx +0 -136
  604. package/src/components/table.tsx +0 -647
  605. package/src/components/tabs-labels.ts +0 -13
  606. package/src/components/tabs.tsx +0 -693
  607. package/src/components/text-link.tsx +0 -359
  608. package/src/components/text.tsx +0 -150
  609. package/src/components/textarea.tsx +0 -330
  610. package/src/components/tile-radio.tsx +0 -332
  611. package/src/components/time-input.tsx +0 -121
  612. package/src/components/toast-labels.ts +0 -12
  613. package/src/components/toast.tsx +0 -519
  614. package/src/components/toggle-group.tsx +0 -752
  615. package/src/components/toggle-legend.tsx +0 -394
  616. package/src/components/tooltip.tsx +0 -1149
  617. package/src/components/translation-review-editor.tsx +0 -290
  618. package/src/components/translation-review-labels.ts +0 -181
  619. package/src/components/translation-review-parts.tsx +0 -272
  620. package/src/components/translation-review.tsx +0 -1240
  621. package/src/components/tree-view.tsx +0 -589
  622. package/src/components/treemap-labels.ts +0 -47
  623. package/src/components/treemap-layout.ts +0 -210
  624. package/src/components/treemap.tsx +0 -1184
  625. package/src/components/trigger-aria.ts +0 -44
  626. package/src/components/use-anchor-dir.ts +0 -20
  627. package/src/components/use-mobile-reveal.ts +0 -62
  628. package/src/components/use-table-state.ts +0 -753
  629. package/src/components/user-avatar.tsx +0 -116
  630. package/src/components/wizard-stepper.tsx +0 -42
  631. package/src/components/write-lock-labels.ts +0 -10
  632. package/src/components/write-lock.tsx +0 -253
  633. package/src/currencies.ts +0 -70
  634. package/src/data-table.ts +0 -42
  635. package/src/demo/demo-banner.tsx +0 -153
  636. package/src/demo/demo-countdown.ts +0 -136
  637. package/src/demo/demo-ended.tsx +0 -90
  638. package/src/demo/demo-labels.ts +0 -78
  639. package/src/demo/demo-session.ts +0 -32
  640. package/src/demo/demo-start.tsx +0 -156
  641. package/src/eslint/index.ts +0 -161
  642. package/src/eslint/role-utilities.generated.ts +0 -146
  643. package/src/eslint/role-utilities.ts +0 -233
  644. package/src/feedback/feedback-attachment-download.tsx +0 -77
  645. package/src/feedback/feedback-attachment-labels.ts +0 -37
  646. package/src/feedback/feedback-attachment.tsx +0 -1239
  647. package/src/feedback/feedback-capture.ts +0 -287
  648. package/src/feedback/feedback-comment-thread.tsx +0 -353
  649. package/src/feedback/feedback-context-labels.ts +0 -10
  650. package/src/feedback/feedback-context.tsx +0 -213
  651. package/src/feedback/feedback-crash.ts +0 -547
  652. package/src/feedback/feedback-dialog-labels.ts +0 -29
  653. package/src/feedback/feedback-dialog.tsx +0 -440
  654. package/src/feedback/feedback-entry-file.tsx +0 -150
  655. package/src/feedback/feedback-inbox.tsx +0 -552
  656. package/src/feedback/feedback-labels.ts +0 -144
  657. package/src/feedback/feedback-menu-labels.ts +0 -12
  658. package/src/feedback/feedback-menu.tsx +0 -177
  659. package/src/feedback/feedback-mutation.ts +0 -20
  660. package/src/feedback/feedback-page-link.ts +0 -90
  661. package/src/feedback/feedback-public-labels.ts +0 -81
  662. package/src/feedback/feedback-public-list.tsx +0 -524
  663. package/src/feedback/feedback-public-switch.tsx +0 -102
  664. package/src/feedback/feedback-public-types.ts +0 -101
  665. package/src/feedback/feedback-record.ts +0 -423
  666. package/src/feedback/feedback-replies-context.ts +0 -9
  667. package/src/feedback/feedback-replies.tsx +0 -343
  668. package/src/feedback/feedback-rework-entry-chip.tsx +0 -18
  669. package/src/feedback/feedback-row-detail-labels.ts +0 -30
  670. package/src/feedback/feedback-row-detail.tsx +0 -858
  671. package/src/feedback/feedback-status-undo.ts +0 -109
  672. package/src/feedback/feedback-submit-dialog.tsx +0 -118
  673. package/src/feedback/feedback-submit.tsx +0 -237
  674. package/src/feedback/feedback-swipe.tsx +0 -192
  675. package/src/feedback/feedback-table-labels.ts +0 -27
  676. package/src/feedback/feedback-table.tsx +0 -912
  677. package/src/feedback/feedback-thread-labels.ts +0 -22
  678. package/src/feedback/feedback-thread-parts-labels.ts +0 -41
  679. package/src/feedback/feedback-thread.tsx +0 -905
  680. package/src/feedback/feedback-vocabulary.ts +0 -263
  681. package/src/feedback/feedback-vote-button.tsx +0 -124
  682. package/src/feedback.ts +0 -116
  683. package/src/hooks/use-anchored-panel.ts +0 -396
  684. package/src/hooks/use-anchored-rect.ts +0 -77
  685. package/src/hooks/use-announce.ts +0 -85
  686. package/src/hooks/use-authed-src.ts +0 -167
  687. package/src/hooks/use-body-scroll-lock.ts +0 -119
  688. package/src/hooks/use-breakpoint.ts +0 -133
  689. package/src/hooks/use-close-transition.ts +0 -107
  690. package/src/hooks/use-copy-to-clipboard.ts +0 -122
  691. package/src/hooks/use-debounce.ts +0 -114
  692. package/src/hooks/use-dismiss.ts +0 -371
  693. package/src/hooks/use-document-title.ts +0 -33
  694. package/src/hooks/use-file-drop.ts +0 -121
  695. package/src/hooks/use-file-picker-labels.ts +0 -28
  696. package/src/hooks/use-file-picker.tsx +0 -359
  697. package/src/hooks/use-focus-trap.ts +0 -199
  698. package/src/hooks/use-hotkey.ts +0 -189
  699. package/src/hooks/use-large-text.ts +0 -41
  700. package/src/hooks/use-long-press.ts +0 -140
  701. package/src/hooks/use-media-query.ts +0 -39
  702. package/src/hooks/use-mounted.ts +0 -39
  703. package/src/hooks/use-noindex.ts +0 -37
  704. package/src/hooks/use-overlay-history.ts +0 -714
  705. package/src/hooks/use-row-swipe.ts +0 -207
  706. package/src/hooks/use-search-param-state.ts +0 -434
  707. package/src/hooks/use-windowed-rows.ts +0 -225
  708. package/src/i18n/defaults.ts +0 -267
  709. package/src/i18n/german.ts +0 -2057
  710. package/src/i18n/kit-labels.tsx +0 -1003
  711. package/src/i18n/languages.ts +0 -349
  712. package/src/i18n/locales/de-CH.ts +0 -24
  713. package/src/i18n/locales/en.ts +0 -267
  714. package/src/i18n/locales/es.ts +0 -1997
  715. package/src/i18n/locales/fr.ts +0 -2035
  716. package/src/i18n/locales/hu.ts +0 -2002
  717. package/src/i18n/locales/it.ts +0 -2012
  718. package/src/i18n/locales/zh.ts +0 -1910
  719. package/src/i18n/plurals.ts +0 -86
  720. package/src/i18n/review.ts +0 -475
  721. package/src/i18n/shared-labels.ts +0 -138
  722. package/src/i18n/swiss.ts +0 -29
  723. package/src/index.ts +0 -1179
  724. package/src/landing/access.ts +0 -93
  725. package/src/landing/landing-actions.tsx +0 -97
  726. package/src/landing/landing-labels.ts +0 -59
  727. package/src/landing/landing-sections.tsx +0 -341
  728. package/src/landing/page-seo.ts +0 -261
  729. package/src/landing/public-header.tsx +0 -121
  730. package/src/landing/public-page.tsx +0 -106
  731. package/src/landing/routing.tsx +0 -225
  732. package/src/lib/broadcast.ts +0 -52
  733. package/src/lib/calc.ts +0 -323
  734. package/src/lib/chip-edge-snap.ts +0 -212
  735. package/src/lib/clipboard-minus.ts +0 -107
  736. package/src/lib/clipping.ts +0 -34
  737. package/src/lib/cn.ts +0 -28
  738. package/src/lib/column-mapping.ts +0 -234
  739. package/src/lib/countries.ts +0 -91
  740. package/src/lib/data-attributes.ts +0 -14
  741. package/src/lib/date-format.ts +0 -227
  742. package/src/lib/dates.ts +0 -266
  743. package/src/lib/day-format.ts +0 -96
  744. package/src/lib/decimal-marks.ts +0 -108
  745. package/src/lib/describe-failure.ts +0 -18
  746. package/src/lib/device-settings.ts +0 -147
  747. package/src/lib/direction.ts +0 -74
  748. package/src/lib/document-navigation.ts +0 -17
  749. package/src/lib/document-title.ts +0 -41
  750. package/src/lib/download.ts +0 -172
  751. package/src/lib/float-clearance.ts +0 -70
  752. package/src/lib/floating-stack.ts +0 -70
  753. package/src/lib/focus-left.ts +0 -156
  754. package/src/lib/format.ts +0 -522
  755. package/src/lib/http-status.ts +0 -38
  756. package/src/lib/iban.ts +0 -251
  757. package/src/lib/ime.ts +0 -24
  758. package/src/lib/intl-cache.ts +0 -25
  759. package/src/lib/iso-day.ts +0 -13
  760. package/src/lib/lazy-with-fallback.ts +0 -82
  761. package/src/lib/logger.ts +0 -69
  762. package/src/lib/minus.ts +0 -50
  763. package/src/lib/path-pattern.ts +0 -55
  764. package/src/lib/person-name.ts +0 -155
  765. package/src/lib/phone.ts +0 -208
  766. package/src/lib/plain-text.ts +0 -22
  767. package/src/lib/presence.ts +0 -18
  768. package/src/lib/qr-encode.ts +0 -444
  769. package/src/lib/quiet-focus.ts +0 -32
  770. package/src/lib/reduced-motion.ts +0 -22
  771. package/src/lib/refs.ts +0 -22
  772. package/src/lib/round.ts +0 -43
  773. package/src/lib/safe-storage.ts +0 -119
  774. package/src/lib/server-wake.ts +0 -315
  775. package/src/lib/session-refresh.ts +0 -472
  776. package/src/lib/share-format.ts +0 -18
  777. package/src/lib/status-tone.ts +0 -43
  778. package/src/lib/strip-fade.ts +0 -287
  779. package/src/lib/table-text.ts +0 -554
  780. package/src/lib/text-case.ts +0 -19
  781. package/src/lib/text-match.ts +0 -169
  782. package/src/lib/thenable.ts +0 -19
  783. package/src/lib/translation-review.ts +0 -717
  784. package/src/notifications/device-zone-report.ts +0 -67
  785. package/src/notifications/feedback-notification-types-labels.ts +0 -33
  786. package/src/notifications/feedback-notification-types.ts +0 -71
  787. package/src/notifications/notification-bell.tsx +0 -119
  788. package/src/notifications/notification-inbox.tsx +0 -309
  789. package/src/notifications/notification-parts.tsx +0 -81
  790. package/src/notifications/notification-types-setting.tsx +0 -406
  791. package/src/notifications/notification-types.ts +0 -89
  792. package/src/notifications/notifications-labels.ts +0 -287
  793. package/src/notifications/push-device-setting.tsx +0 -230
  794. package/src/notifications/push-devices-setting.tsx +0 -374
  795. package/src/notifications/push-worker.ts +0 -168
  796. package/src/notifications/quiet-hours-setting.tsx +0 -238
  797. package/src/notifications/unsubscribe-panel.tsx +0 -200
  798. package/src/notifications/use-push-device.ts +0 -448
  799. package/src/offline/local-first.ts +0 -100
  800. package/src/offline/pwa-update-prompt-labels.ts +0 -9
  801. package/src/offline/pwa-update-prompt.tsx +0 -78
  802. package/src/offline/query-persistence.ts +0 -391
  803. package/src/offline/service-worker-updates.ts +0 -81
  804. package/src/offline/shared-computer-labels.ts +0 -8
  805. package/src/offline/shared-computer.tsx +0 -87
  806. package/src/offline/sign-out-pending-dialog-labels.ts +0 -21
  807. package/src/offline/sign-out-pending-dialog.tsx +0 -135
  808. package/src/offline.ts +0 -37
  809. package/src/push-worker.ts +0 -25
  810. package/src/rhf/fields.tsx +0 -2187
  811. package/src/rhf/form-core.tsx +0 -324
  812. package/src/rhf/form.tsx +0 -12
  813. package/src/rhf/line-items.tsx +0 -135
  814. package/src/rhf/use-rhf-wizard-step.ts +0 -104
  815. package/src/rhf.ts +0 -21
  816. package/src/search/command-palette-labels.ts +0 -16
  817. package/src/search/command-palette.tsx +0 -624
  818. package/src/search/global-search-labels.ts +0 -13
  819. package/src/search/global-search.tsx +0 -660
  820. package/src/search/search-index.ts +0 -283
  821. package/src/search/use-command-key.ts +0 -23
  822. package/src/search.ts +0 -14
  823. package/src/settings/device-reset-setting-labels.ts +0 -20
  824. package/src/settings/device-reset-setting.tsx +0 -116
  825. package/src/settings/settings-card-classes.ts +0 -11
  826. package/src/settings/settings-catalogue.ts +0 -150
  827. package/src/settings/settings-context.ts +0 -33
  828. package/src/settings/settings-heading.tsx +0 -67
  829. package/src/settings/settings-labels.ts +0 -87
  830. package/src/settings/settings-layout.tsx +0 -471
  831. package/src/settings/settings-section.tsx +0 -108
  832. package/src/settings/use-account-appearance.ts +0 -148
  833. package/src/settings/use-account-language.ts +0 -146
  834. package/src/settings/use-settings-focus.ts +0 -128
  835. package/src/settings/use-settings-route.ts +0 -258
  836. package/src/shell/account-menu-language.tsx +0 -130
  837. package/src/shell/account-menu-text-size.tsx +0 -80
  838. package/src/shell/admin-menu.tsx +0 -183
  839. package/src/shell/app-shell-labels.ts +0 -11
  840. package/src/shell/app-shell.tsx +0 -1560
  841. package/src/shell/auth-layout.tsx +0 -186
  842. package/src/shell/company-switcher-labels.ts +0 -13
  843. package/src/shell/company-switcher.tsx +0 -136
  844. package/src/shell/dev-menu.tsx +0 -257
  845. package/src/shell/fill-height.tsx +0 -45
  846. package/src/shell/language-code.ts +0 -5
  847. package/src/shell/option-switcher-menu.tsx +0 -84
  848. package/src/shell/sync-status-indicator.tsx +0 -303
  849. package/src/shell/top-bar-actions.tsx +0 -87
  850. package/src/shell/top-bar-brand.tsx +0 -78
  851. package/src/shell/top-bar-parts-labels.ts +0 -148
  852. package/src/shell/top-bar.tsx +0 -40
  853. package/src/shell/topbar-action-menu.tsx +0 -567
  854. package/src/shell/topbar-controls.tsx +0 -269
  855. package/src/shell/tour-menu.tsx +0 -187
  856. package/src/shell.ts +0 -108
  857. package/src/sync/db.ts +0 -129
  858. package/src/sync/engine.ts +0 -1011
  859. package/src/sync/hooks.ts +0 -98
  860. package/src/sync/testing.ts +0 -73
  861. package/src/sync/wire.ts +0 -61
  862. package/src/sync.ts +0 -37
  863. package/src/table-text.ts +0 -8
  864. package/src/testing/css-variables.generated.ts +0 -112
  865. package/src/testing/css-variables.ts +0 -189
  866. package/src/testing/index.ts +0 -29
  867. package/src/testing/react.ts +0 -58
  868. package/src/testing/tour.ts +0 -41
  869. package/src/testing/viewport.ts +0 -357
  870. package/src/theme/chart-palette.ts +0 -232
  871. package/src/theme/color.ts +0 -272
  872. package/src/theme/contrast-tokens.ts +0 -268
  873. package/src/theme/contrast.ts +0 -250
  874. package/src/theme/palette-derive.ts +0 -825
  875. package/src/theme/palette-presets.ts +0 -544
  876. package/src/theme/palette-store.ts +0 -108
  877. package/src/theme/text-size.ts +0 -289
  878. package/src/theme/theme-store.ts +0 -157
  879. package/src/tour/tour-catalogue-labels.ts +0 -75
  880. package/src/tour/tour-catalogue.tsx +0 -335
  881. package/src/tour/tour-dock.ts +0 -134
  882. package/src/tour/tour-engine.ts +0 -37
  883. package/src/tour/tour-gates.ts +0 -218
  884. package/src/tour/tour-guide-button.tsx +0 -64
  885. package/src/tour/tour-guide-labels.ts +0 -15
  886. package/src/tour/tour-info.ts +0 -144
  887. package/src/tour/tour-labels.ts +0 -23
  888. package/src/tour/tour-overlay.tsx +0 -1170
  889. package/src/tour/tour.tsx +0 -750
  890. package/src/tour/use-tour-progress.ts +0 -671
  891. package/src/tour/use-tour-url-sync.ts +0 -266
  892. package/src/tour.ts +0 -21
  893. package/src/vite-env.d.ts +0 -23
  894. package/src/wizard/stepper-nav.tsx +0 -360
  895. package/src/wizard/types.ts +0 -275
  896. package/src/wizard/use-wizard.ts +0 -560
  897. package/src/wizard/validation.ts +0 -39
  898. package/src/wizard/wizard-context.tsx +0 -74
  899. package/src/wizard/wizard-labels.ts +0 -29
  900. package/src/wizard/wizard-step.tsx +0 -76
  901. package/src/wizard/wizard-summary.tsx +0 -84
  902. package/src/wizard.ts +0 -15
@@ -1,1011 +0,0 @@
1
- import { listenOn, postOnce } from "../lib/broadcast";
2
- import { httpStatusOf } from "../lib/http-status";
3
- import { isOfflineError, stampAfter } from "../offline/local-first";
4
- import { isSharedComputer } from "../offline/shared-computer";
5
- import type { OutboxChange, OutboxStatus, SyncDb, SyncedRow } from "./db";
6
- import type { SyncRequestBody, SyncResponseBody, SyncWireChange } from "./wire";
7
-
8
- /*
9
- * The sync engine (docs/local-first-harmonization.md §2.3): kastlan's handover client,
10
- * generalised (D10). Keksdose's SERVER rules and wire shapes; the client policies are ka's —
11
- * a new edit merges into the row's last `pending` upsert, a refused change is held per change
12
- * (`waiting` / `blocked` / `rejected`), a capped backoff on errors, the queue kept at sign-out.
13
- *
14
- * A round, per scope in `scopes()` order (read every round, D11): take the scope's queue and
15
- * mark it `sending`; post `{scope, last_knowledge, changes, refusals, client_now, paged?}`
16
- * with `client_now` read fresh (D2 amendment); apply the answer in ONE transaction, only if
17
- * the generation is unchanged (D15); follow `page_cursor` with pull-only requests, the cursor
18
- * written with the last page only (D12). Then the app's `steps`, then one more pass if a step
19
- * changed something. One round at a time; a request during a round runs once more after it.
20
- *
21
- * Across tabs (0.52; refactor audit 2026-10-30, FLW-1): every tab runs its own engine on the one
22
- * IndexedDB copy, and before 0.52 nothing stood between them — a second tab took the first's
23
- * `sending` changes as a closed tab's and sent them again, its failure laid them back so an edit
24
- * merged into a change already out and was deleted with it, and a sign-out in one tab was pulled
25
- * straight back from 0 by the other's poll. Now a round runs under a Web Lock named after the
26
- * database, so the rounds of all tabs take turns, first come first served; and `forget()`
27
- * writes a new epoch into `meta` with the clear, which every round reads, so another tab's
28
- * round drops its answer and that tab's engine stops.
29
- */
30
-
31
- export interface SyncCounts {
32
- pending: number;
33
- waiting: number;
34
- blocked: number;
35
- rejected: number;
36
- }
37
-
38
- export interface SyncStepContext {
39
- signal: AbortSignal;
40
- /** False once `forget()` bumped the generation — in this tab, or (0.52) in another tab on
41
- * the same database, heard over a BroadcastChannel: the step writes nothing more (D15). */
42
- isCurrent(): boolean;
43
- }
44
-
45
- export interface SyncStepResult {
46
- /** The step met no connection: the round ends offline. */
47
- offline?: boolean;
48
- /** One more push/pull in the same round (kastlan's second pass after its photos). */
49
- changed?: boolean;
50
- /** What the step keeps on the device, added to the engine's counts. */
51
- counts?: Partial<SyncCounts>;
52
- }
53
-
54
- /** Work after the push/pull, such as kastlan's photo upload. `D` is the app's database type
55
- * (`createSyncDb<HandoverDb>`), so a step reaches the app's own tables without a cast. */
56
- export interface SyncStep<D extends SyncDb = SyncDb> {
57
- name: string;
58
- run(db: D, ctx: SyncStepContext): Promise<SyncStepResult>;
59
- /** Its counts read from the device between rounds (a photo taken offline shows at once). */
60
- counts?(db: D): Promise<Partial<SyncCounts>>;
61
- }
62
-
63
- export interface SyncEngineState {
64
- phase: "idle" | "syncing" | "offline" | "error";
65
- online: boolean;
66
- counts: SyncCounts;
67
- /** When a scope's walk last completed (ISO), kept in `meta` (D9). */
68
- lastSyncedAt: string | null;
69
- /** When the next attempt after an error runs (epoch ms), or `null`. */
70
- nextRetryAt: number | null;
71
- }
72
-
73
- export interface SyncRoundResult {
74
- /** `paused`: `paused()` held the round. (A round asked for while another tab's round holds
75
- * the database waits its turn, 0.52.) `dropped`: `forget()` ran during it, here or in
76
- * another tab, its answer unused. */
77
- outcome: "synced" | "offline" | "error" | "paused" | "dropped";
78
- /** Changes this round set `waiting`, `blocked` or `rejected`. */
79
- waiting: number;
80
- blocked: number;
81
- rejected: number;
82
- error?: unknown;
83
- }
84
-
85
- export interface SyncEngineOptions<D extends SyncDb = SyncDb> {
86
- /** The database; its type `D` is the one the `steps` get. */
87
- db: D;
88
- /** The scopes this session's role holds, in round order (kastlan: handover, meetings, meters,
89
- * maintenance, contacts). Read at the start of every round: roles are per company. */
90
- scopes: () => readonly string[];
91
- /** Posts one request (`POST /sync`) and answers its body; throws as the app's client does. */
92
- send: (body: SyncRequestBody, signal: AbortSignal) => Promise<SyncResponseBody>;
93
- /** Default {@link isOfflineError}: no answer, or the session refresh's passing 401. */
94
- isOffline?: (err: unknown) => boolean;
95
- /** A whole request refused for the plan → every sent change `waiting` with this code.
96
- * Default: a 402 whose body names a `code` (or `detail.code`). */
97
- planCode?: (err: unknown) => string | null;
98
- /** `refused.code`s kept as `waiting` and sent again every round; any other code is
99
- * `blocked`, sent again only on `retryBlocked()`. Default `["billing_read_only"]`. */
100
- holdCodes?: readonly string[];
101
- /** Ask for a paged pull and follow its cursor (D12). */
102
- paged?: boolean;
103
- steps?: readonly SyncStep<D>[];
104
- /** No round while this holds (kastlan: a practice tour fakes the server's answers). */
105
- paused?: () => boolean;
106
- /** The poll (30 s) and the debounce after a write (800 ms). */
107
- pollMs?: number;
108
- soonMs?: number;
109
- /** The waits after consecutive errors; the last repeats (D13). */
110
- backoffMs?: readonly number[];
111
- /** Default `isSharedComputer`: on a computer marked shared the engine does not start (D15). */
112
- sharedComputer?: () => boolean;
113
- /**
114
- * 0.52: the backstop on one round, counted from the moment it holds the database's lock
115
- * (a tab waiting behind a stuck round is not cut with it). Past it the round is cut: its
116
- * requests and steps are aborted, anything they answer or write later is dropped, and the
117
- * round ends `error` with a `TimeoutError` (`DOMException`), not `offline`, so the backoff
118
- * starts and the lock goes to the next tab even when a request or a step ignores its
119
- * signal. The app's own request timeouts come first; this only bounds what they miss.
120
- * Default 10 minutes, long on purpose: kastlan's photo backlog can take minutes, and a cut
121
- * round has still made progress (each photo uploaded before the cut is off the queue).
122
- * `0` or `Infinity`: none.
123
- */
124
- roundTimeoutMs?: number;
125
- }
126
-
127
- export interface SyncEngine {
128
- /** Into the copy at once and into the queue, in the scope the caller names (D11). An edit
129
- * merges into the row's last `pending` upsert; deleting a row that never reached the
130
- * server forgets its queue; stamped with `stampAfter`. */
131
- write(
132
- scope: string,
133
- entity: string,
134
- id: string,
135
- op: "upsert" | "delete",
136
- data?: Record<string, unknown>,
137
- ): Promise<void>;
138
- /** Starts the poll and the round on `online` (nothing on a computer marked shared).
139
- * An engine another tab's `forget()` stopped stays stopped through `start()`: only its
140
- * own `forget()` (the app's session end) or a new engine (`releaseSyncEngine`) re-arms
141
- * it. A start cannot tell a new sign-in from a remount in a tab whose session never
142
- * ended, and the latter would pull the signed-out account back into the wiped copy
143
- * (D15; the 0.52 code review asked, and the contract stands). */
144
- start(): void;
145
- /** Stops the poll and the timers, aborts the round in flight and drops its answer (the
146
- * queue is laid back). A new engine's round on the same database waits for it. */
147
- stop(): void;
148
- /** Sync soon (debounced): after a write. */
149
- requestSync(): void;
150
- /** "Sync now": resets the backoff, runs a round of its own after the one under way — another
151
- * tab's too (0.52) — and answers its result. */
152
- syncNow(): Promise<SyncRoundResult>;
153
- /** A round that sends the `blocked` changes too. */
154
- retryBlocked(): Promise<void>;
155
- /** Drop the rejected changes (of one scope) and pull that scope afresh. */
156
- discardRejected(scope?: string): Promise<void>;
157
- /** The rejected changes with their reasons, for the app's banner. */
158
- rejected(): Promise<OutboxChange[]>;
159
- /** Sign-out: bump the generation (a round in flight writes nothing more), then clear the
160
- * rows and `meta` — and the outbox and the app's tables unless `keepQueue` (D15). In every
161
- * tab (0.52): the clear writes a new epoch into `meta`, so a round of another tab's engine on
162
- * this database drops its answer, and that engine stops — the session it ran for has ended.
163
- * Another tab's `forget()` heard while this clear runs is decided by the epoch the copy is
164
- * left with: one that committed first was undone by this clear and stops nothing. */
165
- forget(opts: { keepQueue: boolean }): Promise<void>;
166
- getState(): SyncEngineState;
167
- subscribe(listener: () => void): () => void;
168
- }
169
-
170
- export const DEFAULT_SYNC_BACKOFF_MS: readonly number[] = [5_000, 15_000, 60_000, 300_000];
171
- export const DEFAULT_HOLD_CODES: readonly string[] = ["billing_read_only"];
172
- /** `roundTimeoutMs`'s default (see there). */
173
- const ROUND_TIMEOUT_MS = 10 * 60_000;
174
- /** The longest delay `setTimeout` keeps (2^31 - 1 ms, ~24.8 days); a longer one fires at once. */
175
- const MAX_TIMEOUT_MS = 2_147_483_647;
176
-
177
- const LAST_SYNCED_AT = "lastSyncedAt";
178
- /** The copy's epoch in `meta` (0.52): a new one with every `forget()`, in its clear. */
179
- const EPOCH = "epoch";
180
- const lastKnowledgeKey = (scope: string) => `lastKnowledge:${scope}`;
181
- const UNSENT: readonly OutboxStatus[] = ["pending", "sending", "waiting", "blocked"];
182
- const ZERO: SyncCounts = { pending: 0, waiting: 0, blocked: 0, rejected: 0 };
183
-
184
- /** The started engine of each database: one engine per database (D15). */
185
- const running = new Map<string, SyncEngine>();
186
-
187
- /** The round in flight on each database, of whichever engine: a new engine's round waits
188
- * for it, so two never take the same queue (a company switch A→B→A within one round). */
189
- const flights = new Map<string, Promise<unknown>>();
190
-
191
- /** The engines {@link syncEngineFor} keeps, by database name. */
192
- const kept = new Map<string, { db: SyncDb; engine: SyncEngine }>();
193
-
194
- /** The generation changed under a round: its answer is dropped. */
195
- class Dropped extends Error {}
196
-
197
- /** The copy's epoch moved since this engine started: `forget()` ran in another tab. The
198
- * round's answer is dropped as well. */
199
- class ForgottenElsewhere extends Dropped {}
200
-
201
- /** The browser's Web Locks: a secure context (https, localhost), Safari 15.4+. */
202
- function webLocks(): LockManager | undefined {
203
- return typeof navigator === "undefined" ? undefined : (navigator as { locks?: LockManager }).locks;
204
- }
205
-
206
- /** A `forget()`'s epoch: unique enough to tell two apart, and on plain http too, where
207
- * `crypto.randomUUID` is missing (refactor audit 2026-10-30, DUP-7). */
208
- function freshEpoch(): string {
209
- const uuid = (globalThis.crypto as Partial<Crypto> | undefined)?.randomUUID;
210
- return uuid ? uuid.call(globalThis.crypto) : `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
211
- }
212
-
213
- /** A row created on this device and never echoed by the server carries `local: true`. */
214
- type CopyRow = SyncedRow & { local?: boolean };
215
-
216
- function browserOnline(): boolean {
217
- return typeof navigator === "undefined" || navigator.onLine !== false;
218
- }
219
-
220
- function defaultPlanCode(err: unknown): string | null {
221
- if (httpStatusOf(err) !== 402) return null;
222
- const body = (err as { response?: { data?: { code?: unknown; detail?: { code?: unknown } } } }).response?.data;
223
- const code = typeof body?.code === "string" ? body.code : body?.detail?.code;
224
- return typeof code === "string" ? code : null;
225
- }
226
-
227
- /** A refusal's `code` and `detail` from an error's body: `{code, detail}` or FastAPI's
228
- * `{detail: {code, detail}}`. */
229
- function refusalOf(err: unknown): { code?: string; detail?: string } {
230
- const body = (err as { response?: { data?: unknown } }).response?.data as
231
- { code?: unknown; detail?: unknown } | undefined;
232
- const inner =
233
- body?.detail && typeof body.detail === "object" ? (body.detail as { code?: unknown; detail?: unknown }) : body;
234
- const text = (v: unknown) => (typeof v === "string" && v ? v : undefined);
235
- return { code: text(inner?.code), detail: text(inner?.detail) };
236
- }
237
-
238
- function latest(stamps: unknown[]): string | undefined {
239
- let best: string | undefined;
240
- for (const s of stamps) {
241
- if (typeof s !== "string" || !Number.isFinite(Date.parse(s))) continue;
242
- if (best === undefined || Date.parse(s) > Date.parse(best)) best = s;
243
- }
244
- return best;
245
- }
246
-
247
- const toWire = ({ entity, id, op, data, updated_at }: OutboxChange): SyncWireChange =>
248
- op === "delete" ? { entity, id, op, updated_at } : { entity, id, op, data, updated_at };
249
-
250
- /**
251
- * The engine on one database (D15: one engine per database; a company switch builds a new
252
- * engine on the new database and stops the old one). Nothing runs until `start()`
253
- * (`useSyncWorker`), and `start()` does nothing on a computer marked shared.
254
- */
255
- export function createSyncEngine<D extends SyncDb = SyncDb>(options: SyncEngineOptions<D>): SyncEngine {
256
- const { db } = options;
257
- const isOffline = options.isOffline ?? isOfflineError;
258
- const planCode = options.planCode ?? defaultPlanCode;
259
- const holdCodes = new Set(options.holdCodes ?? DEFAULT_HOLD_CODES);
260
- const steps = options.steps ?? [];
261
- const pollMs = options.pollMs ?? 30_000;
262
- const soonMs = options.soonMs ?? 800;
263
- const backoffMs = options.backoffMs?.length ? options.backoffMs : DEFAULT_SYNC_BACKOFF_MS;
264
- const sharedComputer = options.sharedComputer ?? isSharedComputer;
265
- const roundTimeoutMs = options.roundTimeoutMs ?? ROUND_TIMEOUT_MS;
266
- /** The rounds the deadline cut, by their signal: their late work lays nothing back. */
267
- const cut = new WeakSet<AbortSignal>();
268
-
269
- let generation = 0;
270
- let state: SyncEngineState = {
271
- phase: "idle",
272
- online: browserOnline(),
273
- counts: ZERO,
274
- lastSyncedAt: null,
275
- nextRetryAt: null,
276
- };
277
- const listeners = new Set<() => void>();
278
- let started = false;
279
- let poll: ReturnType<typeof setInterval> | null = null;
280
- let soon: ReturnType<typeof setTimeout> | null = null;
281
- let retry: ReturnType<typeof setTimeout> | null = null;
282
- let failures = 0;
283
- let current: Promise<SyncRoundResult> | null = null;
284
- let again = false;
285
- let controller: AbortController | null = null;
286
- let stepCounts: Record<string, Partial<SyncCounts>> = {};
287
-
288
- // ── Across tabs (0.52, FLW-1) ───────────────────────────────────────────────
289
- /** The name of this database's Web Lock and BroadcastChannel (one origin's tabs share both,
290
- * as they share the IndexedDB). */
291
- const across = `eifi1-sync:${db.name}`;
292
- /** The copy's epochs this engine runs under: the one read when it started (or at its first
293
- * round), and each its own `forget()` wrote. Any other one means `forget()` ran elsewhere. */
294
- const epochs = new Set<string | null>();
295
- let epochRead: Promise<void> | null = null;
296
- /** `forget()` ran in another tab: this engine runs no round until its own `forget()`. */
297
- let forgotten = false;
298
- /** This engine's own `forget()`s whose clear is under way. */
299
- let forgetting = 0;
300
- let unlisten: (() => void) | null = null;
301
-
302
- function set(next: Partial<SyncEngineState>) {
303
- state = { ...state, ...next };
304
- listeners.forEach((listener) => listener());
305
- }
306
-
307
- /** The counts and `lastSyncedAt` read again from the device. Never throws: a closed or
308
- * deleted database leaves the last counts standing. */
309
- function refreshCounts(): Promise<void> {
310
- return readCounts().catch(() => undefined);
311
- }
312
-
313
- async function readCounts(): Promise<void> {
314
- const gen = generation;
315
- const count = (status: OutboxStatus) => db.outbox.where("status").equals(status).count();
316
- const [pending, sending, waiting, blocked, rejected] = await Promise.all(
317
- (["pending", "sending", "waiting", "blocked", "rejected"] as const).map(count),
318
- );
319
- const fromSteps = { ...stepCounts };
320
- for (const step of steps) if (step.counts) fromSteps[step.name] = await step.counts(db);
321
- const last = (await db.meta.get(LAST_SYNCED_AT))?.value;
322
- if (gen !== generation) return;
323
- if (Object.keys(fromSteps).length) stepCounts = fromSteps;
324
- const counts = { pending: pending + sending, waiting, blocked, rejected };
325
- for (const extra of Object.values(stepCounts)) {
326
- for (const key of Object.keys(counts) as (keyof SyncCounts)[]) counts[key] += extra[key] ?? 0;
327
- }
328
- set({ counts, lastSyncedAt: typeof last === "string" ? last : null });
329
- }
330
-
331
- function readEpoch(): Promise<string | null> {
332
- return db.meta.get(EPOCH).then((entry) => (typeof entry?.value === "string" ? entry.value : null));
333
- }
334
-
335
- /** Read once per engine — at `start()`, before its first round can wait on another tab's —
336
- * so a `forget()` that lands while it waits is seen as another tab's. A failed read is
337
- * tried again by the next round. */
338
- function captureEpoch(): Promise<void> {
339
- if (epochRead) return epochRead;
340
- const read = readEpoch().then((epoch) => void epochs.add(epoch));
341
- epochRead = read;
342
- read.catch(() => {
343
- if (epochRead === read) epochRead = null;
344
- });
345
- return read;
346
- }
347
-
348
- /** While started, hear `forget()` in another tab at once: a step in flight sees
349
- * `isCurrent()` turn false, the request in flight is aborted, and the poll stops. Without
350
- * BroadcastChannel the next round's read of the epoch stops it instead. */
351
- function listen() {
352
- unlisten ??= listenOn(across, (data) => {
353
- const epoch = (data as { epoch?: unknown } | null)?.epoch;
354
- if (typeof epoch === "string" && !epochs.has(epoch)) void forgetHere();
355
- });
356
- }
357
-
358
- /** `forget()` ran in another tab: the session this engine ran for has ended there. Its
359
- * round in flight writes nothing more, and it runs none of its own any more.
360
- *
361
- * Not while its own `forget()` clears the copy: that clear decides, by the epoch it leaves
362
- * in `meta`. 0.52 code review (src/sync/engine.ts): in kastlan's sign-out cascade tab A's
363
- * epoch reached tab B while B's own clear waited behind A's, so B's `forget()` resolved
364
- * with B stopped and forgotten — the forget that re-arms an engine left it dead. */
365
- function forgetHere(): Promise<void> {
366
- if (forgotten || forgetting > 0) return Promise.resolve();
367
- forgotten = true;
368
- if (started) halt();
369
- else drop();
370
- return endSession();
371
- }
372
-
373
- /** The round in flight, or waiting for the lock, writes nothing more: its wait and its
374
- * request are aborted, its answer dropped, the queue laid back. */
375
- function drop() {
376
- generation += 1;
377
- controller?.abort();
378
- }
379
-
380
- /** What the engine shows of a session that ended — by its own `forget()` or another tab's —
381
- * goes with it: the steps' counts, the last sync, an error and its retry. 0.52 code review
382
- * (src/sync/engine.ts): the two paths each reset part of it, and an engine another tab
383
- * stopped showed "error" for a retry that never came. */
384
- function endSession(): Promise<void> {
385
- stepCounts = {};
386
- failures = 0;
387
- clearRetry();
388
- set({ phase: "idle", lastSyncedAt: null, nextRetryAt: null });
389
- return refreshCounts();
390
- }
391
-
392
- /** The round's epoch still stands in `meta`, read in the caller's transaction, so another
393
- * tab's `forget()` commits wholly before it or after it. Else `forget()` ran: in this tab
394
- * (the generation moved too) or in another — then the engine stops. */
395
- async function standing(gen: number, epoch: string | null): Promise<void> {
396
- const now = await readEpoch();
397
- if (gen !== generation) throw new Dropped();
398
- if (now !== epoch) throw epochs.has(now) ? new Dropped() : new ForgottenElsewhere();
399
- }
400
-
401
- // ── The queue ──────────────────────────────────────────────────────────────
402
-
403
- /** Mark the scope's queue as in the request (a `sending` left by a closed tab goes back
404
- * first); answers it with the statuses it had. Under the Web Lock (0.52) a `sending` here
405
- * is a closed tab's, as this assumes: no other tab's round is out. Without Web Locks
406
- * (plain http) it may be another open tab's, as before 0.52.
407
- *
408
- * Only under the round's epoch. 0.52 code review (src/sync/engine.ts): after another tab's
409
- * `forget()` had landed mid-round, every scope left still posted the kept queue under the
410
- * session that had ended and pulled itself from 0, only for `apply` to drop the answer. */
411
- function takeQueue(scope: string, withBlocked: boolean, gen: number, epoch: string | null): Promise<OutboxChange[]> {
412
- return db.transaction("rw", db.outbox, db.meta, async () => {
413
- await standing(gen, epoch);
414
- await db.outbox
415
- .where("scope")
416
- .equals(scope)
417
- .and((c) => c.status === "sending")
418
- .modify({ status: "pending" });
419
- const take: OutboxStatus[] = withBlocked ? ["pending", "waiting", "blocked"] : ["pending", "waiting"];
420
- const queue = (await db.outbox.where("scope").equals(scope).toArray())
421
- .filter((c) => take.includes(c.status))
422
- .sort((a, b) => a.seq! - b.seq!);
423
- await db.outbox.bulkUpdate(queue.map((c) => ({ key: c.seq!, changes: { status: "sending" as const } })));
424
- return queue;
425
- });
426
- }
427
-
428
- /** The request failed or its answer was dropped: each change back to the status it had.
429
- * Not by a round the deadline cut: its late work ends after the next round — this tab's
430
- * or another's — may have taken these changes `sending` again, and laid back `pending`
431
- * under that round an edit would merge into a change already out and be deleted with it
432
- * (FLW-1's lost edit). The next round's `takeQueue` takes them back instead, as a closed
433
- * tab's. */
434
- function layBack(sent: OutboxChange[], signal: AbortSignal) {
435
- if (cut.has(signal)) return;
436
- return db.outbox.bulkUpdate(sent.map((c) => ({ key: c.seq!, changes: { status: c.status } })));
437
- }
438
-
439
- function mark(changes: OutboxChange[], fields: Partial<OutboxChange>) {
440
- return db.outbox.bulkUpdate(changes.map((c) => ({ key: c.seq!, changes: fields })));
441
- }
442
-
443
- /** Put one page's rows in the copy, settle the sent changes, lay the unsent ones back. The
444
- * epoch is read in the same transaction: a `forget()` of another tab either commits before
445
- * it (this answer is dropped) or after it (and clears what it wrote). */
446
- async function apply(
447
- scope: string,
448
- answer: SyncResponseBody,
449
- sent: OutboxChange[],
450
- gen: number,
451
- epoch: string | null,
452
- lastPage: boolean,
453
- ) {
454
- return db.transaction("rw", db.rows, db.outbox, db.meta, async () => {
455
- await standing(gen, epoch);
456
- const refused = answer.refused ?? null;
457
- const refusedIds = new Set(refused?.change_ids ?? []);
458
- const rejections = new Map((answer.rejections ?? []).map((r) => [r.change_id, r]));
459
- const kept = sent.filter((c) => refusedIds.has(c.id));
460
- const rejected = sent.filter((c) => !refusedIds.has(c.id) && rejections.has(c.id));
461
- await db.outbox.bulkDelete(sent.filter((c) => !refusedIds.has(c.id) && !rejections.has(c.id)).map((c) => c.seq!));
462
- const hold = refused != null && holdCodes.has(refused.code);
463
- if (refused)
464
- await mark(kept, { status: hold ? "waiting" : "blocked", code: refused.code, reason: refused.detail });
465
- for (const change of rejected) {
466
- const why = rejections.get(change.id)!;
467
- await db.outbox.update(change.seq!, { status: "rejected", code: why.code, reason: why.detail });
468
- }
469
- const pulled = new Set<string>();
470
- for (const change of answer.changes) {
471
- pulled.add(`${change.entity}\u0000${change.id}`);
472
- if (change.op === "delete") await db.rows.delete([change.entity, change.id]);
473
- else {
474
- const row: CopyRow = {
475
- entity: change.entity,
476
- id: change.id,
477
- pk: change.pk ?? null,
478
- local: false,
479
- data: change.data ?? {},
480
- // The server's stamp rides beside `data`: kept, so the next edit is stamped after it.
481
- updated_at: change.updated_at ?? null,
482
- };
483
- await db.rows.put(row);
484
- }
485
- }
486
- // A pull never undoes what was typed while the request was out.
487
- const unsent = (await db.outbox.where("status").anyOf(UNSENT).toArray())
488
- .filter((c) => pulled.has(`${c.entity}\u0000${c.id}`))
489
- .sort((a, b) => a.seq! - b.seq!);
490
- for (const change of unsent) {
491
- if (change.op === "delete") {
492
- await db.rows.delete([change.entity, change.id]);
493
- continue;
494
- }
495
- const row = (await db.rows.get([change.entity, change.id])) as CopyRow | undefined;
496
- await db.rows.put({
497
- ...row,
498
- entity: change.entity,
499
- id: change.id,
500
- pk: row?.pk ?? null,
501
- data: { ...row?.data, ...change.data },
502
- });
503
- }
504
- if (lastPage) {
505
- await db.meta.put({ key: lastKnowledgeKey(scope), value: answer.server_knowledge });
506
- await db.meta.put({ key: LAST_SYNCED_AT, value: new Date().toISOString() });
507
- }
508
- return {
509
- waiting: hold ? kept.length : 0,
510
- blocked: refused && !hold ? kept.length : 0,
511
- rejected: rejected.length,
512
- };
513
- });
514
- }
515
-
516
- /** A scope the role no longer holds (403): its queue is rejected and it leaves the round.
517
- * The server's `code` and `detail` are kept (a demo's `demo_read_only`); without them,
518
- * `forbidden` and "Not allowed to sync <scope>". */
519
- async function loseScope(scope: string, err: unknown): Promise<number> {
520
- const why = refusalOf(err);
521
- return db.transaction("rw", db.outbox, async () => {
522
- const queue = (await db.outbox.where("scope").equals(scope).toArray()).filter((c) => UNSENT.includes(c.status));
523
- await mark(queue, {
524
- status: "rejected",
525
- code: why.code ?? "forbidden",
526
- reason: why.code && why.detail ? why.detail : `Not allowed to sync ${scope}`,
527
- });
528
- return queue.length;
529
- });
530
- }
531
-
532
- type ScopeResult = { offline: true } | { offline: false; waiting: number; blocked: number; rejected: number };
533
-
534
- async function syncScope(
535
- scope: string,
536
- gen: number,
537
- epoch: string | null,
538
- signal: AbortSignal,
539
- withBlocked: boolean,
540
- ): Promise<ScopeResult> {
541
- const sent = await takeQueue(scope, withBlocked, gen, epoch);
542
- const last = Number((await db.meta.get(lastKnowledgeKey(scope)))?.value ?? 0) || 0;
543
- const body = (changes: SyncWireChange[], page_cursor: SyncRequestBody["page_cursor"]): SyncRequestBody => ({
544
- scope,
545
- last_knowledge: last,
546
- changes,
547
- refusals: true,
548
- client_now: new Date().toISOString(),
549
- ...(options.paged ? { paged: true, page_cursor } : {}),
550
- });
551
- let answer: SyncResponseBody;
552
- try {
553
- answer = await options.send(body(sent.map(toWire), null), signal);
554
- } catch (err) {
555
- await layBack(sent, signal);
556
- if (gen !== generation) throw new Dropped();
557
- if (isOffline(err)) return { offline: true };
558
- const plan = planCode(err);
559
- if (!plan && httpStatusOf(err) !== 403) throw err;
560
- // A refusal is the answer, kept only under the round's epoch as `apply` keeps one. 0.52
561
- // code review (src/sync/engine.ts): a 403 heard after another tab's `forget()` marked
562
- // the kept queue `rejected`, never sent again, for a session that had ended.
563
- return db.transaction("rw", db.outbox, db.meta, async (): Promise<ScopeResult> => {
564
- await standing(gen, epoch);
565
- if (plan) {
566
- // The whole request refused for the plan: every change waits for it.
567
- await mark(sent, { status: "waiting", code: plan, reason: plan });
568
- return { offline: false, waiting: sent.length, blocked: 0, rejected: 0 };
569
- }
570
- return { offline: false, waiting: 0, blocked: 0, rejected: await loseScope(scope, err) };
571
- });
572
- }
573
- let cursor = options.paged ? (answer.page_cursor ?? null) : null;
574
- let tally: ScopeResult;
575
- try {
576
- tally = { offline: false, ...(await apply(scope, answer, sent, gen, epoch, cursor == null)) };
577
- } catch (err) {
578
- if (err instanceof Dropped) await layBack(sent, signal);
579
- throw err;
580
- }
581
- // The rest of a paged pull: pull-only; the cursor is written with the last page alone,
582
- // so an interrupted walk starts again from the old one (D12).
583
- while (cursor) {
584
- let page: SyncResponseBody;
585
- try {
586
- page = await options.send(body([], cursor), signal);
587
- } catch (err) {
588
- if (gen !== generation) throw new Dropped();
589
- if (isOffline(err)) return { offline: true };
590
- throw err;
591
- }
592
- cursor = page.page_cursor ?? null;
593
- await apply(scope, page, [], gen, epoch, cursor == null);
594
- }
595
- return tally;
596
- }
597
-
598
- // ── The round ──────────────────────────────────────────────────────────────
599
-
600
- async function round(withBlocked: boolean, gen: number): Promise<SyncRoundResult> {
601
- const result: SyncRoundResult = { outcome: "synced", waiting: 0, blocked: 0, rejected: 0 };
602
- if (gen !== generation || forgotten) return { ...result, outcome: "dropped" };
603
- // Aborted by stop() and forget(): the wait for the lock, then the round's requests.
604
- const own = new AbortController();
605
- controller = own;
606
- try {
607
- const locks = webLocks();
608
- if (!locks) return await begin(result, withBlocked, gen, own);
609
- let granted = false;
610
- try {
611
- // Queued, first come first served: the rounds of every tab on this database take
612
- // turns, and one asked for meanwhile waits for its turn (`current` and `again` keep
613
- // one per engine). 0.52 code review (src/sync/engine.ts): an `ifAvailable` round
614
- // that found the lock taken answered `paused` at once, so the sign-out dialog's
615
- // "Sync now" closed as if it had synced; its one deferred round lost a
616
- // `retryBlocked()`'s `blocked`; and two tabs' waits refused each other's next try —
617
- // the spec refuses `ifAvailable` while any request is queued. A closed tab's lock
618
- // is released with it.
619
- return await locks.request(across, { signal: own.signal }, () => {
620
- granted = true;
621
- return begin(result, withBlocked, gen, own);
622
- });
623
- } catch (err) {
624
- // `begin` throws only what the app's `paused()` throws. A wait aborted by stop() or
625
- // forget() is dropped there; a lock manager that refuses (an opaque origin, a
626
- // document no longer active) runs the round as before 0.52.
627
- if (granted) return { ...result, outcome: "error", error: err };
628
- return await begin(result, withBlocked, gen, own);
629
- }
630
- } finally {
631
- if (controller === own) controller = null;
632
- }
633
- }
634
-
635
- /** The round begins — under the lock, where there is one, after however long it waited:
636
- * the decision point, and the start of its deadline. */
637
- async function begin(
638
- result: SyncRoundResult,
639
- withBlocked: boolean,
640
- gen: number,
641
- own: AbortController,
642
- ): Promise<SyncRoundResult> {
643
- if (gen !== generation || forgotten) return { ...result, outcome: "dropped" };
644
- if (options.paused?.()) return { ...result, outcome: "paused" };
645
- if (!browserOnline()) return { ...result, outcome: "offline" };
646
- return withDeadline(locked(result, withBlocked, gen, own.signal), result, gen, own);
647
- }
648
-
649
- /** The round, or its cut at `roundTimeoutMs`, whichever comes first. 0.52 code review,
650
- * finding 3 (Marcel: "kit backstop + app timeouts"): a round holds the lock of every tab
651
- * on its database, and one whose request never settles (axios has no default timeout) or
652
- * whose step never does stopped sync in all of them until that tab was closed. The cut
653
- * settles the round by the race, so the lock is released even when the app ignores the
654
- * abort; the generation moves, so what the cut work answers or writes later is dropped. */
655
- function withDeadline(
656
- round: Promise<SyncRoundResult>,
657
- result: SyncRoundResult,
658
- gen: number,
659
- own: AbortController,
660
- ): Promise<SyncRoundResult> {
661
- if (!(roundTimeoutMs > 0 && Number.isFinite(roundTimeoutMs))) return round;
662
- let timer: ReturnType<typeof setTimeout> | undefined;
663
- const deadline = new Promise<SyncRoundResult>((resolve) => {
664
- timer = setTimeout(
665
- () => {
666
- const error = new DOMException(`The sync round ran past ${roundTimeoutMs} ms`, "TimeoutError");
667
- // Settled first: the race ends on the cut, whatever the abort below sets off.
668
- resolve({ ...result, outcome: "error", error });
669
- cut.add(own.signal);
670
- if (gen === generation) generation += 1;
671
- own.abort(error);
672
- },
673
- Math.min(roundTimeoutMs, MAX_TIMEOUT_MS),
674
- );
675
- });
676
- return Promise.race([round, deadline]).finally(() => clearTimeout(timer));
677
- }
678
-
679
- /** The round itself. Never throws. */
680
- async function locked(
681
- result: SyncRoundResult,
682
- withBlocked: boolean,
683
- gen: number,
684
- signal: AbortSignal,
685
- ): Promise<SyncRoundResult> {
686
- try {
687
- await captureEpoch();
688
- const epoch = await readEpoch();
689
- if (gen !== generation) throw new Dropped();
690
- if (!epochs.has(epoch)) throw new ForgottenElsewhere();
691
- set({ phase: "syncing" });
692
- for (let pass = 0; ; pass++) {
693
- for (const scope of options.scopes()) {
694
- if (gen !== generation) throw new Dropped();
695
- const r = await syncScope(scope, gen, epoch, signal, withBlocked);
696
- if (r.offline) return { ...result, outcome: "offline" };
697
- result.waiting += r.waiting;
698
- result.blocked += r.blocked;
699
- result.rejected += r.rejected;
700
- }
701
- if (pass > 0) break;
702
- let changed = false;
703
- for (const step of steps) {
704
- if (gen !== generation) throw new Dropped();
705
- const ran = await step.run(db, { signal, isCurrent: () => gen === generation });
706
- if (gen !== generation) throw new Dropped();
707
- if (ran.counts) stepCounts = { ...stepCounts, [step.name]: ran.counts };
708
- if (ran.offline) return { ...result, outcome: "offline" };
709
- if (ran.changed) changed = true;
710
- }
711
- if (!changed) break;
712
- }
713
- return result;
714
- } catch (err) {
715
- if (err instanceof ForgottenElsewhere) void forgetHere();
716
- if (err instanceof Dropped || gen !== generation) return { ...result, outcome: "dropped" };
717
- return { ...result, outcome: "error", error: err };
718
- }
719
- }
720
-
721
- function clearRetry() {
722
- if (retry) clearTimeout(retry);
723
- retry = null;
724
- }
725
-
726
- function resetBackoff() {
727
- failures = 0;
728
- clearRetry();
729
- if (state.nextRetryAt !== null) set({ nextRetryAt: null });
730
- }
731
-
732
- const inBackoff = () => state.nextRetryAt !== null && Date.now() < state.nextRetryAt;
733
-
734
- function settle(result: SyncRoundResult) {
735
- switch (result.outcome) {
736
- case "synced":
737
- failures = 0;
738
- clearRetry();
739
- set({ phase: "idle", online: true, nextRetryAt: null });
740
- break;
741
- case "offline":
742
- // No retry timer offline: the `online` event or the poll brings the next round (D13).
743
- clearRetry();
744
- set({ phase: "offline", online: false, nextRetryAt: null });
745
- break;
746
- case "error": {
747
- const wait = backoffMs[Math.min(failures, backoffMs.length - 1)];
748
- failures += 1;
749
- clearRetry();
750
- if (started) {
751
- retry = setTimeout(() => {
752
- retry = null;
753
- void run(false);
754
- }, wait);
755
- }
756
- set({ phase: "error", nextRetryAt: started ? Date.now() + wait : null });
757
- break;
758
- }
759
- case "dropped":
760
- set({ phase: "idle" });
761
- break;
762
- case "paused":
763
- break;
764
- }
765
- }
766
-
767
- function run(withBlocked: boolean): Promise<SyncRoundResult> {
768
- if (current) {
769
- again = true;
770
- return current;
771
- }
772
- const before = flights.get(db.name);
773
- // The generation is read before any wait: a stop() while this waits drops the round, so
774
- // it never sends this copy's queue with another account's client nor pulls into it.
775
- const gen = generation;
776
- const flight = (async () => {
777
- // Another engine's round on this database (stopped, still settling) ends first.
778
- if (before) await before.catch(() => undefined);
779
- try {
780
- const result = await round(withBlocked, gen);
781
- settle(result);
782
- await refreshCounts();
783
- return result;
784
- } finally {
785
- current = null;
786
- if (again) {
787
- again = false;
788
- tick();
789
- }
790
- }
791
- })();
792
- current = flight;
793
- flights.set(db.name, flight);
794
- const done = () => {
795
- if (flights.get(db.name) === flight) flights.delete(db.name);
796
- };
797
- void flight.then(done, done);
798
- return flight;
799
- }
800
-
801
- /** A round from the poll, a write or a request during a round: not while backing off. */
802
- function tick() {
803
- if (started && !inBackoff()) void run(false);
804
- }
805
-
806
- /** A round of its own, begun after the one under way — and after one that a request made
807
- * meanwhile started as that one ended — so `retryBlocked()` never joins a round that
808
- * leaves `blocked` out (0.52 code review, src/sync/engine.ts). */
809
- async function runFresh(withBlocked: boolean): Promise<SyncRoundResult> {
810
- resetBackoff();
811
- while (current) {
812
- again = false;
813
- await current.catch(() => undefined);
814
- }
815
- return run(withBlocked);
816
- }
817
-
818
- const onOnline = () => {
819
- set({ online: true });
820
- resetBackoff();
821
- void run(false);
822
- };
823
- const onOffline = () => set({ online: false });
824
-
825
- /** `stop()`, and the end `forgetHere` puts to a started engine. */
826
- function halt() {
827
- if (!started) return;
828
- started = false;
829
- // The next engine's round waits for the dropped one to settle.
830
- drop();
831
- if (running.get(db.name) === engine) running.delete(db.name);
832
- if (poll) clearInterval(poll);
833
- poll = null;
834
- if (soon) clearTimeout(soon);
835
- soon = null;
836
- clearRetry();
837
- if (typeof window !== "undefined") {
838
- window.removeEventListener("online", onOnline);
839
- window.removeEventListener("offline", onOffline);
840
- }
841
- unlisten?.();
842
- unlisten = null;
843
- }
844
-
845
- const engine: SyncEngine = {
846
- async write(scope, entity, id, op, data = {}) {
847
- await db.transaction("rw", db.rows, db.outbox, async () => {
848
- const row = (await db.rows.get([entity, id])) as CopyRow | undefined;
849
- const ofRow = await db.outbox.where("[entity+id]").equals([entity, id]).toArray();
850
- ofRow.sort((a, b) => a.seq! - b.seq!);
851
- const updated_at = stampAfter(
852
- latest([row?.updated_at, row?.data.updated_at, ...ofRow.map((c) => c.updated_at)]),
853
- );
854
- // A new edit to the row moves a rejected change out of the way (D13).
855
- const rejected = ofRow.filter((c) => c.scope === scope && c.status === "rejected");
856
- if (op === "delete") {
857
- await db.rows.delete([entity, id]);
858
- const neverSynced = row != null && (row.local ?? row.pk == null);
859
- if (neverSynced && !ofRow.some((c) => c.status === "sending")) {
860
- // Never reached the server: forgetting its queue is enough — a waiting change
861
- // included, or it would create the row once the plan is back.
862
- await db.outbox.bulkDelete(ofRow.map((c) => c.seq!));
863
- return;
864
- }
865
- await db.outbox.bulkDelete(rejected.map((c) => c.seq!));
866
- await db.outbox.add({ scope, entity, id, op, data: {}, updated_at, status: "pending" });
867
- return;
868
- }
869
- const next: CopyRow = { ...row, entity, id, pk: row?.pk ?? null, data: { ...row?.data, ...data } };
870
- if (!row) next.local = true;
871
- await db.rows.put(next);
872
- const carried = Object.assign({}, ...rejected.filter((c) => c.op === "upsert").map((c) => c.data)) as Record<
873
- string,
874
- unknown
875
- >;
876
- await db.outbox.bulkDelete(rejected.map((c) => c.seq!));
877
- const last = ofRow.filter((c) => c.scope === scope && c.status === "pending").at(-1);
878
- if (last && last.op === "upsert") {
879
- // Several edits before a round go up as one change, with the last one's time.
880
- await db.outbox.update(last.seq!, { data: { ...carried, ...last.data, ...data }, updated_at });
881
- } else {
882
- await db.outbox.add({ scope, entity, id, op, data: { ...carried, ...data }, updated_at, status: "pending" });
883
- }
884
- });
885
- engine.requestSync();
886
- },
887
-
888
- start() {
889
- if (started || forgotten || sharedComputer()) return;
890
- const other = running.get(db.name);
891
- if (other && other !== engine) other.stop();
892
- running.set(db.name, engine);
893
- started = true;
894
- captureEpoch().catch(() => undefined);
895
- listen();
896
- if (typeof window !== "undefined") {
897
- window.addEventListener("online", onOnline);
898
- window.addEventListener("offline", onOffline);
899
- }
900
- poll = setInterval(tick, pollMs);
901
- void refreshCounts();
902
- void run(false);
903
- },
904
-
905
- stop: halt,
906
-
907
- requestSync() {
908
- void refreshCounts();
909
- if (!started) return;
910
- if (soon) clearTimeout(soon);
911
- soon = setTimeout(() => {
912
- soon = null;
913
- tick();
914
- }, soonMs);
915
- },
916
-
917
- syncNow: () => runFresh(false),
918
-
919
- async retryBlocked() {
920
- await runFresh(true);
921
- },
922
-
923
- async discardRejected(scope) {
924
- const gone = (await db.outbox.where("status").equals("rejected").toArray()).filter(
925
- (c) => !scope || c.scope === scope,
926
- );
927
- if (gone.length === 0) return;
928
- await db.transaction("rw", db.rows, db.outbox, db.meta, async () => {
929
- await db.outbox.bulkDelete(gone.map((c) => c.seq!));
930
- for (const change of gone) {
931
- const others = await db.outbox.where("[entity+id]").equals([change.entity, change.id]).count();
932
- if (others === 0) await db.rows.delete([change.entity, change.id]);
933
- }
934
- // The scope pulls afresh, so the copy shows what the server holds.
935
- for (const s of new Set(gone.map((c) => c.scope))) await db.meta.put({ key: lastKnowledgeKey(s), value: 0 });
936
- });
937
- await refreshCounts();
938
- engine.requestSync();
939
- },
940
-
941
- async rejected() {
942
- return db.outbox.where("status").equals("rejected").sortBy("seq");
943
- },
944
-
945
- async forget({ keepQueue }) {
946
- drop();
947
- // Its own forget() starts this engine's next session: it runs again where another
948
- // tab's forget() had ended it.
949
- forgotten = false;
950
- const epoch = freshEpoch();
951
- epochs.add(epoch);
952
- const tables = keepQueue ? [db.rows, db.meta] : db.tables;
953
- forgetting += 1;
954
- try {
955
- await db.transaction("rw", tables, async () => {
956
- for (const table of tables) await table.clear();
957
- await db.meta.put({ key: EPOCH, value: epoch });
958
- });
959
- } finally {
960
- forgetting -= 1;
961
- }
962
- // Best-effort: the epoch in `meta` still stops another tab's next round. This engine's
963
- // own channel hears it too, and keeps running: the epoch is already one of its own.
964
- postOnce(across, { epoch });
965
- // Another tab's forget() met while this clear ran was left to the copy: one committed
966
- // before this clear was undone by it; one committed after it ended this session too.
967
- // (Its message is posted after its commit, so that commit is in the epoch read here.)
968
- const now = await readEpoch().catch(() => epoch);
969
- await (epochs.has(now) ? endSession() : forgetHere());
970
- },
971
-
972
- getState: () => state,
973
-
974
- subscribe(listener) {
975
- listeners.add(listener);
976
- return () => listeners.delete(listener);
977
- },
978
- };
979
- return engine;
980
- }
981
-
982
- /**
983
- * The engine kept for `options.db` (by its name), built on first use: one engine per
984
- * database for the session (D15), so a company switch A→B→A finds A's engine again instead
985
- * of building a second one beside a round in flight. The first call's options stand: read
986
- * changing values (scopes, the client) through functions. A database object opened afresh
987
- * under the same name replaces the kept engine (the old one is stopped).
988
- */
989
- export function syncEngineFor<D extends SyncDb = SyncDb>(options: SyncEngineOptions<D>): SyncEngine {
990
- const name = options.db.name;
991
- const found = kept.get(name);
992
- if (found && found.db === options.db) return found.engine;
993
- found?.engine.stop();
994
- const engine = createSyncEngine(options);
995
- kept.set(name, { db: options.db, engine });
996
- return engine;
997
- }
998
-
999
- /**
1000
- * Let go of the engine {@link syncEngineFor} keeps for the database `name` (0.46): it is
1001
- * stopped, its database closed and forgotten, so a later `syncEngineFor` builds afresh. Call it
1002
- * on sign-out (and for a company left for good): every database visited in a tab otherwise
1003
- * keeps its engine and an open IndexedDB connection until a reload. Nothing kept: a no-op.
1004
- */
1005
- export function releaseSyncEngine(name: string): void {
1006
- const found = kept.get(name);
1007
- if (!found) return;
1008
- kept.delete(name);
1009
- found.engine.stop();
1010
- found.db.close();
1011
- }