cursedbelt 1.0.0 → 2.3.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 (533) hide show
  1. package/dist/client/index.js +1 -1
  2. package/dist/core/category/index.d.ts.map +1 -1
  3. package/dist/core/category/index.js +2 -1
  4. package/dist/core/category/index.js.map +1 -1
  5. package/dist/core/category/types.js +1 -1
  6. package/dist/core/category/types.js.map +1 -1
  7. package/dist/core/file-tree/fileTreeModel.d.ts +49 -0
  8. package/dist/core/file-tree/fileTreeModel.d.ts.map +1 -1
  9. package/dist/core/file-tree/fileTreeModel.js +84 -0
  10. package/dist/core/file-tree/fileTreeModel.js.map +1 -1
  11. package/dist/core/layout/index.js +1 -1
  12. package/dist/core/layout/index.js.map +1 -1
  13. package/dist/core/media/index.js +1 -1
  14. package/dist/core/media/index.js.map +1 -1
  15. package/dist/core/media/types.js +1 -1
  16. package/dist/core/media/types.js.map +1 -1
  17. package/dist/core/media/uploadLimits.js +1 -1
  18. package/dist/core/media/uploadLimits.js.map +1 -1
  19. package/dist/core/sharing/model.d.ts +1 -1
  20. package/dist/ctgr/index.js +1 -1
  21. package/dist/index.js +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/react/activity-bar/ActivityBar.d.ts +17 -0
  24. package/dist/react/activity-bar/ActivityBar.d.ts.map +1 -1
  25. package/dist/react/activity-bar/ActivityBar.js.map +1 -1
  26. package/dist/react/analytics/index.d.ts +1 -1
  27. package/dist/react/analytics/index.js +1 -1
  28. package/dist/react/chat/MessageComposer.d.ts.map +1 -1
  29. package/dist/react/chat/MessageComposer.js +5 -4
  30. package/dist/react/chat/MessageComposer.js.map +1 -1
  31. package/dist/react/components/Button.d.ts.map +1 -1
  32. package/dist/react/components/Button.js +3 -2
  33. package/dist/react/components/Button.js.map +1 -1
  34. package/dist/react/components/Checkbox.d.ts +19 -3
  35. package/dist/react/components/Checkbox.d.ts.map +1 -1
  36. package/dist/react/components/Checkbox.js +29 -12
  37. package/dist/react/components/Checkbox.js.map +1 -1
  38. package/dist/react/components/CodeArea.d.ts.map +1 -1
  39. package/dist/react/components/CodeArea.js +2 -1
  40. package/dist/react/components/CodeArea.js.map +1 -1
  41. package/dist/react/components/Collapsible.d.ts.map +1 -1
  42. package/dist/react/components/Collapsible.js +2 -1
  43. package/dist/react/components/Collapsible.js.map +1 -1
  44. package/dist/react/components/ComboboxField.d.ts.map +1 -1
  45. package/dist/react/components/ComboboxField.js +3 -2
  46. package/dist/react/components/ComboboxField.js.map +1 -1
  47. package/dist/react/components/CopyButton.d.ts.map +1 -1
  48. package/dist/react/components/CopyButton.js +2 -1
  49. package/dist/react/components/CopyButton.js.map +1 -1
  50. package/dist/react/components/DataTable/ColumnChecklist.d.ts.map +1 -1
  51. package/dist/react/components/DataTable/ColumnChecklist.js +2 -1
  52. package/dist/react/components/DataTable/ColumnChecklist.js.map +1 -1
  53. package/dist/react/components/DataTable/Pagination.d.ts.map +1 -1
  54. package/dist/react/components/DataTable/Pagination.js +2 -1
  55. package/dist/react/components/DataTable/Pagination.js.map +1 -1
  56. package/dist/react/components/DataTable/SortHeader.d.ts.map +1 -1
  57. package/dist/react/components/DataTable/SortHeader.js +2 -1
  58. package/dist/react/components/DataTable/SortHeader.js.map +1 -1
  59. package/dist/react/components/DataTable/TableFilterBuilder.d.ts +61 -0
  60. package/dist/react/components/DataTable/TableFilterBuilder.d.ts.map +1 -0
  61. package/dist/react/components/DataTable/{FilterBuilder.js → TableFilterBuilder.js} +26 -3
  62. package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -0
  63. package/dist/react/components/DataTable/Toolbar.d.ts +1 -1
  64. package/dist/react/components/DataTable/Toolbar.js +3 -3
  65. package/dist/react/components/DataTable/Toolbar.js.map +1 -1
  66. package/dist/react/components/DataTable/cells/LinkCell.d.ts.map +1 -1
  67. package/dist/react/components/DataTable/cells/LinkCell.js +2 -1
  68. package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
  69. package/dist/react/components/DataTable/index.d.ts +1 -1
  70. package/dist/react/components/DataTable/index.d.ts.map +1 -1
  71. package/dist/react/components/DataTable/index.js +4 -1
  72. package/dist/react/components/DataTable/index.js.map +1 -1
  73. package/dist/react/components/DragHandle.d.ts.map +1 -1
  74. package/dist/react/components/DragHandle.js +2 -1
  75. package/dist/react/components/DragHandle.js.map +1 -1
  76. package/dist/react/components/EmojiField.d.ts.map +1 -1
  77. package/dist/react/components/EmojiField.js +2 -1
  78. package/dist/react/components/EmojiField.js.map +1 -1
  79. package/dist/react/components/EventTimeline.d.ts +3 -2
  80. package/dist/react/components/EventTimeline.d.ts.map +1 -1
  81. package/dist/react/components/EventTimeline.js +3 -2
  82. package/dist/react/components/EventTimeline.js.map +1 -1
  83. package/dist/react/components/IconButton.d.ts +1 -1
  84. package/dist/react/components/IconButton.js +1 -1
  85. package/dist/react/components/InfoHint.d.ts.map +1 -1
  86. package/dist/react/components/InfoHint.js +5 -4
  87. package/dist/react/components/InfoHint.js.map +1 -1
  88. package/dist/react/components/InheritBadge.js +1 -1
  89. package/dist/react/components/InheritBadge.js.map +1 -1
  90. package/dist/react/components/InlineEditableText.d.ts.map +1 -1
  91. package/dist/react/components/InlineEditableText.js +2 -1
  92. package/dist/react/components/InlineEditableText.js.map +1 -1
  93. package/dist/react/components/MeterList.d.ts.map +1 -1
  94. package/dist/react/components/MeterList.js +2 -1
  95. package/dist/react/components/MeterList.js.map +1 -1
  96. package/dist/react/components/PathText.d.ts +2 -2
  97. package/dist/react/components/PathText.d.ts.map +1 -1
  98. package/dist/react/components/PathText.js +2 -1
  99. package/dist/react/components/PathText.js.map +1 -1
  100. package/dist/react/components/RatingField.d.ts.map +1 -1
  101. package/dist/react/components/RatingField.js +2 -1
  102. package/dist/react/components/RatingField.js.map +1 -1
  103. package/dist/react/components/SecretInput.js +3 -2
  104. package/dist/react/components/SecretInput.js.map +1 -1
  105. package/dist/react/components/SegmentedControl.d.ts.map +1 -1
  106. package/dist/react/components/SegmentedControl.js +2 -1
  107. package/dist/react/components/SegmentedControl.js.map +1 -1
  108. package/dist/react/components/StarRating.d.ts.map +1 -1
  109. package/dist/react/components/StarRating.js +9 -5
  110. package/dist/react/components/StarRating.js.map +1 -1
  111. package/dist/react/components/StatStrip.d.ts.map +1 -1
  112. package/dist/react/components/StatStrip.js +2 -1
  113. package/dist/react/components/StatStrip.js.map +1 -1
  114. package/dist/react/components/StatTile.d.ts.map +1 -1
  115. package/dist/react/components/StatTile.js +3 -2
  116. package/dist/react/components/StatTile.js.map +1 -1
  117. package/dist/react/components/Tabs.d.ts.map +1 -1
  118. package/dist/react/components/Tabs.js +2 -1
  119. package/dist/react/components/Tabs.js.map +1 -1
  120. package/dist/react/components/TruncatedText.d.ts +2 -1
  121. package/dist/react/components/TruncatedText.d.ts.map +1 -1
  122. package/dist/react/components/TruncatedText.js +2 -1
  123. package/dist/react/components/TruncatedText.js.map +1 -1
  124. package/dist/react/components/calendar/MonthGrid.d.ts.map +1 -1
  125. package/dist/react/components/calendar/MonthGrid.js +2 -1
  126. package/dist/react/components/calendar/MonthGrid.js.map +1 -1
  127. package/dist/react/components/calendar/WeekView.d.ts.map +1 -1
  128. package/dist/react/components/calendar/WeekView.js +2 -1
  129. package/dist/react/components/calendar/WeekView.js.map +1 -1
  130. package/dist/react/components/revealOnHover.d.ts.map +1 -1
  131. package/dist/react/components/revealOnHover.js +2 -1
  132. package/dist/react/components/revealOnHover.js.map +1 -1
  133. package/dist/react/file-tree/FileTree.d.ts +50 -1
  134. package/dist/react/file-tree/FileTree.d.ts.map +1 -1
  135. package/dist/react/file-tree/FileTree.js +234 -6
  136. package/dist/react/file-tree/FileTree.js.map +1 -1
  137. package/dist/react/file-tree/fileTree.css +52 -0
  138. package/dist/react/filter-rail/FilterRail.d.ts.map +1 -1
  139. package/dist/react/filter-rail/FilterRail.js +5 -4
  140. package/dist/react/filter-rail/FilterRail.js.map +1 -1
  141. package/dist/react/folder-tree/FolderTreeRail.d.ts.map +1 -1
  142. package/dist/react/folder-tree/FolderTreeRail.js +2 -1
  143. package/dist/react/folder-tree/FolderTreeRail.js.map +1 -1
  144. package/dist/react/index.d.ts +5 -1
  145. package/dist/react/index.d.ts.map +1 -1
  146. package/dist/react/index.js +5 -1
  147. package/dist/react/index.js.map +1 -1
  148. package/dist/react/layoutEngine/index.js +1 -1
  149. package/dist/react/layoutEngine/index.js.map +1 -1
  150. package/dist/react/lib/field.d.ts.map +1 -1
  151. package/dist/react/lib/field.js +3 -2
  152. package/dist/react/lib/field.js.map +1 -1
  153. package/dist/react/lib/floatingStyles.d.ts.map +1 -1
  154. package/dist/react/lib/floatingStyles.js +2 -1
  155. package/dist/react/lib/floatingStyles.js.map +1 -1
  156. package/dist/react/lib/tapTarget.d.ts +96 -0
  157. package/dist/react/lib/tapTarget.d.ts.map +1 -0
  158. package/dist/react/lib/tapTarget.js +96 -0
  159. package/dist/react/lib/tapTarget.js.map +1 -0
  160. package/dist/react/media/VideoTrimmer.d.ts +1 -1
  161. package/dist/react/media/VideoTrimmer.js +1 -1
  162. package/dist/react/media/denseGallery.css +3 -3
  163. package/dist/react/media/ffmpegWasm.js +2 -2
  164. package/dist/react/media/ffmpegWasm.js.map +1 -1
  165. package/dist/react/media/videoPlayer.css +42 -0
  166. package/dist/react/namespace/NamespaceTargetCapsule.d.ts +2 -2
  167. package/dist/react/namespace/NamespaceTargetCapsule.d.ts.map +1 -1
  168. package/dist/react/nav/BreadcrumbTrail.d.ts.map +1 -1
  169. package/dist/react/nav/BreadcrumbTrail.js +4 -3
  170. package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
  171. package/dist/react/nav/HubTileGrid.d.ts.map +1 -1
  172. package/dist/react/nav/HubTileGrid.js +2 -1
  173. package/dist/react/nav/HubTileGrid.js.map +1 -1
  174. package/dist/react/nav/SideNav.d.ts.map +1 -1
  175. package/dist/react/nav/SideNav.js +4 -3
  176. package/dist/react/nav/SideNav.js.map +1 -1
  177. package/dist/react/nav/SideNavCustomize.d.ts.map +1 -1
  178. package/dist/react/nav/SideNavCustomize.js +2 -1
  179. package/dist/react/nav/SideNavCustomize.js.map +1 -1
  180. package/dist/react/palette/CommandPalette.d.ts.map +1 -1
  181. package/dist/react/palette/CommandPalette.js +3 -2
  182. package/dist/react/palette/CommandPalette.js.map +1 -1
  183. package/dist/server/drivers/pooledControlStores.pg.test.js +1 -0
  184. package/dist/server/drivers/pooledControlStores.pg.test.js.map +1 -1
  185. package/dist/server/drivers/pooledIdentityStore.pg.test.js +1 -0
  186. package/dist/server/drivers/pooledIdentityStore.pg.test.js.map +1 -1
  187. package/dist/server/drivers/retentionPolicy.pg.test.js +1 -0
  188. package/dist/server/drivers/retentionPolicy.pg.test.js.map +1 -1
  189. package/dist/server/guard/argon2Binding.d.ts +1 -1
  190. package/dist/server/guard/argon2Binding.js +1 -1
  191. package/dist/server/incidents/store.d.ts +3 -3
  192. package/dist/server/index.js +1 -1
  193. package/dist/server/index.js.map +1 -1
  194. package/dist/server/master-lock/masterLock.d.ts +1 -1
  195. package/dist/server/media-bun/index.js +1 -1
  196. package/dist/server/media-bun/index.js.map +1 -1
  197. package/dist/server/paths/appDataHome.d.ts +20 -0
  198. package/dist/server/paths/appDataHome.d.ts.map +1 -0
  199. package/dist/server/paths/appDataHome.js +69 -0
  200. package/dist/server/paths/appDataHome.js.map +1 -0
  201. package/dist/server/retention/archive.d.ts +3 -4
  202. package/dist/server/retention/archive.d.ts.map +1 -1
  203. package/dist/server/retention/archive.js +20 -15
  204. package/dist/server/retention/archive.js.map +1 -1
  205. package/dist/server/retention/index.d.ts +1 -1
  206. package/dist/server/retention/index.js +1 -1
  207. package/dist/server/retention/policy.d.ts +2 -2
  208. package/dist/server/retention/policy.d.ts.map +1 -1
  209. package/dist/server/retention/policy.js +1 -1
  210. package/dist/server/retention/store.d.ts.map +1 -1
  211. package/dist/server/retention/store.js +27 -20
  212. package/dist/server/retention/store.js.map +1 -1
  213. package/dist/server/runtime/localHost.d.ts +10 -5
  214. package/dist/server/runtime/localHost.d.ts.map +1 -1
  215. package/dist/server/runtime/localHost.js +12 -8
  216. package/dist/server/runtime/localHost.js.map +1 -1
  217. package/dist/server/runtime/meterRollup.pg.test.js +1 -0
  218. package/dist/server/runtime/meterRollup.pg.test.js.map +1 -1
  219. package/dist/server/runtime/recipe.d.ts +4 -4
  220. package/dist/server/runtime/retentionJob.pg.test.js +1 -0
  221. package/dist/server/runtime/retentionJob.pg.test.js.map +1 -1
  222. package/dist/server/search/pgSearchProvider.pg.test.js +1 -0
  223. package/dist/server/search/pgSearchProvider.pg.test.js.map +1 -1
  224. package/dist/shared/errors.d.ts +1 -1
  225. package/dist/shared/errors.js +1 -1
  226. package/dist/styles-static.css +391 -0
  227. package/dist/styles.css +27 -0
  228. package/package.json +19 -167
  229. package/scripts/generateStaticStyles.ts +122 -0
  230. package/scripts/publicSurface.ts +455 -0
  231. package/scripts/testTmpRoot.ts +34 -5
  232. package/src/client/index.ts +1 -1
  233. package/src/core/category/index.ts +2 -1
  234. package/src/core/category/types.ts +1 -1
  235. package/src/core/file-tree/fileTreeModel.spec.ts +134 -0
  236. package/src/core/file-tree/fileTreeModel.ts +104 -0
  237. package/src/core/layout/index.ts +1 -1
  238. package/src/core/media/index.ts +1 -1
  239. package/src/core/media/types.ts +1 -1
  240. package/src/core/media/uploadLimits.ts +1 -1
  241. package/src/core/sharing/model.ts +1 -1
  242. package/src/ctgr/index.ts +1 -1
  243. package/src/declaredDepsAreImported.spec.ts +87 -0
  244. package/src/demoFixture.spec.ts +133 -0
  245. package/src/index.ts +1 -1
  246. package/src/lockfileIsTracked.spec.ts +50 -0
  247. package/src/namedSubpathsResolve.spec.ts +119 -0
  248. package/src/publicSurface.spec.ts +72 -0
  249. package/src/react/activity-bar/ActivityBar.tsx +17 -0
  250. package/src/react/analytics/index.ts +1 -1
  251. package/src/react/chat/MessageComposer.tsx +5 -4
  252. package/src/react/components/Button.tsx +3 -2
  253. package/src/react/components/Checkbox.spec.tsx +51 -0
  254. package/src/react/components/Checkbox.tsx +68 -26
  255. package/src/react/components/CodeArea.tsx +2 -1
  256. package/src/react/components/CodeChip.spec.tsx +2 -2
  257. package/src/react/components/Collapsible.tsx +2 -1
  258. package/src/react/components/ComboboxField.tsx +3 -2
  259. package/src/react/components/CopyButton.tsx +2 -1
  260. package/src/react/components/CopyableText.spec.tsx +1 -1
  261. package/src/react/components/DataTable/ColumnChecklist.tsx +2 -1
  262. package/src/react/components/DataTable/Pagination.tsx +2 -1
  263. package/src/react/components/DataTable/SortHeader.tsx +2 -1
  264. package/src/react/components/DataTable/{FilterBuilder.tsx → TableFilterBuilder.tsx} +33 -5
  265. package/src/react/components/DataTable/Toolbar.tsx +3 -3
  266. package/src/react/components/DataTable/cells/LinkCell.tsx +2 -1
  267. package/src/react/components/DataTable/index.ts +8 -1
  268. package/src/react/components/DragHandle.tsx +2 -1
  269. package/src/react/components/EmojiField.tsx +2 -1
  270. package/src/react/components/EventTimeline.tsx +3 -2
  271. package/src/react/components/FileLocation.spec.tsx +1 -1
  272. package/src/react/components/IconButton.tsx +1 -1
  273. package/src/react/components/InfoHint.tsx +5 -4
  274. package/src/react/components/InheritBadge.tsx +1 -1
  275. package/src/react/components/InlineEditableText.tsx +2 -1
  276. package/src/react/components/MeterList.tsx +2 -1
  277. package/src/react/components/PathText.tsx +4 -3
  278. package/src/react/components/RatingField.tsx +2 -1
  279. package/src/react/components/SecretInput.tsx +3 -3
  280. package/src/react/components/SegmentedControl.tsx +2 -2
  281. package/src/react/components/StarRating.spec.tsx +2 -2
  282. package/src/react/components/StarRating.tsx +9 -5
  283. package/src/react/components/StatStrip.tsx +2 -1
  284. package/src/react/components/StatTile.tsx +3 -3
  285. package/src/react/components/Tabs.spec.tsx +3 -2
  286. package/src/react/components/Tabs.tsx +2 -1
  287. package/src/react/components/TruncatedText.tsx +2 -1
  288. package/src/react/components/calendar/MonthGrid.spec.tsx +2 -1
  289. package/src/react/components/calendar/MonthGrid.tsx +2 -1
  290. package/src/react/components/calendar/WeekView.tsx +2 -1
  291. package/src/react/components/revealOnHover.ts +3 -2
  292. package/src/react/file-tree/FileTree.spec.tsx +248 -0
  293. package/src/react/file-tree/FileTree.tsx +304 -4
  294. package/src/react/file-tree/fileTree.css +52 -0
  295. package/src/react/file-tree/fileTreeGeometry.fixture.json +151 -0
  296. package/src/react/file-tree/fileTreeGeometry.spec.ts +33 -52
  297. package/src/react/filter-rail/FilterRail.spec.tsx +2 -1
  298. package/src/react/filter-rail/FilterRail.tsx +5 -4
  299. package/src/react/folder-tree/FolderTreeRail.tsx +2 -1
  300. package/src/react/index.ts +13 -3
  301. package/src/react/layoutEngine/index.ts +1 -1
  302. package/src/react/layoutEngine/types.ts +1 -1
  303. package/src/react/lib/field.ts +4 -2
  304. package/src/react/lib/floatingStyles.spec.ts +2 -1
  305. package/src/react/lib/floatingStyles.ts +3 -1
  306. package/src/react/lib/tapTarget.ts +100 -0
  307. package/src/react/media/MaskPainter.tsx +2 -2
  308. package/src/react/media/VideoTrimmer.tsx +1 -1
  309. package/src/react/media/denseGallery.css +3 -3
  310. package/src/react/media/ffmpegWasm.ts +2 -2
  311. package/src/react/media/videoPlayer.css +42 -0
  312. package/src/react/namespace/NamespaceTargetCapsule.tsx +2 -2
  313. package/src/react/nav/BreadcrumbTrail.tsx +4 -3
  314. package/src/react/nav/HubTileGrid.tsx +2 -1
  315. package/src/react/nav/SideNav.tsx +4 -3
  316. package/src/react/nav/SideNavCustomize.tsx +2 -1
  317. package/src/react/palette/CommandPalette.tsx +3 -2
  318. package/src/server/ctgr.ts +1 -1
  319. package/src/server/drivers/pooledControlStores.pg.test.ts +1 -0
  320. package/src/server/drivers/pooledIdentityStore.pg.test.ts +1 -0
  321. package/src/server/drivers/retentionPolicy.pg.test.ts +1 -0
  322. package/src/server/guard/argon2Binding.ts +1 -1
  323. package/src/server/incidents/store.ts +3 -3
  324. package/src/server/index.ts +1 -1
  325. package/src/server/master-lock/masterLock.spec.ts +18 -2
  326. package/src/server/master-lock/masterLock.ts +1 -1
  327. package/src/server/media-bun/image.spec.ts +1 -0
  328. package/src/server/media-bun/index.ts +1 -1
  329. package/src/server/media-bun/video.spec.ts +1 -0
  330. package/src/server/paths/appDataHome.spec.ts +87 -0
  331. package/src/server/paths/appDataHome.ts +84 -0
  332. package/src/server/retention/archive.spec.ts +26 -4
  333. package/src/server/retention/archive.ts +20 -15
  334. package/src/server/retention/index.ts +1 -1
  335. package/src/server/retention/policy.ts +2 -2
  336. package/src/server/retention/store.spec.ts +38 -10
  337. package/src/server/retention/store.ts +26 -19
  338. package/src/server/runtime/localHost.spec.ts +22 -3
  339. package/src/server/runtime/localHost.ts +12 -9
  340. package/src/server/runtime/meterRollup.pg.test.ts +1 -0
  341. package/src/server/runtime/recipe.ts +4 -4
  342. package/src/server/runtime/retentionJob.pg.test.ts +1 -0
  343. package/src/server/search/pgSearchProvider.pg.test.ts +1 -0
  344. package/src/server/storage/binaryServerProcessing.integration.spec.ts +6 -2
  345. package/src/server/storage/chunked.integration.spec.ts +5 -2
  346. package/src/server/storage/fileUploadClient-chunked.integration.spec.ts +5 -2
  347. package/src/server/storage/files/adapter.photo.display.spec.ts +1 -0
  348. package/src/server/storage/imageThumbnail.spec.ts +1 -0
  349. package/src/server/storage/transcode.integration.spec.ts +5 -2
  350. package/src/server/storage/videoProcessor.posterOnly.spec.ts +1 -0
  351. package/src/shared/errors.ts +1 -1
  352. package/src/skippedTestsAreAnswered.spec.ts +215 -0
  353. package/src/styles-static.css +391 -0
  354. package/src/styles.css +27 -0
  355. package/src/stylesStaticMatches.spec.ts +134 -0
  356. package/src/tapTargetStylesheet.spec.ts +150 -0
  357. package/src/testTmpRoot.spec.ts +102 -0
  358. package/dist/core/duplicates/index.d.ts +0 -116
  359. package/dist/core/duplicates/index.d.ts.map +0 -1
  360. package/dist/core/duplicates/index.js +0 -178
  361. package/dist/core/duplicates/index.js.map +0 -1
  362. package/dist/core/namespace/helpers.d.ts +0 -14
  363. package/dist/core/namespace/helpers.d.ts.map +0 -1
  364. package/dist/core/namespace/helpers.js +0 -29
  365. package/dist/core/namespace/helpers.js.map +0 -1
  366. package/dist/core/namespace/index.d.ts +0 -4
  367. package/dist/core/namespace/index.d.ts.map +0 -1
  368. package/dist/core/namespace/index.js +0 -6
  369. package/dist/core/namespace/index.js.map +0 -1
  370. package/dist/core/namespace/memoryStore.d.ts +0 -19
  371. package/dist/core/namespace/memoryStore.d.ts.map +0 -1
  372. package/dist/core/namespace/memoryStore.js +0 -70
  373. package/dist/core/namespace/memoryStore.js.map +0 -1
  374. package/dist/core/namespace/types.d.ts +0 -44
  375. package/dist/core/namespace/types.d.ts.map +0 -1
  376. package/dist/core/namespace/types.js +0 -8
  377. package/dist/core/namespace/types.js.map +0 -1
  378. package/dist/game/core/DisposeRegistry.d.ts +0 -42
  379. package/dist/game/core/DisposeRegistry.d.ts.map +0 -1
  380. package/dist/game/core/DisposeRegistry.js +0 -67
  381. package/dist/game/core/DisposeRegistry.js.map +0 -1
  382. package/dist/game/core/drawBudget.d.ts +0 -26
  383. package/dist/game/core/drawBudget.d.ts.map +0 -1
  384. package/dist/game/core/drawBudget.js +0 -55
  385. package/dist/game/core/drawBudget.js.map +0 -1
  386. package/dist/game/core/fixedTimestep.d.ts +0 -38
  387. package/dist/game/core/fixedTimestep.d.ts.map +0 -1
  388. package/dist/game/core/fixedTimestep.js +0 -47
  389. package/dist/game/core/fixedTimestep.js.map +0 -1
  390. package/dist/game/core/index.d.ts +0 -9
  391. package/dist/game/core/index.d.ts.map +0 -1
  392. package/dist/game/core/index.js +0 -8
  393. package/dist/game/core/index.js.map +0 -1
  394. package/dist/game/core/inputCollector.d.ts +0 -82
  395. package/dist/game/core/inputCollector.d.ts.map +0 -1
  396. package/dist/game/core/inputCollector.js +0 -176
  397. package/dist/game/core/inputCollector.js.map +0 -1
  398. package/dist/game/core/resizeCanvas.d.ts +0 -19
  399. package/dist/game/core/resizeCanvas.d.ts.map +0 -1
  400. package/dist/game/core/resizeCanvas.js +0 -29
  401. package/dist/game/core/resizeCanvas.js.map +0 -1
  402. package/dist/game/react/index.d.ts +0 -3
  403. package/dist/game/react/index.d.ts.map +0 -1
  404. package/dist/game/react/index.js +0 -5
  405. package/dist/game/react/index.js.map +0 -1
  406. package/dist/game/react/useGameEngine.d.ts +0 -64
  407. package/dist/game/react/useGameEngine.d.ts.map +0 -1
  408. package/dist/game/react/useGameEngine.js +0 -78
  409. package/dist/game/react/useGameEngine.js.map +0 -1
  410. package/dist/react/canvas/CanvasNavHUD.d.ts +0 -64
  411. package/dist/react/canvas/CanvasNavHUD.d.ts.map +0 -1
  412. package/dist/react/canvas/CanvasNavHUD.js +0 -58
  413. package/dist/react/canvas/CanvasNavHUD.js.map +0 -1
  414. package/dist/react/canvas/fitLabel.d.ts +0 -53
  415. package/dist/react/canvas/fitLabel.d.ts.map +0 -1
  416. package/dist/react/canvas/fitLabel.js +0 -104
  417. package/dist/react/canvas/fitLabel.js.map +0 -1
  418. package/dist/react/canvas/index.d.ts +0 -4
  419. package/dist/react/canvas/index.d.ts.map +0 -1
  420. package/dist/react/canvas/index.js +0 -4
  421. package/dist/react/canvas/index.js.map +0 -1
  422. package/dist/react/canvas/lod.d.ts +0 -40
  423. package/dist/react/canvas/lod.d.ts.map +0 -1
  424. package/dist/react/canvas/lod.js +0 -37
  425. package/dist/react/canvas/lod.js.map +0 -1
  426. package/dist/react/components/DataTable/FilterBuilder.d.ts +0 -38
  427. package/dist/react/components/DataTable/FilterBuilder.d.ts.map +0 -1
  428. package/dist/react/components/DataTable/FilterBuilder.js.map +0 -1
  429. package/dist/react/seo/HeadProvider.d.ts +0 -14
  430. package/dist/react/seo/HeadProvider.d.ts.map +0 -1
  431. package/dist/react/seo/HeadProvider.js +0 -44
  432. package/dist/react/seo/HeadProvider.js.map +0 -1
  433. package/dist/react/seo/applyHeadToDocument.d.ts +0 -3
  434. package/dist/react/seo/applyHeadToDocument.d.ts.map +0 -1
  435. package/dist/react/seo/applyHeadToDocument.js +0 -66
  436. package/dist/react/seo/applyHeadToDocument.js.map +0 -1
  437. package/dist/react/seo/index.d.ts +0 -3
  438. package/dist/react/seo/index.d.ts.map +0 -1
  439. package/dist/react/seo/index.js +0 -7
  440. package/dist/react/seo/index.js.map +0 -1
  441. package/dist/react/timeline/ClipView.d.ts +0 -29
  442. package/dist/react/timeline/ClipView.d.ts.map +0 -1
  443. package/dist/react/timeline/ClipView.js +0 -53
  444. package/dist/react/timeline/ClipView.js.map +0 -1
  445. package/dist/react/timeline/Playhead.d.ts +0 -16
  446. package/dist/react/timeline/Playhead.d.ts.map +0 -1
  447. package/dist/react/timeline/Playhead.js +0 -26
  448. package/dist/react/timeline/Playhead.js.map +0 -1
  449. package/dist/react/timeline/TimeRuler.d.ts +0 -21
  450. package/dist/react/timeline/TimeRuler.d.ts.map +0 -1
  451. package/dist/react/timeline/TimeRuler.js +0 -31
  452. package/dist/react/timeline/TimeRuler.js.map +0 -1
  453. package/dist/react/timeline/TimelineSurface.d.ts +0 -29
  454. package/dist/react/timeline/TimelineSurface.d.ts.map +0 -1
  455. package/dist/react/timeline/TimelineSurface.js +0 -56
  456. package/dist/react/timeline/TimelineSurface.js.map +0 -1
  457. package/dist/react/timeline/TrackLane.d.ts +0 -31
  458. package/dist/react/timeline/TrackLane.d.ts.map +0 -1
  459. package/dist/react/timeline/TrackLane.js +0 -32
  460. package/dist/react/timeline/TrackLane.js.map +0 -1
  461. package/dist/react/timeline/index.d.ts +0 -6
  462. package/dist/react/timeline/index.d.ts.map +0 -1
  463. package/dist/react/timeline/index.js +0 -12
  464. package/dist/react/timeline/index.js.map +0 -1
  465. package/dist/server/private-media/index.d.ts +0 -7
  466. package/dist/server/private-media/index.d.ts.map +0 -1
  467. package/dist/server/private-media/index.js +0 -13
  468. package/dist/server/private-media/index.js.map +0 -1
  469. package/dist/server/seo/index.d.ts +0 -3
  470. package/dist/server/seo/index.d.ts.map +0 -1
  471. package/dist/server/seo/index.js +0 -6
  472. package/dist/server/seo/index.js.map +0 -1
  473. package/dist/server/seo/mountSeoRoutes.d.ts +0 -9
  474. package/dist/server/seo/mountSeoRoutes.d.ts.map +0 -1
  475. package/dist/server/seo/mountSeoRoutes.js +0 -19
  476. package/dist/server/seo/mountSeoRoutes.js.map +0 -1
  477. package/dist/server/seo/serveHeadInjectedIndex.d.ts +0 -9
  478. package/dist/server/seo/serveHeadInjectedIndex.d.ts.map +0 -1
  479. package/dist/server/seo/serveHeadInjectedIndex.js +0 -20
  480. package/dist/server/seo/serveHeadInjectedIndex.js.map +0 -1
  481. package/dist/upload.d.ts +0 -148
  482. package/dist/upload.d.ts.map +0 -1
  483. package/dist/upload.js +0 -159
  484. package/dist/upload.js.map +0 -1
  485. package/dist/verify/runtime/index.d.ts +0 -16
  486. package/dist/verify/runtime/index.d.ts.map +0 -1
  487. package/dist/verify/runtime/index.js +0 -16
  488. package/dist/verify/runtime/index.js.map +0 -1
  489. package/scripts/demoCheck.ts +0 -42
  490. package/src/core/duplicates/index.spec.ts +0 -116
  491. package/src/core/duplicates/index.ts +0 -203
  492. package/src/core/namespace/helpers.ts +0 -31
  493. package/src/core/namespace/index.ts +0 -12
  494. package/src/core/namespace/memoryStore.ts +0 -77
  495. package/src/core/namespace/namespace.spec.ts +0 -117
  496. package/src/core/namespace/types.ts +0 -53
  497. package/src/game/core/DisposeRegistry.spec.ts +0 -53
  498. package/src/game/core/DisposeRegistry.ts +0 -70
  499. package/src/game/core/drawBudget.spec.ts +0 -51
  500. package/src/game/core/drawBudget.ts +0 -75
  501. package/src/game/core/fixedTimestep.spec.ts +0 -91
  502. package/src/game/core/fixedTimestep.ts +0 -79
  503. package/src/game/core/index.ts +0 -16
  504. package/src/game/core/inputCollector.spec.ts +0 -29
  505. package/src/game/core/inputCollector.ts +0 -245
  506. package/src/game/core/resizeCanvas.ts +0 -31
  507. package/src/game/react/index.ts +0 -10
  508. package/src/game/react/useGameEngine.ts +0 -125
  509. package/src/react/canvas/CanvasNavHUD.spec.tsx +0 -62
  510. package/src/react/canvas/CanvasNavHUD.tsx +0 -210
  511. package/src/react/canvas/fitLabel.spec.ts +0 -82
  512. package/src/react/canvas/fitLabel.ts +0 -150
  513. package/src/react/canvas/index.ts +0 -15
  514. package/src/react/canvas/lod.ts +0 -53
  515. package/src/react/seo/HeadProvider.tsx +0 -67
  516. package/src/react/seo/applyHeadToDocument.ts +0 -73
  517. package/src/react/seo/index.ts +0 -12
  518. package/src/react/seo/seo.spec.tsx +0 -101
  519. package/src/react/timeline/ClipView.tsx +0 -146
  520. package/src/react/timeline/Playhead.tsx +0 -63
  521. package/src/react/timeline/TimeRuler.tsx +0 -85
  522. package/src/react/timeline/TimelineSurface.tsx +0 -157
  523. package/src/react/timeline/TrackLane.tsx +0 -87
  524. package/src/react/timeline/index.ts +0 -12
  525. package/src/react/timeline/timeline.spec.tsx +0 -175
  526. package/src/server/private-media/index.ts +0 -45
  527. package/src/server/seo/index.ts +0 -8
  528. package/src/server/seo/mountSeoRoutes.ts +0 -37
  529. package/src/server/seo/seo.spec.ts +0 -128
  530. package/src/server/seo/serveHeadInjectedIndex.ts +0 -36
  531. package/src/upload.spec.ts +0 -300
  532. package/src/upload.ts +0 -288
  533. package/src/verify/runtime/index.ts +0 -64
@@ -29,6 +29,7 @@ import { useDragReorder } from '../hooks/useDragReorder';
29
29
  import { NavItemMenu } from './NavItemMenu';
30
30
  import { type IconResolver, renderIcon } from './navShared';
31
31
  import { Tooltip } from '../components/Tooltip';
32
+ import { TAP_TARGET } from '../lib/tapTarget';
32
33
 
33
34
  export interface ReorderableRowsProps {
34
35
  domains: NavDomain[];
@@ -98,7 +99,7 @@ export function RowCustomizeControls({
98
99
  // pointer affordance only and must NOT be a Tab stop, so keyboard users tab
99
100
  // cleanly through the nav links (a11y gotcha #NavItemMenu).
100
101
  tabIndex={-1}
101
- className={`inline-flex size-6 cursor-grab items-center justify-center rounded text-muted-foreground hover:text-foreground ${'pointer-coarse:min-h-11 pointer-coarse:min-w-11'}`}
102
+ className={`inline-flex size-6 cursor-grab items-center justify-center rounded text-muted-foreground hover:text-foreground ${TAP_TARGET}`}
102
103
  >
103
104
  <GripVertical className='size-4' />
104
105
  </button>
@@ -42,6 +42,7 @@ import {
42
42
  CommandList,
43
43
  } from './Command';
44
44
  import { useShortcutRegistry } from './useShortcutRegistry';
45
+ import { TAP_TARGET_HEIGHT } from '../lib/tapTarget';
45
46
 
46
47
  export interface CommandPaletteProps {
47
48
  /** The single navigation registry (task 13) — the palette's page source of truth. */
@@ -540,7 +541,7 @@ function PinsQuickBar({
540
541
  data-cb-pin-add=''
541
542
  tooltip='Pin this page'
542
543
  leftIcon={<Plus className='size-3.5' aria-hidden='true' />}
543
- className='h-auto shrink-0 rounded-full border border-dashed border-border px-2.5 py-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground pointer-coarse:min-h-11'
544
+ className={`h-auto shrink-0 rounded-full border border-dashed border-border px-2.5 py-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground ${TAP_TARGET_HEIGHT}`}
544
545
  >
545
546
  Pin this page
546
547
  </Button>
@@ -616,7 +617,7 @@ function PinChip({
616
617
  }
617
618
  : undefined
618
619
  }
619
- className='h-auto shrink-0 rounded-full border-border bg-surface px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted pointer-coarse:min-h-11'
620
+ className={`h-auto shrink-0 rounded-full border-border bg-surface px-2.5 py-1 text-xs font-medium text-foreground hover:bg-muted ${TAP_TARGET_HEIGHT}`}
620
621
  >
621
622
  {icon != null ? (
622
623
  <span
@@ -2,7 +2,7 @@
2
2
  * ctgr Hono transport — the server half of the GET-tunnel: the `unGETify` middleware
3
3
  * that decodes a `/ctgr<XX>` twin's query params back into the original request, and
4
4
  * the `buildRoutes` registrar that wires the real verb route and its GET twin from one
5
- * call. The codec is `cursedbelt/ctgr`; the browser side is `cursedbelt/client`.
5
+ * call. The codec re-exports from `cursedbelt/server`; the browser side is `cursedbelt/client`.
6
6
  *
7
7
  * Protection is unchanged and lives elsewhere by design: the getOnly-claim 403 keys
8
8
  * off the RAW request method (always GET on a twin) in the auth middleware, and the
@@ -31,6 +31,7 @@ const URL_ = process.env.CC_PG_URL;
31
31
  let sql: SQL;
32
32
  let pool: SaasPool;
33
33
 
34
+ // SKIPPED unless CC_PG_URL names a throwaway Postgres — this suite's subject IS the engine. `bun run test:pg` supplies one.
34
35
  describe.skipIf(!URL_)('the shared control-plane stores against a real Postgres (04 §1)', () => {
35
36
  // 120s, not Bun's default 5s. This hook drops and re-creates a role, a database
36
37
  // and a whole migration chain against a REAL Postgres; on a shared 4-core CI
@@ -44,6 +44,7 @@ let store: RegistryIdentityStore;
44
44
 
45
45
  const NOW = '2026-07-26T00:00:00.000Z';
46
46
 
47
+ // SKIPPED unless CC_PG_URL names a throwaway Postgres — this suite's subject IS the engine. `bun run test:pg` supplies one.
47
48
  describe.skipIf(!URL_)('the saas identity tier against a real Postgres (04 §1a)', () => {
48
49
  // 120s, not Bun's default 5s. This hook drops and re-creates a role, a database
49
50
  // and a whole migration chain against a REAL Postgres; on a shared 4-core CI
@@ -35,6 +35,7 @@ const URL_ = process.env.CC_PG_URL;
35
35
  let sql: SQL;
36
36
  let executor: StatementExecutor;
37
37
 
38
+ // SKIPPED unless CC_PG_URL names a throwaway Postgres — this suite's subject IS the engine. `bun run test:pg` supplies one.
38
39
  describe.skipIf(!URL_)('the retention policy table against a real Postgres (D30)', () => {
39
40
  beforeAll(async () => {
40
41
  sql = new SQL(URL_ as string);
@@ -167,7 +167,7 @@ export const registerArgon2idBinding = (binding: RawArgon2idBinding): void => {
167
167
  * module exists to prevent.
168
168
  *
169
169
  * The same indirection that hides the specifier from a bundler hides it from
170
- * every static import walker, so `cursedbelt/guard`'s peer map would report
170
+ * every static import walker, so the guard's peer map would report
171
171
  * this edge as absent. The pragma is what puts it back.
172
172
  *
173
173
  * @peer @node-rs/argon2
@@ -21,9 +21,9 @@
21
21
  * (`checks.ts`) does not call `reconcile` at all for a check that threw.
22
22
  *
23
23
  * ── Where the file lives ────────────────────────────────────────────────────
24
- * Whatever `Database` the caller opens. The console keeps it under
25
- * `~/.app-managed/cursed-satellites/incidents/`, deliberately NOT in the metrics
26
- * database: metrics are capped, regenerable telemetry that trims on write, and
24
+ * Whatever `Database` the caller opens. The console keeps it in an incidents
25
+ * database of its own under the host's app-data root, deliberately NOT in the
26
+ * metrics database: metrics are capped, regenerable telemetry that trims on write, and
27
27
  * an incident is a governed record whose whole value is that it survives — most
28
28
  * of all across the restart a deploy of the console itself performs.
29
29
  */
@@ -117,7 +117,7 @@ export {
117
117
  } from './jobs';
118
118
  // ── Media adapter (task 19): image rendering via sharp (lazy/optional peer) + ──
119
119
  // video trim / probe / thumbnail via native ffmpeg. Also at the dedicated
120
- // `cursedbelt/server/media` entry; sharp is imported lazily inside renderImage,
120
+ // media-bun entry below; sharp is imported lazily inside renderImage,
121
121
  // so importing this barrel never loads the native binary.
122
122
  export {
123
123
  buildProbeArgs,
@@ -209,6 +209,22 @@ describe("the idle clock", () => {
209
209
  });
210
210
  });
211
211
 
212
+ /**
213
+ * 🔴 The two tests below declare their own budget, and it is not padding.
214
+ *
215
+ * Every `unlock()` runs a real argon2id verification — that IS the thing under test — and
216
+ * these two are the only ones in the file that do it in bulk: 9 attempts and 17 attempts.
217
+ * Measured 2026-09-13 on this machine: 4.5s and 7.2s against bun's DEFAULT per-test budget
218
+ * of 5000ms, so the second failed on every run and the first was one hiccup behind it. The
219
+ * file's header says it "leans on the repo's per-test budget"; this repo's `bunfig.toml`
220
+ * declares none, so that budget is 5s and the claim was simply false.
221
+ *
222
+ * Stated per test rather than raised in `bunfig.toml`, deliberately: a repo-wide 20s would
223
+ * buy these two nothing the line below does not, and would cost all 5,200 other tests the
224
+ * fast failure that tells you something is HUNG rather than slow.
225
+ */
226
+ const KDF_BULK_MS = 20_000;
227
+
212
228
  describe("the guess throttle", () => {
213
229
  test("five failures are free, the sixth costs a wait, and a success clears it", async () => {
214
230
  let now = 1_000_000;
@@ -230,7 +246,7 @@ describe("the guess throttle", () => {
230
246
  now += 30_001;
231
247
  expect((await lock.unlock(VERIFIER)).ok).toBe(true);
232
248
  expect(lock.status().retryAfterMs).toBe(0);
233
- });
249
+ }, KDF_BULK_MS);
234
250
 
235
251
  test("a windowed refusal does not advance the counter", async () => {
236
252
  let now = 1_000_000;
@@ -242,7 +258,7 @@ describe("the guess throttle", () => {
242
258
  // cost nothing.
243
259
  const next = await lock.unlock("wrong");
244
260
  expect(next.ok === false && next.retryAfterMs).toBe(60_000);
245
- });
261
+ }, KDF_BULK_MS);
246
262
  });
247
263
 
248
264
  describe("rotation", () => {
@@ -83,7 +83,7 @@ export interface MasterLockOptions {
83
83
  * The BOOTSTRAP record, as JSON, used only when the store is empty — the same shape as
84
84
  * {@link MasterLockRecord} with `idleMs` optional.
85
85
  *
86
- * It arrives 0600 in `~/.app-managed/deploy/secrets/<app>.env` as `MASTER_LOCK_SEED`,
86
+ * It arrives as `MASTER_LOCK_SEED` in the app's own 0600 deploy-secrets env file,
87
87
  * copied from the vault's own `vault_keys` row, so the lock is armed from the first
88
88
  * request rather than from whenever somebody remembers to set one. A lock that has to
89
89
  * be configured before it protects anything protects nothing on the day it matters —
@@ -19,6 +19,7 @@ async function jpeg(w: number, h: number, color = { r: 200, g: 50, b: 50 }): Pro
19
19
  .toBuffer();
20
20
  }
21
21
 
22
+ // SKIPPED when the OPTIONAL `sharp` peer is not installed — it is this suite's subject. Runs by default when it is.
22
23
  describe.skipIf(!sharp)('renderImage (sharp)', () => {
23
24
  test('resize + format-convert → webp with the right dimensions (acceptance #8)', async () => {
24
25
  const src = await jpeg(400, 300);
@@ -1,4 +1,4 @@
1
- // cursedbelt/server/media (media-bun) — the Bun server adapter for the media-core
1
+ // src/server/media-bun — the Bun server adapter, re-exported from `cursedbelt/server`, for the media-core
2
2
  // specs: image rendering via sharp (lazy/optional), video trim + probe + thumbnail
3
3
  // via native ffmpeg/ffprobe. App handlers keep auth, FsNode lookup, quota, and
4
4
  // storeFile; this owns the actual pixel/codec work.
@@ -52,6 +52,7 @@ afterAll(() => {
52
52
  if (dir) rmSync(dir, { recursive: true, force: true });
53
53
  });
54
54
 
55
+ // SKIPPED on a host without ffmpeg/ffprobe — it synthesizes and decodes real media. Runs by default wherever ffmpeg is installed.
55
56
  describe.skipIf(!haveFfmpeg)('media-bun video (ffmpeg)', () => {
56
57
  test('probeMedia reads dimensions + duration', async () => {
57
58
  const meta = await probeMedia(source);
@@ -0,0 +1,87 @@
1
+ import { describe, expect, it } from "bun:test";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { APP_DATA_HOME_ENV, resolveAppDataHome, XDG_DATA_HOME_ENV } from "./appDataHome";
5
+
6
+ /**
7
+ * The published-library defect this file is the gate for: a resolver that knows
8
+ * the name of a directory in one person's home. Every assertion below is either
9
+ * "the caller decides" or "the OS decides"; there is no third source.
10
+ */
11
+ describe("the app-data home", () => {
12
+ it("takes an explicit argument over everything else", () => {
13
+ expect(
14
+ resolveAppDataHome({
15
+ override: "/srv/data",
16
+ env: { [APP_DATA_HOME_ENV]: "/env/data", [XDG_DATA_HOME_ENV]: "/xdg", HOME: "/home/x" },
17
+ }),
18
+ ).toBe("/srv/data");
19
+ });
20
+
21
+ it("reads APP_DATA_HOME — the one env var — before any convention", () => {
22
+ expect(
23
+ resolveAppDataHome({
24
+ env: { [APP_DATA_HOME_ENV]: "/env/data", [XDG_DATA_HOME_ENV]: "/xdg", HOME: "/home/x" },
25
+ }),
26
+ ).toBe("/env/data");
27
+ });
28
+
29
+ it("honors XDG_DATA_HOME on every platform, macOS included", () => {
30
+ for (const platform of ["darwin", "linux"]) {
31
+ expect(resolveAppDataHome({ env: { [XDG_DATA_HOME_ENV]: "/xdg", HOME: "/home/x" }, platform })).toBe("/xdg");
32
+ }
33
+ });
34
+
35
+ it("falls back to the macOS convention, not to anybody's dotfolder", () => {
36
+ const resolved = resolveAppDataHome({ env: { HOME: "/home/x" }, platform: "darwin" });
37
+ expect(resolved).toBe(join("/home/x", "Library", "Application Support"));
38
+ });
39
+
40
+ it("falls back to the XDG default path elsewhere", () => {
41
+ expect(resolveAppDataHome({ env: { HOME: "/home/x" }, platform: "linux" })).toBe(
42
+ join("/home/x", ".local", "share"),
43
+ );
44
+ });
45
+
46
+ // Whitespace is how an env var gets "set" by a shell script that meant to unset
47
+ // it; treating it as configuration points every app at the filesystem root.
48
+ it("treats a blank setting as no setting", () => {
49
+ expect(
50
+ resolveAppDataHome({
51
+ override: " ",
52
+ env: { [APP_DATA_HOME_ENV]: " ", [XDG_DATA_HOME_ENV]: "", HOME: "/home/x" },
53
+ platform: "linux",
54
+ }),
55
+ ).toBe(join("/home/x", ".local", "share"));
56
+ });
57
+
58
+ it("falls back to the process's own home when the env names none", () => {
59
+ expect(resolveAppDataHome({ env: {}, platform: "linux" })).toBe(join(homedir(), ".local", "share"));
60
+ });
61
+
62
+ /**
63
+ * 🔴 The assertion this module exists for. It is deliberately a property of the
64
+ * OUTPUT rather than of the source text, so it still holds if somebody
65
+ * reintroduces the old folder through a variable, a `join`, or a constant.
66
+ */
67
+ it("never resolves a previous generation's private directory", () => {
68
+ const envs: Array<Record<string, string | undefined>> = [
69
+ {},
70
+ { HOME: "/home/x" },
71
+ { HOME: "/home/x", [APP_DATA_HOME_ENV]: "/srv/data" },
72
+ { HOME: "/home/x", [XDG_DATA_HOME_ENV]: "/xdg" },
73
+ // The previous generation's own env var must no longer steer anything: the
74
+ // value below is deliberately unlike any convention, so a resolver that
75
+ // still read this var could not produce a passing answer by accident.
76
+ { HOME: "/home/x", APP_MANAGED_HOME: "/nowhere/inherited" },
77
+ ];
78
+ for (const env of envs) {
79
+ for (const platform of ["darwin", "linux"]) {
80
+ const resolved = resolveAppDataHome({ env, platform });
81
+ expect(resolved).not.toContain("app-managed");
82
+ expect(resolved).not.toContain("code-retiring");
83
+ expect(resolved).not.toContain("Master-Docs");
84
+ }
85
+ }
86
+ });
87
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Where a machine keeps an app's data — as CONFIGURATION, never as a convention
3
+ * baked into the library.
4
+ *
5
+ * ## What this replaces, and why it is a defect rather than untidiness
6
+ *
7
+ * Until 2026-09-13 three live modules in this package joined the ambient home
8
+ * directory to a hardcoded dotfolder belonging to the previous generation: the
9
+ * retention store, the archive, and `resolveAppDataRoot`. This package is
10
+ * PUBLISHED. Anybody who installed it got a library that went looking for a
11
+ * directory in their home that has nothing to do with them, and — worse — wrote
12
+ * into it if it happened to exist.
13
+ *
14
+ * A library may not know the name of a directory in somebody's home. It may know
15
+ * two things: what the caller told it, and what the operating system says the
16
+ * convention is. That is the whole ladder below.
17
+ *
18
+ * ## The ladder
19
+ *
20
+ * 1. an explicit `override` — a `--data-root` flag, a host's own resolution;
21
+ * 2. `$APP_DATA_HOME` — the ONE env var, for a machine that keeps every app's
22
+ * data under one root so a fleet-wide tool can see all of it at once;
23
+ * 3. `$XDG_DATA_HOME` — the user already told the OS where data goes;
24
+ * 4. the platform default: `~/Library/Application Support` on macOS,
25
+ * `~/.local/share` everywhere else.
26
+ *
27
+ * `$XDG_DATA_HOME` is honored on macOS too. It is not the platform's own
28
+ * convention there, but a user who sets it has stated a preference, and a
29
+ * preference outranks a default on any platform.
30
+ *
31
+ * Nothing here touches the filesystem: it is a pure mapping from environment to
32
+ * path, so a caller can reason about the answer before creating anything, and a
33
+ * test can assert both platforms on one machine.
34
+ */
35
+ import { homedir } from "node:os";
36
+ import { join } from "node:path";
37
+
38
+ /** The one env var. A machine with a shared data root sets this and nothing else. */
39
+ export const APP_DATA_HOME_ENV = "APP_DATA_HOME";
40
+
41
+ /** The cross-desktop convention this honors ahead of the platform default. */
42
+ export const XDG_DATA_HOME_ENV = "XDG_DATA_HOME";
43
+
44
+ export interface ResolveAppDataHomeOptions {
45
+ /** An explicit answer from the caller. Wins outright — this is the "argument" half. */
46
+ readonly override?: string;
47
+ /** Environment to read. Injected by tests; defaults to the process's own. */
48
+ readonly env?: Record<string, string | undefined>;
49
+ /**
50
+ * Platform to resolve for. Injected by tests so both branches are proved on one
51
+ * machine; defaults to the running platform.
52
+ */
53
+ readonly platform?: string;
54
+ }
55
+
56
+ /** Trimmed, or undefined — an env var set to whitespace is not a configuration. */
57
+ function configured(value: string | undefined): string | undefined {
58
+ const trimmed = value?.trim();
59
+ return trimmed ? trimmed : undefined;
60
+ }
61
+
62
+ /**
63
+ * The machine's app-data home. See the module note for the ordering rationale.
64
+ */
65
+ export function resolveAppDataHome(options: ResolveAppDataHomeOptions = {}): string {
66
+ const env = options.env ?? process.env;
67
+ const override = configured(options.override);
68
+ if (override) return override;
69
+
70
+ const appDataHome = configured(env[APP_DATA_HOME_ENV]);
71
+ if (appDataHome) return appDataHome;
72
+
73
+ const xdg = configured(env[XDG_DATA_HOME_ENV]);
74
+ if (xdg) return xdg;
75
+
76
+ // `env.HOME` rather than `homedir()` alone, so a caller passing an env object
77
+ // gets a resolution it can actually control — every test below depends on it,
78
+ // and so does any host that boots an app under a different account.
79
+ const home = configured(env.HOME) ?? homedir();
80
+ const platform = options.platform ?? process.platform;
81
+ return platform === "darwin"
82
+ ? join(home, "Library", "Application Support")
83
+ : join(home, ".local", "share");
84
+ }
@@ -9,6 +9,7 @@ import { afterEach, describe, expect, test } from "bun:test";
9
9
  import { chmodSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
10
10
  import { tmpdir } from "node:os";
11
11
  import { join } from "node:path";
12
+ import { resolveAppDataHome } from "../paths/appDataHome";
12
13
  import {
13
14
  archiveStamp,
14
15
  createJsonlArchive,
@@ -133,18 +134,39 @@ describe("pendingBytes — the staleness signal a deployed box can actually obse
133
134
  });
134
135
 
135
136
  describe("resolveArchiveHome", () => {
136
- test("the Mac's shared home is TERMINAL and is the path the owner's plan names", () => {
137
+ test("the shared OS-convention home is TERMINAL — the archive simply grows there", () => {
137
138
  const root = tmp();
138
- mkdirSync(join(root, ".app-managed"), { recursive: true });
139
+ const conventional = resolveAppDataHome({ env: { HOME: root } });
140
+ mkdirSync(conventional, { recursive: true });
139
141
  const resolved = resolveArchiveHome("notes", { HOME: root });
140
142
  expect(resolved).toEqual({
141
- dir: join(root, ".app-managed", "cursed-satellites", "archive", "notes"),
143
+ dir: join(conventional, "cursed-satellites", "archive", "notes"),
142
144
  terminal: true,
143
145
  });
144
146
  });
145
147
 
148
+ test("APP_DATA_HOME is honored before the convention, and before it exists", () => {
149
+ const configured = join(tmp(), "managed");
150
+ expect(resolveArchiveHome("notes", { APP_DATA_HOME: configured })).toEqual({
151
+ dir: join(configured, "cursed-satellites", "archive", "notes"),
152
+ terminal: true,
153
+ });
154
+ });
155
+
156
+ /**
157
+ * 🔴 Up to 2026-09-13 this resolved a hardcoded dotfolder in the owner's home —
158
+ * in a PUBLISHED package. Asserted on the output, so it still holds if the
159
+ * folder comes back through a variable.
160
+ */
161
+ test("resolves nothing from a previous generation, whatever the env says", () => {
162
+ const root = tmp();
163
+ const resolved = resolveArchiveHome("notes", { HOME: root, APP_MANAGED_HOME: "/nowhere/inherited" });
164
+ expect(resolved.dir).not.toContain("app-managed");
165
+ expect(resolved.dir).not.toContain("/nowhere/inherited");
166
+ });
167
+
146
168
  test("🔴 a deployed box falls back to the data dir and is STAGED — something must collect it", () => {
147
- const root = tmp(); // no ~/.app-managed, which is every deployed box
169
+ const root = tmp(); // no shared data home, which is every deployed box
148
170
  const dataDir = join(tmp(), "data");
149
171
  mkdirSync(dataDir, { recursive: true });
150
172
  expect(resolveArchiveHome("notes", { HOME: root, APP_DATA_DIR: dataDir })).toEqual({
@@ -28,10 +28,13 @@
28
28
  * prefix and then delete the rest. A sealed file cannot be half a file.
29
29
  *
30
30
  * ── Terminal vs staged, and the staleness refusal ───────────────────────────
31
- * On this Mac the archive is home: `~/.app-managed/cursed-satellites/archive/
32
- * <app>/`, it is `terminal`, and it simply grows. On a deployed box there is no
33
- * `~/.app-managed`, so it resolves to `$APP_DATA_DIR/archive` and is `staged` —
34
- * something else has to come and get it. That distinction is what lets the
31
+ * On a workstation the archive is home: `<app-data-home>/cursed-satellites/
32
+ * archive/<app>/`, it is `terminal`, and it simply grows. The home is
33
+ * CONFIGURATION ({@link resolveAppDataHome}) — `$APP_DATA_HOME`, else the OS's own
34
+ * convention — never a folder this library invented in somebody's home; that was
35
+ * the published defect fixed 2026-09-13. On a deployed box nothing outside the
36
+ * app's own data dir is writable, so it resolves to `$APP_DATA_DIR/archive` and
37
+ * is `staged` — something else has to come and get it. That distinction is what lets the
35
38
  * sweeper honor "refuse to trim if the last successful pull is stale" using a
36
39
  * fact the box can actually observe: how many bytes of sealed segments are still
37
40
  * sitting there uncollected. If the collector stops, the archive fills to its
@@ -54,8 +57,8 @@ import {
54
57
  writeFileSync,
55
58
  writeSync,
56
59
  } from "node:fs";
57
- import { homedir } from "node:os";
58
60
  import { join } from "node:path";
61
+ import { APP_DATA_HOME_ENV, resolveAppDataHome } from "../paths/appDataHome";
59
62
 
60
63
  /** One sealed archive file. */
61
64
  export interface ArchiveSegment {
@@ -127,10 +130,9 @@ function isUsable(dir: string): boolean {
127
130
  * Where this app's archive lives on THIS machine, and whether the machine keeps
128
131
  * it.
129
132
  *
130
- * The path is the one the owner's plan names —
131
- * `~/.app-managed/cursed-satellites/archive/<app>/` — reached through the same
132
- * env ladder the retention home uses, so a test, a container and a deployed box
133
- * each land somewhere writable rather than somewhere assumed.
133
+ * `<app-data-home>/cursed-satellites/archive/<app>/`, reached through the same env
134
+ * ladder the retention home uses, so a test, a container and a deployed box each
135
+ * land somewhere writable rather than somewhere assumed.
134
136
  */
135
137
  export function resolveArchiveHome(
136
138
  app: string,
@@ -141,16 +143,19 @@ export function resolveArchiveHome(
141
143
  if (env.RETENTION_ARCHIVE_HOME) {
142
144
  candidates.push({ dir: join(env.RETENTION_ARCHIVE_HOME, app), terminal: true });
143
145
  }
144
- if (env.APP_MANAGED_HOME) {
146
+ // An explicitly CONFIGURED home is used whether or not it exists yet — the
147
+ // operator naming it is the statement that it is the right place.
148
+ const configured = env[APP_DATA_HOME_ENV]?.trim();
149
+ if (configured) {
145
150
  candidates.push({
146
- dir: join(env.APP_MANAGED_HOME, "cursed-satellites", "archive", app),
151
+ dir: join(configured, "cursed-satellites", "archive", app),
147
152
  terminal: true,
148
153
  });
149
154
  }
150
- const conventional = join(env.HOME ?? homedir(), ".app-managed");
151
- // Only prefer the Mac's shared home when it already exists: creating it on a
152
- // deployed box would strand the archive where no collector looks.
153
- if (existsSync(conventional)) {
155
+ const conventional = resolveAppDataHome({ env });
156
+ // The OS convention is only PREFERRED when it is already there: creating it on
157
+ // a deployed box would strand the archive where no collector looks.
158
+ if (!configured && existsSync(conventional)) {
154
159
  candidates.push({
155
160
  dir: join(conventional, "cursed-satellites", "archive", app),
156
161
  terminal: true,
@@ -4,7 +4,7 @@
4
4
  * ── Why this lives in cursedbelt ────────────────────────────────────────────
5
5
  * Retention used to be spoken in three unrelated vocabularies: this
6
6
  * category-based opt-in design (born in `cursed-satellites/satellite-kit`), the
7
- * DB-backed audit|meter WINDOWS in `cursedbelt/server/drivers/retentionPolicy`
7
+ * DB-backed audit|meter WINDOWS in `cursedbelt/server/drivers`' retentionPolicy
8
8
  * (billing evidence — a genuinely different concern that keeps the name
9
9
  * {@link RetentionWindows}), and `deploy-engine/backupRetention`'s artifact
10
10
  * pruning. DEDUP-8(c) made THIS one canonical and moved it here, so a single
@@ -8,7 +8,7 @@
8
8
  * `ai_check_runs` keeps N per check, the proposal ledger caps at 200,
9
9
  * orch's own `eventRetention` was a whole bespoke sweeper with its own timer, and `deploy-engine/backupRetention.ts` a second one with different
10
10
  * rules. Meanwhile the things nobody had written a rule for grew without limit:
11
- * `~/.app-managed/orch` reached **1.0 GB** — 74 MB of attempt transcripts and a
11
+ * orch's own state directory reached **1.0 GB** — 74 MB of attempt transcripts and a
12
12
  * single 70 MB peer-link `dedupe.json` holding 118 entries — with no policy, no
13
13
  * backup, and nothing that could report it.
14
14
  *
@@ -178,7 +178,7 @@ export type RetentionUnit = "rows" | "files";
178
178
  * why `retentionPolicies.test.ts` pins the ids too.
179
179
  */
180
180
  export interface RetentionDeclaration {
181
- /** App key, matching `Master-Docs/fleet/registry/apps-tracking.csv` (`orch`, `notes`, …). */
181
+ /** App key, matching the fleet registry's app-tracking rows (`orch`, `notes`, …). */
182
182
  app: string;
183
183
  /** Stable, app-scoped id. The override key is `<app>:<id>`. */
184
184
  id: string;
@@ -2,6 +2,7 @@ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
2
  import { mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
3
  import { tmpdir } from "node:os";
4
4
  import { join } from "node:path";
5
+ import { resolveAppDataHome } from "../paths/appDataHome";
5
6
  import type { RetentionDeclaration } from "./policy";
6
7
  import {
7
8
  publishRetention,
@@ -169,23 +170,32 @@ describe("resolveRetentionHome", () => {
169
170
  expect(resolveRetentionHome({ RETENTION_HOME: explicit })).toBe(explicit);
170
171
  });
171
172
 
172
- test("APP_MANAGED_HOME is honored before any conventional path", () => {
173
+ test("APP_DATA_HOME — the one env var — is honored before any convention", () => {
173
174
  setRetentionHomeForTests(null);
174
- expect(resolveRetentionHome({ APP_MANAGED_HOME: home })).toBe(join(home, "retention"));
175
+ expect(resolveRetentionHome({ APP_DATA_HOME: home })).toBe(join(home, "retention"));
175
176
  });
176
177
 
177
- test("the shared Mac home wins over APP_DATA_DIR so the station sees ONE fleet", () => {
178
- // orch's APP_DATA_DIR is ~/.app-managed/orch; publishing there instead of
179
- // the shared home would hide it from every other app's view of the fleet.
178
+ test("a configured home is used even before it exists — naming it is the statement", () => {
180
179
  setRetentionHomeForTests(null);
181
- mkdirSync(join(home, ".app-managed"), { recursive: true });
180
+ const fresh = join(home, "not-yet");
181
+ expect(resolveRetentionHome({ APP_DATA_HOME: fresh, APP_DATA_DIR: join(home, "data") })).toBe(
182
+ join(fresh, "retention"),
183
+ );
184
+ });
185
+
186
+ test("the shared OS-convention home wins over APP_DATA_DIR so the station sees ONE fleet", () => {
187
+ // An app's own APP_DATA_DIR is private to it; publishing there instead of the
188
+ // shared home would hide it from every other app's view of the fleet.
189
+ setRetentionHomeForTests(null);
190
+ const conventional = resolveAppDataHome({ env: { HOME: home } });
191
+ mkdirSync(conventional, { recursive: true });
182
192
  expect(resolveRetentionHome({ HOME: home, APP_DATA_DIR: join(home, "data") })).toBe(
183
- join(home, ".app-managed", "retention"),
193
+ join(conventional, "retention"),
184
194
  );
185
195
  });
186
196
 
187
- test("a deployed box — no app-managed home — falls through to APP_DATA_DIR", () => {
188
- // prod-ec2 has no ~/.app-managed, and under ProtectSystem=strict only the
197
+ test("a deployed box — no shared data home — falls through to APP_DATA_DIR", () => {
198
+ // prod-ec2 has no shared data home, and under ProtectSystem=strict only the
189
199
  // data dir is writable. The app still sweeps; its fleet is that box's.
190
200
  setRetentionHomeForTests(null);
191
201
  const dataDir = join(home, "data");
@@ -199,10 +209,28 @@ describe("resolveRetentionHome", () => {
199
209
  const dataDir = join(home, "data2");
200
210
  expect(
201
211
  resolveRetentionHome({
202
- APP_MANAGED_HOME: "/proc/definitely-not-writable",
212
+ APP_DATA_HOME: "/proc/definitely-not-writable",
203
213
  HOME: join(home, "nonexistent-home"),
204
214
  APP_DATA_DIR: dataDir,
205
215
  }),
206
216
  ).toBe(join(dataDir, "retention"));
207
217
  });
218
+
219
+ /**
220
+ * 🔴 The published defect, asserted on the OUTPUT rather than on the source: up
221
+ * to 2026-09-13 this resolved a hardcoded dotfolder in the owner's home, so
222
+ * every installer of `cursedbelt` got a library reaching into a directory that
223
+ * had nothing to do with them.
224
+ */
225
+ test("resolves nothing from a previous generation, whatever the env says", () => {
226
+ for (const env of [
227
+ { HOME: join(home, "nonexistent-home") },
228
+ { HOME: join(home, "nonexistent-home"), APP_MANAGED_HOME: "/nowhere/inherited" },
229
+ ]) {
230
+ setRetentionHomeForTests(null);
231
+ const resolved = resolveRetentionHome(env);
232
+ expect(resolved).not.toContain("app-managed");
233
+ expect(resolved).not.toContain("/nowhere/inherited");
234
+ }
235
+ });
208
236
  });