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
@@ -0,0 +1,455 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * `bun scripts/publicSurface.ts [--prune]` — refuse a public surface that grows
4
+ * without a recorded reason.
5
+ *
6
+ * ## What this exists for
7
+ *
8
+ * Measured 2026-09-13 across all 845 consumer files on this machine that mention
9
+ * `cursedbelt`: `1.0.0` published **108 export subpaths and 3,359 exported
10
+ * symbols**, of which every consumer together imported **406 — 12.1%**.
11
+ * `cursedbelt/react` alone exposed 1,037 and 178 were used. Nothing objected at
12
+ * any single step, because no single step was large; the owner's own account of
13
+ * how it got there is *"worked on over many projects with tons of features
14
+ * changing"*.
15
+ *
16
+ * 🔴 **This is NOT a dead-code check, and 12.1% is not a bug to delete.** Most of
17
+ * that surface is alive: the `react` barrel re-exports components that live
18
+ * imports keep mounted, and a design system is SUPPOSED to ship more than any one
19
+ * app has adopted. Deleting everything safely deletable removed 4% of the package
20
+ * (tasks 32/33). What was missing was not restraint, it was an INSTRUMENT — the
21
+ * total was never measured anywhere, so growth was invisible at the only moment
22
+ * anyone could have judged it. This file makes growth visible and deliberate; it
23
+ * does not forbid it.
24
+ *
25
+ * Both rows below are a DATED RECORD, not a live claim — the live number is
26
+ * `publicSurface.baseline`, checked against the tree every run, and it can only
27
+ * go down from the second row:
28
+ *
29
+ * subpaths symbols dependencies peers
30
+ * 1.0.0, before tasks 32/33 108 4,167 47 51
31
+ * seeded 2026-09-13 85 3,808 44 47
32
+ *
33
+ * 🔴 Both rows were measured by THIS file, run against `c01b119` and against the
34
+ * tree it was seeded from, so they are commensurable with each other and with
35
+ * every future run. They are NOT the 3,359 in the task that asked for this file:
36
+ * that number came from an independent walk of the same shape, and this one counts
37
+ * 4,167 for the same tree. The gap is method, not surface — a `default` export,
38
+ * a destructured `export const {a, b}`, and a bare `export * from 'cwip/layout'`
39
+ * are each counted here and were not counted there. Which is the point of the
40
+ * "What it counts, exactly" section below: two numbers that do not say what they
41
+ * counted cannot be compared, and only one of them can be a ratchet.
42
+ *
43
+ * `subpaths` excludes the three stylesheet exports, which have no symbols; the
44
+ * `exports` map has three more keys than the count above. The dependency columns
45
+ * are the second half of the same failure and are asserted by
46
+ * `src/declaredDepsAreImported.spec.ts` — see "The dependency half" below.
47
+ *
48
+ * This file is the reason that second row cannot quietly become the first one
49
+ * again: **document the WHY; automate the WHETHER.** A paragraph in a doc asking
50
+ * the next agent to think about whether a new subpath is needed is the failure
51
+ * mode, not the fix.
52
+ *
53
+ * ## The ratchet
54
+ *
55
+ * `publicSurface.baseline` holds a COUNT PER SUBPATH, the same shape as
56
+ * `tools/check-paths.baseline`, which is the mechanism this generation already
57
+ * trusts. Four ways to fail, and the last two are what make it a ratchet rather
58
+ * than a high-water mark:
59
+ *
60
+ * 1. a subpath in `exports` that the baseline does not list → NEW SURFACE
61
+ * 2. a listed subpath whose symbol count went UP → GREW
62
+ * 3. a listed subpath whose count went DOWN, or that is gone → run --prune
63
+ * 4. an entry matching no subpath at all → run --prune
64
+ *
65
+ * (3) and (4) mean a deleted module cannot leave a stale allowance behind for the
66
+ * next module to grow into. Inherited surface can only fall.
67
+ *
68
+ * `--prune` records the burn-down in one deliberate command and REFUSES to raise
69
+ * anything, so a genuine new export is `--prune`-then-review rather than a number
70
+ * edited by hand, and it shows up in the diff as surface.
71
+ *
72
+ * ## 🔴 What it counts, exactly — and what it does not
73
+ *
74
+ * A count is only worth having if it says what it counted; a count that silently
75
+ * measures less than it claims is a guess wearing a measurement's clothes. This
76
+ * walks, per export subpath in `package.json`, the TypeScript AST of the entry
77
+ * module and every `export * from` beneath it, transitively, deduped by name:
78
+ *
79
+ * · `export const/let/var/function/class/interface/type/enum/namespace`
80
+ * · `export { x }`, `export { x as y }`, `export type { x }`
81
+ * · `export * as ns from './m'` (one symbol: `ns`)
82
+ * · `export default` / `export default function Foo` (one symbol: `default`)
83
+ * · `export * from './m'` (transitive; cycles are visited once)
84
+ * · `export * from 'some-package'` — RESOLVED and walked, not skipped. There is
85
+ * one today (`src/core/layout/index.ts` re-exports `cwip/layout`) and a
86
+ * package's symbols re-exported under this package's name are this package's
87
+ * surface. If such a specifier ever resolves to something this cannot parse
88
+ * (a `.js` build with no types), the run FAILS rather than counting zero.
89
+ *
90
+ * Deliberately NOT counted, each because counting it would make the number lie:
91
+ *
92
+ * · non-TS export targets — `./styles.css`, `./theme.css`, `./workspace.css`.
93
+ * A stylesheet has no symbols. They are reported as "asset export(s)" so they
94
+ * cannot vanish from the accounting.
95
+ * · anything not reachable from an `exports` subpath. Internal modules are not
96
+ * public surface. What `src/react/components/**` contains is not the measure;
97
+ * what the `./react` barrel re-exports is.
98
+ * · the two `bin` targets, `cc-verify` and `cc-promote`. 🔴 They ARE public
99
+ * surface — and they are NOT reachable through `exports`, which is exactly why
100
+ * an earlier reachability walk seeded from `exports` alone reported a shipped
101
+ * CLI as removable. A CLI's interface is its argv, not its export list, so
102
+ * counting its exported symbols would measure the wrong thing. Their SHAPE is
103
+ * already asserted by `src/publishShape.spec.ts` ("declares bin paths npm will
104
+ * actually keep"), and nothing here concludes anything is dead — this tool
105
+ * only ever compares `cursedbelt` to its own baseline.
106
+ *
107
+ * ## What this may NOT become
108
+ *
109
+ * · Not a usage check. It must never try to know who imports `cursedbelt`; that
110
+ * is cross-repo, and a repo's gate proves that repo.
111
+ * · Not a ban on growth. New surface is one hand-added line plus a commit
112
+ * message that says why — a reviewable moment, not a wall.
113
+ * · Not a second guard. One check, one baseline, entry criteria in this header.
114
+ *
115
+ * ## The dependency half
116
+ *
117
+ * 47 hard dependencies is the number that decides what a consumer pays, and it is
118
+ * surface by the same argument. It is NOT re-checked here, because it is already
119
+ * checked once: `src/declaredDepsAreImported.spec.ts` fails on any `dependencies`
120
+ * or `peerDependencies` entry that no file under `src/` or `scripts/` imports, and
121
+ * `src/noPathDeps.spec.ts` fails on a `file:`/`link:`/`workspace:`/`portal:` range
122
+ * in any dependency block. Two ratchets that disagree is the guard sprawl this
123
+ * generation is trying to avoid, so this file points at them instead of repeating
124
+ * them.
125
+ *
126
+ * 🔴 If you ever move that check here, keep the property that made it correct: it
127
+ * scans EVERY file type, not just `.ts`. `tw-animate-css` is imported from
128
+ * `src/styles.css` and a `.ts`-only scan calls it dead.
129
+ *
130
+ * Exit 1 on any failure, so it can sit in a verify chain. It sits in the suite
131
+ * instead — `src/publicSurface.spec.ts` — so `bun run verify` needs no new script
132
+ * and cannot be a script somebody forgot to chain.
133
+ */
134
+ import { dirname, join } from 'node:path';
135
+ import ts from 'typescript';
136
+
137
+ // Resolved from this file, never from `process.cwd()`: the suite imports it and
138
+ // `bun test` can be started from anywhere.
139
+ const ROOT = dirname(import.meta.dir);
140
+ const BASELINE = join(ROOT, 'publicSurface.baseline');
141
+
142
+ const DEFAULT_HEADER = [
143
+ '# Public surface of `cursedbelt`: one line per export subpath, `<exported symbols> <subpath>`.',
144
+ '#',
145
+ '# A CEILING, not an amnesty. The check fails on a subpath NOT listed here, on a listed',
146
+ '# subpath that gained a symbol, and on an entry that no longer matches `package.json`',
147
+ '# exports — so a deleted module cannot leave a stale allowance behind. Surface can only fall.',
148
+ '#',
149
+ '# To record a deliberate change: bun scripts/publicSurface.ts --prune',
150
+ '# It refuses to RAISE anything, so new surface is a reviewed diff, never a silent one.',
151
+ '#',
152
+ '# What a count includes, what it excludes and why, and the measurement this file is a',
153
+ '# ratchet against, are in the header of scripts/publicSurface.ts. Say nothing here that',
154
+ '# the entries below already say.',
155
+ ];
156
+
157
+ export type Surface = {
158
+ /** subpath (as written in `exports`) → deduped exported symbol count */
159
+ counts: Map<string, number>;
160
+ /** subpaths skipped because they resolve to a non-TS asset (the three stylesheets) */
161
+ assets: string[];
162
+ };
163
+
164
+ const isRelative = (spec: string): boolean => spec.startsWith('./') || spec.startsWith('../');
165
+ const isWalkable = (file: string): boolean => /\.(m|c)?tsx?$/.test(file);
166
+
167
+ /** `./a/b` from `src/x/y.tsx` → the first of `src/a/b.ts(x)`, `src/a/b/index.ts(x)`, … that exists. */
168
+ const resolveRelative = async (fromFile: string, spec: string): Promise<string | null> => {
169
+ const base = join(dirname(fromFile), spec);
170
+ const candidates = [`${base}.ts`, `${base}.tsx`, `${base}/index.ts`, `${base}/index.tsx`, base];
171
+ for (const c of candidates) {
172
+ if (await Bun.file(c).exists()) return c;
173
+ }
174
+ return null;
175
+ };
176
+
177
+ /**
178
+ * A bare `export * from 'pkg'` — resolved through node resolution so its symbols
179
+ * are counted rather than silently dropped. Throws when the package resolves to
180
+ * something with no parseable types, because a zero there would be a lie.
181
+ */
182
+ const resolveBare = (fromFile: string, spec: string): string => {
183
+ let resolved: string;
184
+ try {
185
+ resolved = Bun.resolveSync(spec, dirname(fromFile));
186
+ } catch (cause) {
187
+ throw new Error(
188
+ `${fromFile}: \`export * from '${spec}'\` re-exports a package that does not resolve, so its ` +
189
+ 'symbols cannot be counted. Install it, or name the symbols — do not let the count lie.',
190
+ { cause },
191
+ );
192
+ }
193
+ if (!isWalkable(resolved)) {
194
+ throw new Error(
195
+ `${fromFile}: \`export * from '${spec}'\` resolves to ${resolved}, which this measurement cannot ` +
196
+ 'parse. Point it at TypeScript, name the symbols, or teach scripts/publicSurface.ts to read ' +
197
+ 'a declaration bundle — do not let the count lie.',
198
+ );
199
+ }
200
+ return resolved;
201
+ };
202
+
203
+ // One parse per file, shared across all 88 subpaths: the barrels overlap heavily
204
+ // (every `./react/*` subpath re-parses a slice of what `./react` already walked).
205
+ const parsed = new Map<string, ts.SourceFile>();
206
+ const sourceOf = async (file: string): Promise<ts.SourceFile> => {
207
+ const hit = parsed.get(file);
208
+ if (hit !== undefined) return hit;
209
+ // 🔴 Bun.file, NOT node:fs — this package's test preload shares one process with
210
+ // specs that virtualize node:fs, so a readFileSync scan reads MOCK data and the
211
+ // measurement passes against files it never opened. Same trap as
212
+ // publishShape.spec.ts; see its header.
213
+ const source = ts.createSourceFile(file, await Bun.file(file).text(), ts.ScriptTarget.ES2022, true);
214
+ parsed.set(file, source);
215
+ return source;
216
+ };
217
+
218
+ /** Every name a binding pattern introduces — `export const { a, b: [c] } = …` is three. */
219
+ const bindingNames = (name: ts.BindingName, out: Set<string>): void => {
220
+ if (ts.isIdentifier(name)) {
221
+ out.add(name.text);
222
+ return;
223
+ }
224
+ for (const el of name.elements) {
225
+ if (ts.isBindingElement(el)) bindingNames(el.name, out);
226
+ }
227
+ };
228
+
229
+ /** Deduped exported names of `file`, following `export * from` chains. */
230
+ const exportedNames = async (file: string, seen = new Set<string>()): Promise<Set<string>> => {
231
+ const names = new Set<string>();
232
+ if (seen.has(file)) return names;
233
+ seen.add(file);
234
+
235
+ const source = await sourceOf(file);
236
+ const modifiersOf = (node: ts.Node): readonly ts.Modifier[] =>
237
+ ts.canHaveModifiers(node) ? (ts.getModifiers(node) ?? []) : [];
238
+
239
+ for (const st of source.statements) {
240
+ if (ts.isExportDeclaration(st)) {
241
+ if (st.exportClause && ts.isNamedExports(st.exportClause)) {
242
+ for (const el of st.exportClause.elements) names.add(el.name.text);
243
+ } else if (st.exportClause && ts.isNamespaceExport(st.exportClause)) {
244
+ names.add(st.exportClause.name.text);
245
+ } else {
246
+ const spec =
247
+ st.moduleSpecifier && ts.isStringLiteral(st.moduleSpecifier) ? st.moduleSpecifier.text : null;
248
+ if (spec === null) continue;
249
+ let target: string | null;
250
+ if (isRelative(spec)) {
251
+ target = await resolveRelative(file, spec);
252
+ if (target === null) throw new Error(`${file}: \`export * from '${spec}'\` resolves to nothing.`);
253
+ } else {
254
+ target = resolveBare(file, spec);
255
+ }
256
+ for (const n of await exportedNames(target, seen)) names.add(n);
257
+ }
258
+ continue;
259
+ }
260
+ if (ts.isExportAssignment(st)) {
261
+ names.add('default');
262
+ continue;
263
+ }
264
+ const modifiers = modifiersOf(st);
265
+ if (!modifiers.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)) continue;
266
+ // `export default function Foo()` is imported as `default`, not as `Foo`.
267
+ if (modifiers.some((m) => m.kind === ts.SyntaxKind.DefaultKeyword)) {
268
+ names.add('default');
269
+ continue;
270
+ }
271
+ if (ts.isVariableStatement(st)) {
272
+ for (const d of st.declarationList.declarations) bindingNames(d.name, names);
273
+ } else if (
274
+ (ts.isFunctionDeclaration(st) ||
275
+ ts.isClassDeclaration(st) ||
276
+ ts.isInterfaceDeclaration(st) ||
277
+ ts.isTypeAliasDeclaration(st) ||
278
+ ts.isEnumDeclaration(st) ||
279
+ ts.isModuleDeclaration(st)) &&
280
+ st.name !== undefined
281
+ ) {
282
+ if (ts.isIdentifier(st.name) || ts.isStringLiteral(st.name)) names.add(st.name.text);
283
+ }
284
+ }
285
+ return names;
286
+ };
287
+
288
+ /** The `./src/**` or `./scripts/**` TypeScript file a subpath resolves to, or null when it is an asset. */
289
+ const entryOf = (value: unknown): string | null => {
290
+ if (typeof value === 'string')
291
+ return (value.startsWith('./src/') || value.startsWith('./scripts/')) && isWalkable(value)
292
+ ? join(ROOT, value.slice(2))
293
+ : null;
294
+ if (typeof value !== 'object' || value === null) return null;
295
+ // `bun` and `source` are the conditions that point at TypeScript; `types` and
296
+ // `import` point into `dist`, which is build output and may not exist. Reading
297
+ // the conditions in this order is what makes the measurement work on a clean
298
+ // checkout, before `bun run build` has ever run.
299
+ for (const key of ['bun', 'source', 'types', 'import', 'default']) {
300
+ if (key in (value as Record<string, unknown>)) {
301
+ const hit = entryOf((value as Record<string, unknown>)[key]);
302
+ if (hit !== null) return hit;
303
+ }
304
+ }
305
+ for (const v of Object.values(value as Record<string, unknown>)) {
306
+ const hit = entryOf(v);
307
+ if (hit !== null) return hit;
308
+ }
309
+ return null;
310
+ };
311
+
312
+ export const measureSurface = async (pkg: { exports: Record<string, unknown> }): Promise<Surface> => {
313
+ const counts = new Map<string, number>();
314
+ const assets: string[] = [];
315
+ for (const [subpath, value] of Object.entries(pkg.exports)) {
316
+ const entry = entryOf(value);
317
+ if (entry === null) {
318
+ assets.push(subpath);
319
+ continue;
320
+ }
321
+ counts.set(subpath, (await exportedNames(entry)).size);
322
+ }
323
+ return { counts, assets };
324
+ };
325
+
326
+ // ── the baseline ────────────────────────────────────────────────────────────────
327
+
328
+ export type Baseline = { header: string[]; counts: Map<string, number> };
329
+
330
+ export const parseBaseline = (text: string): Baseline => {
331
+ const header = text.split('\n').filter((l) => l.startsWith('#'));
332
+ const counts = new Map<string, number>();
333
+ for (const line of text.split('\n')) {
334
+ if (!line.trim() || line.startsWith('#')) continue;
335
+ const m = line.match(/^\s*(\d+)\s+(\S.*)$/);
336
+ if (m?.[2] !== undefined) counts.set(m[2].trim(), Number(m[1]));
337
+ }
338
+ return { header: header.length > 0 ? header : DEFAULT_HEADER, counts };
339
+ };
340
+
341
+ export const readBaseline = async (): Promise<Baseline> => {
342
+ const f = Bun.file(BASELINE);
343
+ return parseBaseline((await f.exists()) ? await f.text() : '');
344
+ };
345
+
346
+ export const formatBaseline = (header: string[], counts: Map<string, number>): string =>
347
+ `${[...header, ...[...counts.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([k, n]) => `${n} ${k}`)].join(
348
+ '\n',
349
+ )}\n`;
350
+
351
+ export type Failures = { grown: string[]; fresh: string[]; stale: string[] };
352
+
353
+ export const compare = (surface: Surface, baseline: Baseline): Failures => {
354
+ const fresh: string[] = [];
355
+ const grown: string[] = [];
356
+ for (const [subpath, n] of surface.counts) {
357
+ const allowed = baseline.counts.get(subpath);
358
+ if (allowed === undefined) fresh.push(`${subpath} — NEW subpath, ${n} exported symbol(s)`);
359
+ else if (n > allowed) grown.push(`${subpath} — baseline allows ${allowed}, exports ${n} now`);
360
+ }
361
+ const stale = [...baseline.counts.entries()]
362
+ .filter(([subpath, n]) => (surface.counts.get(subpath) ?? 0) < n)
363
+ .map(([subpath, n]) =>
364
+ surface.counts.has(subpath)
365
+ ? `${subpath} — baseline says ${n}, exports ${surface.counts.get(subpath)}`
366
+ : `${subpath} — baseline says ${n}, the subpath is gone`,
367
+ );
368
+ return { fresh, grown, stale };
369
+ };
370
+
371
+ export const run = async (): Promise<Failures & { surface: Surface; baseline: Baseline }> => {
372
+ const pkg = (await Bun.file(join(ROOT, 'package.json')).json()) as { exports: Record<string, unknown> };
373
+ const surface = await measureSurface(pkg);
374
+ const baseline = await readBaseline();
375
+ return { ...compare(surface, baseline), surface, baseline };
376
+ };
377
+
378
+ // ── CLI ─────────────────────────────────────────────────────────────────────────
379
+
380
+ if (import.meta.main) {
381
+ const argv = process.argv.slice(2);
382
+ const prune = argv.includes('--prune');
383
+ const { fresh, grown, stale, surface, baseline } = await run();
384
+ const total = [...surface.counts.values()].reduce((a, b) => a + b, 0);
385
+
386
+ // `--seed` exists once, to record the surface the ratchet starts from. It REFUSES
387
+ // over an existing baseline, so re-seeding means deleting the file first — two
388
+ // visible steps and a whole-file diff, rather than one command that quietly
389
+ // re-baselines whatever grew.
390
+ if (argv.includes('--seed')) {
391
+ if (baseline.counts.size > 0) {
392
+ console.error(`publicSurface --seed refuses: ${BASELINE} already records ${baseline.counts.size} subpath(s).`);
393
+ console.error('Lower it with --prune. Raising it is a hand edit in a commit that says why.');
394
+ process.exit(1);
395
+ }
396
+ await Bun.write(BASELINE, formatBaseline(baseline.header, surface.counts));
397
+ console.log(`publicSurface: seeded ${surface.counts.size} subpath(s), ${total} exported symbol(s).`);
398
+ process.exit(0);
399
+ }
400
+
401
+ if (prune) {
402
+ // 🔴 It may only LOWER. Growth is the thing this file exists to make visible;
403
+ // a prune that could absorb it would be the disable switch.
404
+ if (fresh.length > 0 || grown.length > 0) {
405
+ console.error('publicSurface --prune refuses while the surface has GROWN — it may only lower the baseline.\n');
406
+ for (const v of [...fresh, ...grown]) console.error(` ${v}`);
407
+ console.error(
408
+ '\nTo record deliberate new surface, add the line by hand and say in the commit message why the\npackage needs it — that is the reviewable moment this check exists to create.\n',
409
+ );
410
+ process.exit(1);
411
+ }
412
+ const lowered = new Map<string, number>();
413
+ for (const [subpath, n] of baseline.counts) {
414
+ const now = surface.counts.get(subpath);
415
+ if (now !== undefined) lowered.set(subpath, Math.min(n, now));
416
+ }
417
+ await Bun.write(BASELINE, formatBaseline(baseline.header, lowered));
418
+ console.log(
419
+ `publicSurface: baseline pruned to ${lowered.size} subpath(s), ` +
420
+ `${baseline.counts.size - lowered.size} removed, ${[...lowered.values()].reduce((a, b) => a + b, 0)} symbols.`,
421
+ );
422
+ process.exit(0);
423
+ }
424
+
425
+ let bad = false;
426
+ const report = (label: string, lines: string[], advice: string): void => {
427
+ if (lines.length === 0) return;
428
+ bad = true;
429
+ console.error(`\n${lines.length} ${label}:\n`);
430
+ for (const l of lines) console.error(` ${l}`);
431
+ console.error(`\n${advice}\n`);
432
+ };
433
+ report(
434
+ 'export subpath(s) the baseline does not know',
435
+ fresh,
436
+ 'A new subpath is a permanent public promise. If it is genuinely wanted, add its line to\n' +
437
+ `${BASELINE} and say why in the commit message.`,
438
+ );
439
+ report(
440
+ 'subpath(s) that gained exported symbols',
441
+ grown,
442
+ 'A baseline entry is a ceiling. Raise it by hand, in a commit that says why.',
443
+ );
444
+ report(
445
+ 'baseline entr(y|ies) that no longer match `exports`',
446
+ stale,
447
+ `Surface came off and the ratchet was not tightened. Run:\n cd ${ROOT} && bun scripts/publicSurface.ts --prune`,
448
+ );
449
+ if (bad) process.exit(1);
450
+
451
+ console.log(
452
+ `publicSurface: ${surface.counts.size} subpath(s), ${total} exported symbol(s), ` +
453
+ `${surface.assets.length} asset export(s) — none grown.`,
454
+ );
455
+ }
@@ -41,19 +41,48 @@
41
41
  * no behaviour here for an app to get wrong — only a path.
42
42
  */
43
43
  import { mkdirSync, readdirSync, rmSync, statSync } from "node:fs";
44
+ import { join, resolve } from "node:path";
44
45
 
45
46
  /** Older than this and no live run can still want it. */
46
47
  const STALE_MS = 6 * 60 * 60 * 1000;
47
48
 
49
+ /**
50
+ * ## 🔴 Where the scratch root is, and the bug that moved it (2026-09-13)
51
+ *
52
+ * This used to assemble the PREVIOUS generation's state root out of the ambient
53
+ * home directory. It is a preload, so it ran before the first test in this repo on
54
+ * every `bun test`, which meant every `verify` in this generation was writing into
55
+ * the last one. Measured the day it was found: 77 MB and 259 process directories
56
+ * under it, and a single spec run while the task was being written made number 260.
57
+ * This generation's first law is that nothing may depend on an older one.
58
+ *
59
+ * The checker could not see it: an interpolated `${VAR}/…` matches none of the
60
+ * shapes it looks for, and the line that actually built the path carried no banned
61
+ * string at all — it was assembled from a variable. So `testTmpRoot.spec.ts` is
62
+ * what holds this now, and it asserts the RESOLVED root rather than the source.
63
+ *
64
+ * `$FORGE_STATE` is this generation's state root and the runner hands it to every
65
+ * worker, so under autopilot it is always set. A bare checkout with no generation
66
+ * around it gets a repo-local `.agent.noindex/tmp`, which is already gitignored
67
+ * here — never a path built from `$HOME`.
68
+ *
69
+ * 🔴 `.noindex` stays in the directory name either way: it is the only marker that
70
+ * keeps Spotlight out of files written LATER into a tree, and a test root is
71
+ * written to constantly (measured 43× on this machine). `.agent.noindex` already
72
+ * carries it.
73
+ */
74
+ export function resolveTestTmpRoot(env: Record<string, string | undefined> = process.env): string {
75
+ const forgeState = env.FORGE_STATE?.trim();
76
+ if (forgeState) return join(forgeState, "test-scratch.noindex", "tmp");
77
+ // This file is `<repo>/scripts/testTmpRoot.ts`; the root is one hop up.
78
+ return resolve(import.meta.dir, "..", ".agent.noindex", "tmp");
79
+ }
80
+
48
81
  export function redirectTestTmpRoot(): void {
49
82
  // A suite that genuinely wants the real `$TMPDIR` sets this and wins.
50
83
  if (process.env.TMPDIR_TEST_PASSTHROUGH) return;
51
84
 
52
- // 🔴 `.noindex` is not decoration: it is the only marker that keeps Spotlight
53
- // out of files written LATER into a tree, and a test root is written to
54
- // constantly (measured 43× on this machine).
55
- const appManaged = process.env.APP_MANAGED_HOME ?? `${process.env.HOME}/.app-managed`;
56
- const tmpRoot = `${appManaged}/test-scratch.noindex/tmp`;
85
+ const tmpRoot = resolveTestTmpRoot();
57
86
 
58
87
  // Sweep BEFORE claiming ours. A SIGKILLed run cannot clean up after itself —
59
88
  // that is precisely how the 310k pile was built — so the next run is the only
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * cursedbelt/client — the browser half of the cursedbelt server toolkit:
3
3
  * • the ctgr GET-tunnel (localStorage mode flag, v1 request converter, hard-block guard),
4
- * pairing with `cursedbelt/server` (`unGETify`) + `cursedbelt/ctgr` (the codec);
4
+ * pairing with `cursedbelt/server` (`unGETify`) + the ctgr codec (`src/ctgr`, re-exported from `cursedbelt/server`);
5
5
  * • the WS hub's auto-reconnecting channel socket (partysocket), pairing with
6
6
  * `cursedbelt/server`'s `createWsHub`.
7
7
  */
@@ -1,4 +1,5 @@
1
- // `cursedbelt/category` — reusable hierarchical category/folder model. Pure and
1
+ // src/core/category — reusable hierarchical category/folder model, reaching consumers
2
+ // through `cursedbelt/react`'s categoryTree. Pure and
2
3
  // React-free (the `TreeView` adapter lives in `cursedbelt/react`), so servers
3
4
  // can import the contract + store without pulling any UI runtime.
4
5
 
@@ -1,4 +1,4 @@
1
- // `cursedbelt/category` — a reusable, hierarchical category/folder model shared
1
+ // src/core/category — a reusable, hierarchical category/folder model shared
2
2
  // by content plugins (notes today; snippets/aliases/… later). React-free and
3
3
  // storage-agnostic: the pure `Category` data + a host-injected `CategoryStore`
4
4
  // contract, mirroring how `cwip`'s `AuditSink` and cursedshell's `NotesStore`
@@ -10,6 +10,8 @@ import {
10
10
  matchTypeahead,
11
11
  parentIdOf,
12
12
  resolveDrop,
13
+ resolveKeyboardMove,
14
+ describeDrop,
13
15
  } from './fileTreeModel';
14
16
 
15
17
  /**
@@ -326,3 +328,135 @@ describe('filterFileTree', () => {
326
328
  expect(filterFileTree(TREE, ' ')).toBe(TREE);
327
329
  });
328
330
  });
331
+
332
+ /**
333
+ * ⌥→ / ⌥← — the keyboard's half of the drag, and the only half touch has.
334
+ *
335
+ * 🔴 The assertions that matter are the REFUSALS. A keyboard route that can reach a
336
+ * destination the pointer refuses is a second, weaker opinion about what is legal, and it is
337
+ * the one nobody looks at because the pointer is what gets tested by hand.
338
+ */
339
+ describe('resolveKeyboardMove', () => {
340
+ const rows = flattenFileTree(TREE, []);
341
+
342
+ test('→ demotes into the nearest FOLDER above it among its own siblings', () => {
343
+ // `posters` sits under `simpsons` after `screencaps`; the only unambiguous "in" is the
344
+ // folder immediately above it at the same level.
345
+ expect(
346
+ resolveKeyboardMove({
347
+ nodes: TREE,
348
+ rows,
349
+ row: rowFor(rows, 'simpsons/posters'),
350
+ direction: 'in',
351
+ }).target,
352
+ ).toEqual({ kind: 'into', parentId: 'simpsons/screencaps' });
353
+ });
354
+
355
+ test('→ with nothing above it says so rather than doing nothing', () => {
356
+ const offer = resolveKeyboardMove({
357
+ nodes: TREE,
358
+ rows,
359
+ row: rowFor(rows, 'simpsons'),
360
+ direction: 'in',
361
+ });
362
+ expect(offer.target).toBeNull();
363
+ expect(offer.reason).toBe('There is no folder above it to move into');
364
+ });
365
+
366
+ test('🔴 → refuses a LOCKED folder with the consumer‘s words, exactly as a drop does', () => {
367
+ // `dinos` is `acceptsDrop: false`. `travels` is the row after it, so `dinos` is the
368
+ // nearest folder above — reachable by keyboard and refused by pointer would be the bug.
369
+ const offer = resolveKeyboardMove({
370
+ nodes: TREE,
371
+ rows,
372
+ row: rowFor(rows, 'travels'),
373
+ direction: 'in',
374
+ lockedReason: 'That album is locked',
375
+ });
376
+ expect(offer.target).toBeNull();
377
+ expect(offer.reason).toBe('That album is locked');
378
+ });
379
+
380
+ test('← promotes to the folder its own folder is in', () => {
381
+ expect(
382
+ resolveKeyboardMove({
383
+ nodes: TREE,
384
+ rows,
385
+ row: rowFor(rows, 'simpsons/screencaps/s04'),
386
+ direction: 'out',
387
+ }).target,
388
+ ).toEqual({ kind: 'into', parentId: 'simpsons' });
389
+ });
390
+
391
+ test('← at depth 1 is the top level, and at depth 0 is a refusal with words', () => {
392
+ expect(
393
+ resolveKeyboardMove({
394
+ nodes: TREE,
395
+ rows,
396
+ row: rowFor(rows, 'simpsons/posters'),
397
+ direction: 'out',
398
+ }).target,
399
+ ).toEqual({ kind: 'root' });
400
+ const offer = resolveKeyboardMove({
401
+ nodes: TREE,
402
+ rows,
403
+ row: rowFor(rows, 'travels'),
404
+ direction: 'out',
405
+ });
406
+ expect(offer.target).toBeNull();
407
+ expect(offer.reason).toBe('It is already at the top level');
408
+ });
409
+
410
+ test('🔴 the consumer‘s `canDrop` gets the last word in both directions', () => {
411
+ const canDrop = () => 'No route for that on the server';
412
+ for (const direction of ['in', 'out'] as const) {
413
+ const offer = resolveKeyboardMove({
414
+ nodes: TREE,
415
+ rows,
416
+ row: rowFor(rows, 'simpsons/posters'),
417
+ direction,
418
+ canDrop,
419
+ });
420
+ expect(offer.target).toBeNull();
421
+ expect(offer.reason).toBe('No route for that on the server');
422
+ }
423
+ });
424
+
425
+ test('a shut folder does not hide its own SIBLINGS, so the level is still whole', () => {
426
+ // The visible rows are what the level is read off. `screencaps` being shut removes its
427
+ // children, never its peers — which is what makes reading siblings off `rows` correct.
428
+ const shut = flattenFileTree(TREE, ['simpsons/screencaps']);
429
+ expect(
430
+ resolveKeyboardMove({
431
+ nodes: TREE,
432
+ rows: shut,
433
+ row: rowFor(shut, 'simpsons/posters'),
434
+ direction: 'in',
435
+ }).target,
436
+ ).toEqual({ kind: 'into', parentId: 'simpsons/screencaps' });
437
+ });
438
+ });
439
+
440
+ describe('describeDrop', () => {
441
+ test('names the destination, because "where to" is the question a drag asks', () => {
442
+ expect(describeDrop(TREE, { kind: 'into', parentId: 'simpsons/screencaps' })).toBe(
443
+ 'Into screencaps',
444
+ );
445
+ expect(describeDrop(TREE, { kind: 'root' })).toBe('To the top level');
446
+ expect(describeDrop(TREE, { kind: 'before', siblingId: 'dinos', parentId: null })).toBe(
447
+ 'Above Dinos',
448
+ );
449
+ expect(describeDrop(TREE, { kind: 'after', siblingId: 'dinos', parentId: null })).toBe(
450
+ 'Below Dinos',
451
+ );
452
+ });
453
+
454
+ test('🔴 no target is SILENT — a refusal has its own words and must not be doubled', () => {
455
+ expect(describeDrop(TREE, null)).toBe('');
456
+ });
457
+
458
+ test('a target the tree no longer holds still says something true', () => {
459
+ // The tree can be re-derived from the server between the press and the release.
460
+ expect(describeDrop(TREE, { kind: 'into', parentId: 'gone' })).toBe('Into this folder');
461
+ });
462
+ });