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
@@ -1,77 +0,0 @@
1
- import type { Namespace, NamespaceCreateInput, NamespaceStore, NamespaceUpdateInput } from './types';
2
- import { nextHue, normalizeNamespaceLabel } from './helpers';
3
-
4
- /**
5
- * Zero-config in-memory {@link NamespaceStore}: a single per-boot `Map`, so a
6
- * host that wires nothing up still gets a working (non-persistent) namespace
7
- * list — the same role `createMemoryCategoryStore` plays. Swap in a
8
- * persistent store (f's `createSqliteNamespaceStore`) for real data.
9
- *
10
- * Deliberately not annotated `: NamespaceStore` — the interface allows async
11
- * returns, which would force callers (and these tests) to `await` values that
12
- * are always synchronous here. Structural typing keeps it assignable wherever
13
- * a `NamespaceStore` is expected.
14
- */
15
- export function createMemoryNamespaceStore(
16
- idFactory: () => string = () => crypto.randomUUID(),
17
- now: () => number = () => Date.now(),
18
- ) {
19
- const byId = new Map<string, Namespace>();
20
-
21
- const requireNamespace = (id: string): Namespace => {
22
- const ns = byId.get(id);
23
- if (!ns) throw new Error(`no namespace with id ${id}`);
24
- return ns;
25
- };
26
-
27
- const nextOrd = (): number => {
28
- let max = -1;
29
- for (const ns of byId.values()) {
30
- if (ns.ord > max) max = ns.ord;
31
- }
32
- return max + 1;
33
- };
34
-
35
- return {
36
- list(): Namespace[] {
37
- return [...byId.values()].sort((a, b) => {
38
- if (a.archived !== b.archived) return a.archived ? 1 : -1;
39
- return a.ord - b.ord;
40
- });
41
- },
42
-
43
- create(input: NamespaceCreateInput): Namespace {
44
- const existing = [...byId.values()];
45
- const timestamp = now();
46
- const namespace: Namespace = {
47
- id: idFactory(),
48
- label: normalizeNamespaceLabel(input.label),
49
- hue: input.hue ?? nextHue(existing),
50
- ord: nextOrd(),
51
- archived: false,
52
- createdAt: timestamp,
53
- updatedAt: timestamp,
54
- };
55
- byId.set(namespace.id, namespace);
56
- return namespace;
57
- },
58
-
59
- update(id: string, input: NamespaceUpdateInput): Namespace {
60
- const ns = requireNamespace(id);
61
- const updated: Namespace = {
62
- ...ns,
63
- label: input.label !== undefined ? normalizeNamespaceLabel(input.label) : ns.label,
64
- hue: input.hue ?? ns.hue,
65
- ord: input.ord ?? ns.ord,
66
- archived: input.archived ?? ns.archived,
67
- updatedAt: now(),
68
- };
69
- byId.set(id, updated);
70
- return updated;
71
- },
72
-
73
- remove(id: string): void {
74
- byId.delete(id);
75
- },
76
- };
77
- }
@@ -1,117 +0,0 @@
1
- import { describe, expect, it } from 'bun:test';
2
- import { createMemoryNamespaceStore, nextHue, normalizeNamespaceLabel, type Namespace } from './index';
3
-
4
- const ns = (id: string, ord: number, archived = false, hue = 0): Namespace => ({
5
- id,
6
- label: id,
7
- hue,
8
- ord,
9
- archived,
10
- createdAt: 0,
11
- updatedAt: 0,
12
- });
13
-
14
- describe('createMemoryNamespaceStore', () => {
15
- it('lists in ord order', () => {
16
- let n = 0;
17
- const store = createMemoryNamespaceStore(() => `id${++n}`);
18
- const b = store.create({ label: 'B' });
19
- const a = store.create({ label: 'A' });
20
- store.update(a.id, { ord: 0 });
21
- store.update(b.id, { ord: 1 });
22
- expect(store.list().map((x) => x.id)).toEqual([a.id, b.id]);
23
- });
24
-
25
- it('sorts archived namespaces after non-archived ones regardless of ord', () => {
26
- let n = 0;
27
- const store = createMemoryNamespaceStore(() => `id${++n}`);
28
- const a = store.create({ label: 'A' });
29
- const b = store.create({ label: 'B' });
30
- store.update(a.id, { archived: true, ord: 0 });
31
- store.update(b.id, { ord: 5 });
32
- expect(store.list().map((x) => x.id)).toEqual([b.id, a.id]);
33
- });
34
-
35
- it('creates with auto-incrementing ord and an assigned hue', () => {
36
- const store = createMemoryNamespaceStore();
37
- const a = store.create({ label: 'A' });
38
- const b = store.create({ label: 'B' });
39
- expect(a.ord).toBe(0);
40
- expect(b.ord).toBe(1);
41
- expect(a.hue).toBeGreaterThanOrEqual(0);
42
- expect(a.hue).toBeLessThan(360);
43
- });
44
-
45
- it('normalizes labels on create and update', () => {
46
- const store = createMemoryNamespaceStore();
47
- const a = store.create({ label: ' Multi Word ' });
48
- expect(a.label).toBe('Multi Word');
49
- const updated = store.update(a.id, { label: ' Renamed ' });
50
- expect(updated.label).toBe('Renamed');
51
- });
52
-
53
- it('rejects a blank label', () => {
54
- const store = createMemoryNamespaceStore();
55
- expect(() => store.create({ label: ' ' })).toThrow();
56
- });
57
-
58
- it('updates only the provided fields', () => {
59
- const store = createMemoryNamespaceStore();
60
- const a = store.create({ label: 'A', hue: 10 });
61
- const updated = store.update(a.id, { archived: true });
62
- expect(updated.label).toBe('A');
63
- expect(updated.hue).toBe(10);
64
- expect(updated.archived).toBe(true);
65
- });
66
-
67
- it('removes a namespace', () => {
68
- const store = createMemoryNamespaceStore();
69
- const a = store.create({ label: 'A' });
70
- store.remove(a.id);
71
- expect(store.list()).toEqual([]);
72
- });
73
-
74
- it('throws when updating an unknown id', () => {
75
- const store = createMemoryNamespaceStore();
76
- expect(() => store.update('ghost', { label: 'X' })).toThrow();
77
- });
78
- });
79
-
80
- describe('nextHue', () => {
81
- it('spreads hues across the wheel and avoids exact collisions', () => {
82
- const existing: Namespace[] = [];
83
- const hues: number[] = [];
84
- for (let i = 0; i < 8; i++) {
85
- const hue = nextHue(existing);
86
- hues.push(hue);
87
- existing.push(ns(`n${i}`, i, false, hue));
88
- }
89
- expect(new Set(hues).size).toBe(hues.length);
90
- for (const hue of hues) {
91
- expect(hue).toBeGreaterThanOrEqual(0);
92
- expect(hue).toBeLessThan(360);
93
- }
94
- });
95
-
96
- it('steps past an already-used hue instead of colliding', () => {
97
- // With one existing namespace, the golden-angle step lands on hue 138 —
98
- // occupy it so the collision-avoidance nudge is exercised.
99
- const existing = [ns('a', 0, false, 138)];
100
- expect(nextHue(existing)).toBe(139);
101
- });
102
- });
103
-
104
- describe('normalizeNamespaceLabel', () => {
105
- it('trims and collapses whitespace', () => {
106
- expect(normalizeNamespaceLabel(' a b ')).toBe('a b');
107
- });
108
-
109
- it('caps at 40 characters', () => {
110
- expect(normalizeNamespaceLabel('x'.repeat(50)).length).toBe(40);
111
- });
112
-
113
- it('rejects empty and blank labels', () => {
114
- expect(() => normalizeNamespaceLabel('')).toThrow();
115
- expect(() => normalizeNamespaceLabel(' ')).toThrow();
116
- });
117
- });
@@ -1,53 +0,0 @@
1
- // `cursedbelt/namespace` — a reusable, flat namespace/workspace model shared
2
- // across content plugins (notes today; aliases/… later, see ns-03). React-free
3
- // and storage-agnostic: the pure `Namespace` data + a host-injected
4
- // `NamespaceStore` contract, mirroring `cursedbelt/category`'s split between a
5
- // pure contract and a host-backed (SQLite) implementation. Importing this
6
- // module on a server never pulls React.
7
-
8
- /** A single namespace (colorway-tagged workspace). Namespaces are flat — no
9
- * parent/child nesting, unlike `Category`. `hue` (0–359) is the colorway seed
10
- * the UI derives all tints from. `ord` sorts non-archived namespaces
11
- * ascending; archived namespaces always sort after non-archived ones. */
12
- export interface Namespace {
13
- id: string;
14
- label: string;
15
- hue: number;
16
- ord: number;
17
- archived: boolean;
18
- createdAt: number;
19
- updatedAt: number;
20
- }
21
-
22
- /** Fields a caller supplies when creating a namespace — the store assigns
23
- * `id`, timestamps, and a default `hue`/`ord` when omitted. */
24
- export interface NamespaceCreateInput {
25
- label: string;
26
- hue?: number;
27
- }
28
-
29
- /** Fields a caller supplies when updating a namespace — all optional, only
30
- * the provided fields change. */
31
- export interface NamespaceUpdateInput {
32
- label?: string;
33
- hue?: number;
34
- ord?: number;
35
- archived?: boolean;
36
- }
37
-
38
- /**
39
- * Storage-agnostic namespace store. The host injects a real implementation
40
- * (f backs it with SQLite, per-role); {@link createMemoryNamespaceStore} is
41
- * the zero-config in-memory default.
42
- *
43
- * `remove` is a hard delete and is only legal when the caller has already
44
- * verified no rows reference the namespace — callers should archive instead
45
- * in the common case. A host-backed store enforces this by throwing when
46
- * references still exist.
47
- */
48
- export interface NamespaceStore {
49
- list(): Namespace[] | Promise<Namespace[]>;
50
- create(input: NamespaceCreateInput): Namespace | Promise<Namespace>;
51
- update(id: string, input: NamespaceUpdateInput): Namespace | Promise<Namespace>;
52
- remove(id: string): void | Promise<void>;
53
- }
@@ -1,53 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { DisposeRegistry } from './DisposeRegistry';
3
-
4
- describe('DisposeRegistry', () => {
5
- test('runs cleanups in reverse (LIFO) order and clears the list', () => {
6
- const registry = new DisposeRegistry();
7
- const order: number[] = [];
8
- registry.add(() => order.push(1));
9
- registry.add(() => order.push(2));
10
- registry.add(() => order.push(3));
11
-
12
- expect(registry.size).toBe(3);
13
- registry.disposeAll();
14
-
15
- expect(order).toEqual([3, 2, 1]);
16
- expect(registry.size).toBe(0);
17
- expect(registry.isDisposed).toBe(true);
18
- });
19
-
20
- test('a throwing cleanup does not strand the others', () => {
21
- const registry = new DisposeRegistry();
22
- const ran: string[] = [];
23
- registry.add(() => ran.push('a'));
24
- registry.add(() => {
25
- throw new Error('boom');
26
- });
27
- registry.add(() => ran.push('c'));
28
-
29
- expect(() => registry.disposeAll()).not.toThrow();
30
- // 'c' (last added) runs first, then the thrower is swallowed, then 'a'.
31
- expect(ran).toEqual(['c', 'a']);
32
- });
33
-
34
- test('disposeAll is idempotent', () => {
35
- const registry = new DisposeRegistry();
36
- let count = 0;
37
- registry.add(() => count++);
38
- registry.disposeAll();
39
- registry.disposeAll();
40
- expect(count).toBe(1);
41
- });
42
-
43
- test('adding after disposal runs the cleanup immediately (no leak)', () => {
44
- const registry = new DisposeRegistry();
45
- registry.disposeAll();
46
- let ran = false;
47
- registry.add(() => {
48
- ran = true;
49
- });
50
- expect(ran).toBe(true);
51
- expect(registry.size).toBe(0);
52
- });
53
- });
@@ -1,70 +0,0 @@
1
- /**
2
- * A LIFO cleanup registry for game-engine resources (event listeners, rAF ids,
3
- * WebGL contexts, audio nodes, timers). The single source of truth that replaces
4
- * the per-game `DisposeRegistry` copies the apps each hand-rolled.
5
- *
6
- * A factory registers every resource it allocates with {@link DisposeRegistry.add};
7
- * on unmount the host hook calls {@link DisposeRegistry.disposeAll}, which runs every
8
- * cleanup in reverse registration order — the same ordering React uses for nested
9
- * effect cleanups, so resources tear down in the opposite order they were built.
10
- *
11
- * @example
12
- * ```ts
13
- * const registry = new DisposeRegistry();
14
- * const id = requestAnimationFrame(loop);
15
- * registry.add(() => cancelAnimationFrame(id));
16
- * canvas.addEventListener('pointermove', onMove);
17
- * registry.add(() => canvas.removeEventListener('pointermove', onMove));
18
- * // later, on unmount:
19
- * registry.disposeAll(); // cancels rAF, removes the listener — in reverse order
20
- * ```
21
- */
22
- export class DisposeRegistry {
23
- private cleanups: Array<() => void> = [];
24
- private disposed = false;
25
-
26
- /**
27
- * Register a cleanup. If the registry has already been disposed (e.g. an async
28
- * factory resolved after unmount and registered a late listener), the cleanup runs
29
- * immediately so the resource can never leak.
30
- */
31
- add(fn: () => void): void {
32
- if (this.disposed) {
33
- runSafely(fn);
34
- return;
35
- }
36
- this.cleanups.push(fn);
37
- }
38
-
39
- /** Number of pending cleanups (handy for tests / leak assertions). */
40
- get size(): number {
41
- return this.cleanups.length;
42
- }
43
-
44
- /** Whether {@link disposeAll} has run. */
45
- get isDisposed(): boolean {
46
- return this.disposed;
47
- }
48
-
49
- /**
50
- * Run every registered cleanup in reverse (LIFO) order, swallowing individual
51
- * errors so one failing cleanup can't strand the rest. Idempotent: the list is
52
- * cleared and the registry is permanently marked disposed.
53
- */
54
- disposeAll(): void {
55
- this.disposed = true;
56
- // Reverse order: the last resource allocated is the first torn down.
57
- for (let i = this.cleanups.length - 1; i >= 0; i--) {
58
- runSafely(this.cleanups[i]);
59
- }
60
- this.cleanups = [];
61
- }
62
- }
63
-
64
- function runSafely(fn: () => void): void {
65
- try {
66
- fn();
67
- } catch (error) {
68
- console.error('DisposeRegistry: a cleanup threw and was swallowed', error);
69
- }
70
- }
@@ -1,51 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { drawBudget } from './drawBudget';
3
-
4
- describe('drawBudget', () => {
5
- test('starts at full quality', () => {
6
- const b = drawBudget({ targetFps: 60 });
7
- expect(b.level).toBe(1);
8
- expect(b.fps).toBe(60);
9
- });
10
-
11
- test('30 consecutive slow frames drop level below 0.5', () => {
12
- const b = drawBudget({ targetFps: 60, windowSize: 30 });
13
- for (let i = 0; i < 30; i++) b.record(50); // 50ms ≫ 16.67ms budget
14
- expect(b.level).toBeLessThan(0.5);
15
- expect(b.fps).toBeCloseTo(20, 0); // 1000 / 50ms
16
- });
17
-
18
- test('frames within budget hold full quality', () => {
19
- const b = drawBudget({ targetFps: 60, windowSize: 30 });
20
- for (let i = 0; i < 30; i++) b.record(16); // just under the 16.67ms budget
21
- expect(b.level).toBe(1);
22
- });
23
-
24
- test('recovers to full quality once frames are fast again', () => {
25
- const b = drawBudget({ targetFps: 60, windowSize: 30 });
26
- for (let i = 0; i < 10; i++) b.record(50); // crater the level
27
- expect(b.level).toBeLessThan(0.2);
28
- for (let i = 0; i < 300; i++) b.record(5); // sustained comfortable frames
29
- expect(b.level).toBe(1);
30
- });
31
-
32
- test('recovery is slower than the drop (hysteresis)', () => {
33
- const drop = drawBudget({ targetFps: 60 });
34
- drop.record(50); // one very slow frame
35
- const afterOneSlow = drop.level;
36
-
37
- const rise = drawBudget({ targetFps: 60 });
38
- rise.record(5); // one fast frame (already at 1, so no change)
39
- const afterOneFast = rise.level;
40
-
41
- // A single slow frame moves level much more than a single fast frame.
42
- expect(1 - afterOneSlow).toBeGreaterThan(1 - afterOneFast);
43
- });
44
-
45
- test('ignores NaN / negative frame times', () => {
46
- const b = drawBudget({ targetFps: 60 });
47
- b.record(Number.NaN);
48
- b.record(-10);
49
- expect(b.level).toBe(1);
50
- });
51
- });
@@ -1,75 +0,0 @@
1
- export interface DrawBudgetOptions {
2
- /** Frame-time budget target in frames per second (default `60`). */
3
- targetFps?: number;
4
- /** Rolling-average window in frames (default `30`). Larger = smoother, slower to react. */
5
- windowSize?: number;
6
- }
7
-
8
- export interface DrawBudget {
9
- /** Feed the wall-clock frame time (ms) each frame; updates `level` and `fps`. */
10
- record: (frameMs: number) => void;
11
- /**
12
- * Adaptive quality in `[0, 1]`: `1` = full quality, `0` = minimal. Games multiply
13
- * expensive feature counts (shadow res, particle count, draw distance) by this.
14
- * Drops quickly when behind, recovers slowly when there's headroom (hysteresis,
15
- * so one stray slow frame doesn't crater quality).
16
- */
17
- readonly level: number;
18
- /** Current rolling-average frames per second. */
19
- readonly fps: number;
20
- }
21
-
22
- // Drop ~this fraction of `level` per frame, per multiple of target frame time over
23
- // budget. Recover this much per frame when comfortable. Asymmetric on purpose —
24
- // shed load fast, restore it gently — so quality doesn't oscillate.
25
- const DROP_COEFFICIENT = 0.05;
26
- const RECOVER_PER_FRAME = 0.01;
27
-
28
- /**
29
- * Adaptive draw-quality governor. Tracks a rolling average of frame times and exposes
30
- * a `level` games use to scale render cost so a struggling device stays responsive.
31
- * Replaces the duplicated per-game copies.
32
- */
33
- export function drawBudget(opts: DrawBudgetOptions = {}): DrawBudget {
34
- const { targetFps = 60, windowSize = 30 } = opts;
35
- const targetMs = 1000 / targetFps;
36
-
37
- const samples: number[] = [];
38
- let sum = 0;
39
- let avgMs = targetMs;
40
- let level = 1;
41
-
42
- return {
43
- record(frameMs: number) {
44
- if (!Number.isFinite(frameMs) || frameMs < 0) return;
45
-
46
- samples.push(frameMs);
47
- sum += frameMs;
48
- if (samples.length > windowSize) sum -= samples.shift() as number;
49
- avgMs = sum / samples.length;
50
-
51
- // headroom > 0 → comfortably under budget; < 0 → behind.
52
- const headroom = targetMs - avgMs;
53
- if (headroom < 0) {
54
- // How many target-frames behind we are scales the drop; far behind drops fast.
55
- level += (headroom / targetMs) * DROP_COEFFICIENT;
56
- } else {
57
- level += RECOVER_PER_FRAME;
58
- }
59
- level = clamp01(level);
60
- },
61
- get level() {
62
- return level;
63
- },
64
- get fps() {
65
- if (samples.length === 0) return targetFps; // nothing recorded yet → report the target
66
- return avgMs > 0 ? 1000 / avgMs : targetFps;
67
- },
68
- };
69
- }
70
-
71
- function clamp01(value: number): number {
72
- if (value < 0) return 0;
73
- if (value > 1) return 1;
74
- return value;
75
- }
@@ -1,91 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { createFixedTimestep } from './fixedTimestep';
3
-
4
- describe('createFixedTimestep', () => {
5
- test('a 100ms frame at 60hz runs 6 updates and 1 render', () => {
6
- let updates = 0;
7
- let renders = 0;
8
- const ts = createFixedTimestep({
9
- hz: 60,
10
- maxCatchup: 10, // raise above the default-5 guard so the full backlog is observed
11
- onUpdate: () => updates++,
12
- onRender: () => renders++,
13
- });
14
-
15
- ts.step(100); // 100 / (1000/60) = 6.0 → 6 updates
16
-
17
- expect(updates).toBe(6);
18
- expect(renders).toBe(1);
19
- });
20
-
21
- test('onUpdate receives the fixed dt in seconds', () => {
22
- const dts: number[] = [];
23
- const ts = createFixedTimestep({
24
- hz: 50,
25
- onUpdate: (dt) => dts.push(dt),
26
- onRender: () => {},
27
- });
28
- ts.step(40); // 40 / 20ms = 2 updates, each dt = 0.02s
29
- expect(dts).toEqual([0.02, 0.02]);
30
- });
31
-
32
- test('accumulates fractional frames across steps (5 frames)', () => {
33
- let updates = 0;
34
- let renders = 0;
35
- // 90hz step = 11.111ms. A run of 5×8ms frames (40ms total) → 3 whole steps,
36
- // with the leftover carried frame-to-frame rather than dropped.
37
- const ts = createFixedTimestep({
38
- hz: 90,
39
- onUpdate: () => updates++,
40
- onRender: () => renders++,
41
- });
42
- for (let i = 0; i < 5; i++) ts.step(8);
43
- expect(renders).toBe(5); // one render per frame, always
44
- expect(updates).toBe(3); // 40ms / 11.111ms = 3 whole simulation steps
45
- });
46
-
47
- test('onRender alpha reflects leftover accumulator', () => {
48
- let alpha = -1;
49
- const ts = createFixedTimestep({ hz: 100, onUpdate: () => {}, onRender: (a) => (alpha = a) });
50
- ts.step(15); // step = 10ms → 1 update, 5ms left → alpha = 0.5
51
- expect(alpha).toBeCloseTo(0.5, 5);
52
- });
53
-
54
- test('clamps the spiral of death at maxCatchup and drops the backlog', () => {
55
- let updates = 0;
56
- const ts = createFixedTimestep({
57
- hz: 60,
58
- maxCatchup: 5,
59
- onUpdate: () => updates++,
60
- onRender: () => {},
61
- });
62
- ts.step(5000); // a 5s backgrounded gap would be ~300 steps; clamp to 5
63
- expect(updates).toBe(5);
64
-
65
- // Backlog was dropped, so the next normal frame produces exactly one update.
66
- updates = 0;
67
- ts.step(17);
68
- expect(updates).toBe(1);
69
- });
70
-
71
- test('ignores NaN / negative deltas', () => {
72
- let updates = 0;
73
- const ts = createFixedTimestep({ hz: 60, onUpdate: () => updates++, onRender: () => {} });
74
- ts.step(Number.NaN);
75
- ts.step(-100);
76
- expect(updates).toBe(0);
77
- });
78
-
79
- test('reset clears the accumulator', () => {
80
- let updates = 0;
81
- const ts = createFixedTimestep({ hz: 60, onUpdate: () => updates++, onRender: () => {} });
82
- ts.step(10); // 10ms < 16.667 → 0 updates, 10ms banked
83
- ts.reset();
84
- ts.step(10); // bank cleared → still under one step → 0 updates
85
- expect(updates).toBe(0);
86
- });
87
-
88
- test('throws on a non-positive hz', () => {
89
- expect(() => createFixedTimestep({ hz: 0, onUpdate: () => {}, onRender: () => {} })).toThrow();
90
- });
91
- });
@@ -1,79 +0,0 @@
1
- export interface FixedTimestepOptions {
2
- /** Update rate in Hz (e.g. 60 → a fixed 1/60 s simulation step). */
3
- hz: number;
4
- /**
5
- * Called zero or more times per frame with the fixed timestep in **seconds**
6
- * (e.g. `1/60 ≈ 0.0167`). Drive all simulation/physics from this `dt` so behavior
7
- * is frame-rate independent.
8
- */
9
- onUpdate: (dt: number) => void;
10
- /**
11
- * Called exactly once per frame with the interpolation factor `alpha` in `[0, 1)`
12
- * — the fraction of a step the accumulator is into the next update. Render the
13
- * visual state as `lerp(prev, current, alpha)` for smooth motion between updates.
14
- */
15
- onRender: (alpha: number) => void;
16
- /**
17
- * Maximum simulation steps per frame (default `5`). The "spiral of death" guard:
18
- * without it, a tab backgrounded for 5 s would try to simulate hundreds of steps
19
- * in one frame and lock the thread. When the cap is hit the leftover backlog is
20
- * dropped (time is sacrificed to keep the frame responsive).
21
- */
22
- maxCatchup?: number;
23
- }
24
-
25
- export interface FixedTimestep {
26
- /** Advance by the wall-clock delta since the previous frame, in **milliseconds**. */
27
- step: (deltaMs: number) => void;
28
- /** Clear the accumulator — call after a pause/resume so the gap isn't simulated. */
29
- reset: () => void;
30
- }
31
-
32
- /**
33
- * Creates a fixed-timestep accumulator: the canonical decoupling of a fixed-rate
34
- * simulation from a variable-rate render. Call {@link FixedTimestep.step} once per
35
- * animation frame with the wall-clock delta; it invokes `onUpdate(dt)` zero or more
36
- * times with the fixed `dt`, then `onRender(alpha)` once with the interpolation
37
- * factor. Replaces the verbatim copies the apps each carried per game.
38
- */
39
- export function createFixedTimestep(opts: FixedTimestepOptions): FixedTimestep {
40
- const { hz, onUpdate, onRender, maxCatchup = 5 } = opts;
41
- if (!(hz > 0)) throw new Error(`createFixedTimestep: hz must be > 0 (got ${hz})`);
42
-
43
- const stepMs = 1000 / hz;
44
- const stepSeconds = stepMs / 1000;
45
- // Repeated `accumulator -= stepMs` accrues float error, so an exact integer multiple
46
- // (e.g. 100ms at 60hz = 6 steps) can fall a rounding-bit short of the final step.
47
- // A tiny epsilon recovers the mathematically-correct count without ever firing a
48
- // spurious step (a real sub-step delta is never within 1e-9 ms of the boundary).
49
- const threshold = stepMs - 1e-9;
50
- let accumulator = 0;
51
-
52
- return {
53
- step(deltaMs: number) {
54
- // Guard against NaN, negatives, and the huge delta after a backgrounded tab.
55
- if (!Number.isFinite(deltaMs) || deltaMs < 0) deltaMs = 0;
56
- accumulator += deltaMs;
57
-
58
- let steps = 0;
59
- while (accumulator >= threshold && steps < maxCatchup) {
60
- onUpdate(stepSeconds);
61
- accumulator -= stepMs;
62
- steps++;
63
- }
64
-
65
- // Hit the catch-up cap with backlog remaining → drop it (spiral-of-death guard).
66
- if (steps >= maxCatchup && accumulator >= threshold) {
67
- accumulator = 0;
68
- } else if (accumulator < 0) {
69
- // Epsilon stepping can leave a sub-femtosecond negative residue; floor it.
70
- accumulator = 0;
71
- }
72
-
73
- onRender(accumulator / stepMs);
74
- },
75
- reset() {
76
- accumulator = 0;
77
- },
78
- };
79
- }
@@ -1,16 +0,0 @@
1
- // cursedbelt/game core — engine-agnostic, zero React, browser-safe to import from a
2
- // Bun server test. The React host hook lives in `cursedbelt/game/react`.
3
- export { DisposeRegistry } from './DisposeRegistry';
4
- export type { DrawBudget, DrawBudgetOptions } from './drawBudget';
5
- export { drawBudget } from './drawBudget';
6
- export type { FixedTimestep, FixedTimestepOptions } from './fixedTimestep';
7
- export { createFixedTimestep } from './fixedTimestep';
8
- export type {
9
- GamepadSnapshot,
10
- InputCollector,
11
- InputCollectorOptions,
12
- InputSnapshot,
13
- PointerSnapshot,
14
- } from './inputCollector';
15
- export { createInputCollector, deadzone } from './inputCollector';
16
- export { resizeCanvasToDisplaySize } from './resizeCanvas';