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.
- package/dist/client/index.js +1 -1
- package/dist/core/category/index.d.ts.map +1 -1
- package/dist/core/category/index.js +2 -1
- package/dist/core/category/index.js.map +1 -1
- package/dist/core/category/types.js +1 -1
- package/dist/core/category/types.js.map +1 -1
- package/dist/core/file-tree/fileTreeModel.d.ts +49 -0
- package/dist/core/file-tree/fileTreeModel.d.ts.map +1 -1
- package/dist/core/file-tree/fileTreeModel.js +84 -0
- package/dist/core/file-tree/fileTreeModel.js.map +1 -1
- package/dist/core/layout/index.js +1 -1
- package/dist/core/layout/index.js.map +1 -1
- package/dist/core/media/index.js +1 -1
- package/dist/core/media/index.js.map +1 -1
- package/dist/core/media/types.js +1 -1
- package/dist/core/media/types.js.map +1 -1
- package/dist/core/media/uploadLimits.js +1 -1
- package/dist/core/media/uploadLimits.js.map +1 -1
- package/dist/core/sharing/model.d.ts +1 -1
- package/dist/ctgr/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/react/activity-bar/ActivityBar.d.ts +17 -0
- package/dist/react/activity-bar/ActivityBar.d.ts.map +1 -1
- package/dist/react/activity-bar/ActivityBar.js.map +1 -1
- package/dist/react/analytics/index.d.ts +1 -1
- package/dist/react/analytics/index.js +1 -1
- package/dist/react/chat/MessageComposer.d.ts.map +1 -1
- package/dist/react/chat/MessageComposer.js +5 -4
- package/dist/react/chat/MessageComposer.js.map +1 -1
- package/dist/react/components/Button.d.ts.map +1 -1
- package/dist/react/components/Button.js +3 -2
- package/dist/react/components/Button.js.map +1 -1
- package/dist/react/components/Checkbox.d.ts +19 -3
- package/dist/react/components/Checkbox.d.ts.map +1 -1
- package/dist/react/components/Checkbox.js +29 -12
- package/dist/react/components/Checkbox.js.map +1 -1
- package/dist/react/components/CodeArea.d.ts.map +1 -1
- package/dist/react/components/CodeArea.js +2 -1
- package/dist/react/components/CodeArea.js.map +1 -1
- package/dist/react/components/Collapsible.d.ts.map +1 -1
- package/dist/react/components/Collapsible.js +2 -1
- package/dist/react/components/Collapsible.js.map +1 -1
- package/dist/react/components/ComboboxField.d.ts.map +1 -1
- package/dist/react/components/ComboboxField.js +3 -2
- package/dist/react/components/ComboboxField.js.map +1 -1
- package/dist/react/components/CopyButton.d.ts.map +1 -1
- package/dist/react/components/CopyButton.js +2 -1
- package/dist/react/components/CopyButton.js.map +1 -1
- package/dist/react/components/DataTable/ColumnChecklist.d.ts.map +1 -1
- package/dist/react/components/DataTable/ColumnChecklist.js +2 -1
- package/dist/react/components/DataTable/ColumnChecklist.js.map +1 -1
- package/dist/react/components/DataTable/Pagination.d.ts.map +1 -1
- package/dist/react/components/DataTable/Pagination.js +2 -1
- package/dist/react/components/DataTable/Pagination.js.map +1 -1
- package/dist/react/components/DataTable/SortHeader.d.ts.map +1 -1
- package/dist/react/components/DataTable/SortHeader.js +2 -1
- package/dist/react/components/DataTable/SortHeader.js.map +1 -1
- package/dist/react/components/DataTable/TableFilterBuilder.d.ts +61 -0
- package/dist/react/components/DataTable/TableFilterBuilder.d.ts.map +1 -0
- package/dist/react/components/DataTable/{FilterBuilder.js → TableFilterBuilder.js} +26 -3
- package/dist/react/components/DataTable/TableFilterBuilder.js.map +1 -0
- package/dist/react/components/DataTable/Toolbar.d.ts +1 -1
- package/dist/react/components/DataTable/Toolbar.js +3 -3
- package/dist/react/components/DataTable/Toolbar.js.map +1 -1
- package/dist/react/components/DataTable/cells/LinkCell.d.ts.map +1 -1
- package/dist/react/components/DataTable/cells/LinkCell.js +2 -1
- package/dist/react/components/DataTable/cells/LinkCell.js.map +1 -1
- package/dist/react/components/DataTable/index.d.ts +1 -1
- package/dist/react/components/DataTable/index.d.ts.map +1 -1
- package/dist/react/components/DataTable/index.js +4 -1
- package/dist/react/components/DataTable/index.js.map +1 -1
- package/dist/react/components/DragHandle.d.ts.map +1 -1
- package/dist/react/components/DragHandle.js +2 -1
- package/dist/react/components/DragHandle.js.map +1 -1
- package/dist/react/components/EmojiField.d.ts.map +1 -1
- package/dist/react/components/EmojiField.js +2 -1
- package/dist/react/components/EmojiField.js.map +1 -1
- package/dist/react/components/EventTimeline.d.ts +3 -2
- package/dist/react/components/EventTimeline.d.ts.map +1 -1
- package/dist/react/components/EventTimeline.js +3 -2
- package/dist/react/components/EventTimeline.js.map +1 -1
- package/dist/react/components/IconButton.d.ts +1 -1
- package/dist/react/components/IconButton.js +1 -1
- package/dist/react/components/InfoHint.d.ts.map +1 -1
- package/dist/react/components/InfoHint.js +5 -4
- package/dist/react/components/InfoHint.js.map +1 -1
- package/dist/react/components/InheritBadge.js +1 -1
- package/dist/react/components/InheritBadge.js.map +1 -1
- package/dist/react/components/InlineEditableText.d.ts.map +1 -1
- package/dist/react/components/InlineEditableText.js +2 -1
- package/dist/react/components/InlineEditableText.js.map +1 -1
- package/dist/react/components/MeterList.d.ts.map +1 -1
- package/dist/react/components/MeterList.js +2 -1
- package/dist/react/components/MeterList.js.map +1 -1
- package/dist/react/components/PathText.d.ts +2 -2
- package/dist/react/components/PathText.d.ts.map +1 -1
- package/dist/react/components/PathText.js +2 -1
- package/dist/react/components/PathText.js.map +1 -1
- package/dist/react/components/RatingField.d.ts.map +1 -1
- package/dist/react/components/RatingField.js +2 -1
- package/dist/react/components/RatingField.js.map +1 -1
- package/dist/react/components/SecretInput.js +3 -2
- package/dist/react/components/SecretInput.js.map +1 -1
- package/dist/react/components/SegmentedControl.d.ts.map +1 -1
- package/dist/react/components/SegmentedControl.js +2 -1
- package/dist/react/components/SegmentedControl.js.map +1 -1
- package/dist/react/components/StarRating.d.ts.map +1 -1
- package/dist/react/components/StarRating.js +9 -5
- package/dist/react/components/StarRating.js.map +1 -1
- package/dist/react/components/StatStrip.d.ts.map +1 -1
- package/dist/react/components/StatStrip.js +2 -1
- package/dist/react/components/StatStrip.js.map +1 -1
- package/dist/react/components/StatTile.d.ts.map +1 -1
- package/dist/react/components/StatTile.js +3 -2
- package/dist/react/components/StatTile.js.map +1 -1
- package/dist/react/components/Tabs.d.ts.map +1 -1
- package/dist/react/components/Tabs.js +2 -1
- package/dist/react/components/Tabs.js.map +1 -1
- package/dist/react/components/TruncatedText.d.ts +2 -1
- package/dist/react/components/TruncatedText.d.ts.map +1 -1
- package/dist/react/components/TruncatedText.js +2 -1
- package/dist/react/components/TruncatedText.js.map +1 -1
- package/dist/react/components/calendar/MonthGrid.d.ts.map +1 -1
- package/dist/react/components/calendar/MonthGrid.js +2 -1
- package/dist/react/components/calendar/MonthGrid.js.map +1 -1
- package/dist/react/components/calendar/WeekView.d.ts.map +1 -1
- package/dist/react/components/calendar/WeekView.js +2 -1
- package/dist/react/components/calendar/WeekView.js.map +1 -1
- package/dist/react/components/revealOnHover.d.ts.map +1 -1
- package/dist/react/components/revealOnHover.js +2 -1
- package/dist/react/components/revealOnHover.js.map +1 -1
- package/dist/react/file-tree/FileTree.d.ts +50 -1
- package/dist/react/file-tree/FileTree.d.ts.map +1 -1
- package/dist/react/file-tree/FileTree.js +234 -6
- package/dist/react/file-tree/FileTree.js.map +1 -1
- package/dist/react/file-tree/fileTree.css +52 -0
- package/dist/react/filter-rail/FilterRail.d.ts.map +1 -1
- package/dist/react/filter-rail/FilterRail.js +5 -4
- package/dist/react/filter-rail/FilterRail.js.map +1 -1
- package/dist/react/folder-tree/FolderTreeRail.d.ts.map +1 -1
- package/dist/react/folder-tree/FolderTreeRail.js +2 -1
- package/dist/react/folder-tree/FolderTreeRail.js.map +1 -1
- package/dist/react/index.d.ts +5 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +5 -1
- package/dist/react/index.js.map +1 -1
- package/dist/react/layoutEngine/index.js +1 -1
- package/dist/react/layoutEngine/index.js.map +1 -1
- package/dist/react/lib/field.d.ts.map +1 -1
- package/dist/react/lib/field.js +3 -2
- package/dist/react/lib/field.js.map +1 -1
- package/dist/react/lib/floatingStyles.d.ts.map +1 -1
- package/dist/react/lib/floatingStyles.js +2 -1
- package/dist/react/lib/floatingStyles.js.map +1 -1
- package/dist/react/lib/tapTarget.d.ts +96 -0
- package/dist/react/lib/tapTarget.d.ts.map +1 -0
- package/dist/react/lib/tapTarget.js +96 -0
- package/dist/react/lib/tapTarget.js.map +1 -0
- package/dist/react/media/VideoTrimmer.d.ts +1 -1
- package/dist/react/media/VideoTrimmer.js +1 -1
- package/dist/react/media/denseGallery.css +3 -3
- package/dist/react/media/ffmpegWasm.js +2 -2
- package/dist/react/media/ffmpegWasm.js.map +1 -1
- package/dist/react/media/videoPlayer.css +42 -0
- package/dist/react/namespace/NamespaceTargetCapsule.d.ts +2 -2
- package/dist/react/namespace/NamespaceTargetCapsule.d.ts.map +1 -1
- package/dist/react/nav/BreadcrumbTrail.d.ts.map +1 -1
- package/dist/react/nav/BreadcrumbTrail.js +4 -3
- package/dist/react/nav/BreadcrumbTrail.js.map +1 -1
- package/dist/react/nav/HubTileGrid.d.ts.map +1 -1
- package/dist/react/nav/HubTileGrid.js +2 -1
- package/dist/react/nav/HubTileGrid.js.map +1 -1
- package/dist/react/nav/SideNav.d.ts.map +1 -1
- package/dist/react/nav/SideNav.js +4 -3
- package/dist/react/nav/SideNav.js.map +1 -1
- package/dist/react/nav/SideNavCustomize.d.ts.map +1 -1
- package/dist/react/nav/SideNavCustomize.js +2 -1
- package/dist/react/nav/SideNavCustomize.js.map +1 -1
- package/dist/react/palette/CommandPalette.d.ts.map +1 -1
- package/dist/react/palette/CommandPalette.js +3 -2
- package/dist/react/palette/CommandPalette.js.map +1 -1
- package/dist/server/drivers/pooledControlStores.pg.test.js +1 -0
- package/dist/server/drivers/pooledControlStores.pg.test.js.map +1 -1
- package/dist/server/drivers/pooledIdentityStore.pg.test.js +1 -0
- package/dist/server/drivers/pooledIdentityStore.pg.test.js.map +1 -1
- package/dist/server/drivers/retentionPolicy.pg.test.js +1 -0
- package/dist/server/drivers/retentionPolicy.pg.test.js.map +1 -1
- package/dist/server/guard/argon2Binding.d.ts +1 -1
- package/dist/server/guard/argon2Binding.js +1 -1
- package/dist/server/incidents/store.d.ts +3 -3
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/server/master-lock/masterLock.d.ts +1 -1
- package/dist/server/media-bun/index.js +1 -1
- package/dist/server/media-bun/index.js.map +1 -1
- package/dist/server/paths/appDataHome.d.ts +20 -0
- package/dist/server/paths/appDataHome.d.ts.map +1 -0
- package/dist/server/paths/appDataHome.js +69 -0
- package/dist/server/paths/appDataHome.js.map +1 -0
- package/dist/server/retention/archive.d.ts +3 -4
- package/dist/server/retention/archive.d.ts.map +1 -1
- package/dist/server/retention/archive.js +20 -15
- package/dist/server/retention/archive.js.map +1 -1
- package/dist/server/retention/index.d.ts +1 -1
- package/dist/server/retention/index.js +1 -1
- package/dist/server/retention/policy.d.ts +2 -2
- package/dist/server/retention/policy.d.ts.map +1 -1
- package/dist/server/retention/policy.js +1 -1
- package/dist/server/retention/store.d.ts.map +1 -1
- package/dist/server/retention/store.js +27 -20
- package/dist/server/retention/store.js.map +1 -1
- package/dist/server/runtime/localHost.d.ts +10 -5
- package/dist/server/runtime/localHost.d.ts.map +1 -1
- package/dist/server/runtime/localHost.js +12 -8
- package/dist/server/runtime/localHost.js.map +1 -1
- package/dist/server/runtime/meterRollup.pg.test.js +1 -0
- package/dist/server/runtime/meterRollup.pg.test.js.map +1 -1
- package/dist/server/runtime/recipe.d.ts +4 -4
- package/dist/server/runtime/retentionJob.pg.test.js +1 -0
- package/dist/server/runtime/retentionJob.pg.test.js.map +1 -1
- package/dist/server/search/pgSearchProvider.pg.test.js +1 -0
- package/dist/server/search/pgSearchProvider.pg.test.js.map +1 -1
- package/dist/shared/errors.d.ts +1 -1
- package/dist/shared/errors.js +1 -1
- package/dist/styles-static.css +391 -0
- package/dist/styles.css +27 -0
- package/package.json +19 -167
- package/scripts/generateStaticStyles.ts +122 -0
- package/scripts/publicSurface.ts +455 -0
- package/scripts/testTmpRoot.ts +34 -5
- package/src/client/index.ts +1 -1
- package/src/core/category/index.ts +2 -1
- package/src/core/category/types.ts +1 -1
- package/src/core/file-tree/fileTreeModel.spec.ts +134 -0
- package/src/core/file-tree/fileTreeModel.ts +104 -0
- package/src/core/layout/index.ts +1 -1
- package/src/core/media/index.ts +1 -1
- package/src/core/media/types.ts +1 -1
- package/src/core/media/uploadLimits.ts +1 -1
- package/src/core/sharing/model.ts +1 -1
- package/src/ctgr/index.ts +1 -1
- package/src/declaredDepsAreImported.spec.ts +87 -0
- package/src/demoFixture.spec.ts +133 -0
- package/src/index.ts +1 -1
- package/src/lockfileIsTracked.spec.ts +50 -0
- package/src/namedSubpathsResolve.spec.ts +119 -0
- package/src/publicSurface.spec.ts +72 -0
- package/src/react/activity-bar/ActivityBar.tsx +17 -0
- package/src/react/analytics/index.ts +1 -1
- package/src/react/chat/MessageComposer.tsx +5 -4
- package/src/react/components/Button.tsx +3 -2
- package/src/react/components/Checkbox.spec.tsx +51 -0
- package/src/react/components/Checkbox.tsx +68 -26
- package/src/react/components/CodeArea.tsx +2 -1
- package/src/react/components/CodeChip.spec.tsx +2 -2
- package/src/react/components/Collapsible.tsx +2 -1
- package/src/react/components/ComboboxField.tsx +3 -2
- package/src/react/components/CopyButton.tsx +2 -1
- package/src/react/components/CopyableText.spec.tsx +1 -1
- package/src/react/components/DataTable/ColumnChecklist.tsx +2 -1
- package/src/react/components/DataTable/Pagination.tsx +2 -1
- package/src/react/components/DataTable/SortHeader.tsx +2 -1
- package/src/react/components/DataTable/{FilterBuilder.tsx → TableFilterBuilder.tsx} +33 -5
- package/src/react/components/DataTable/Toolbar.tsx +3 -3
- package/src/react/components/DataTable/cells/LinkCell.tsx +2 -1
- package/src/react/components/DataTable/index.ts +8 -1
- package/src/react/components/DragHandle.tsx +2 -1
- package/src/react/components/EmojiField.tsx +2 -1
- package/src/react/components/EventTimeline.tsx +3 -2
- package/src/react/components/FileLocation.spec.tsx +1 -1
- package/src/react/components/IconButton.tsx +1 -1
- package/src/react/components/InfoHint.tsx +5 -4
- package/src/react/components/InheritBadge.tsx +1 -1
- package/src/react/components/InlineEditableText.tsx +2 -1
- package/src/react/components/MeterList.tsx +2 -1
- package/src/react/components/PathText.tsx +4 -3
- package/src/react/components/RatingField.tsx +2 -1
- package/src/react/components/SecretInput.tsx +3 -3
- package/src/react/components/SegmentedControl.tsx +2 -2
- package/src/react/components/StarRating.spec.tsx +2 -2
- package/src/react/components/StarRating.tsx +9 -5
- package/src/react/components/StatStrip.tsx +2 -1
- package/src/react/components/StatTile.tsx +3 -3
- package/src/react/components/Tabs.spec.tsx +3 -2
- package/src/react/components/Tabs.tsx +2 -1
- package/src/react/components/TruncatedText.tsx +2 -1
- package/src/react/components/calendar/MonthGrid.spec.tsx +2 -1
- package/src/react/components/calendar/MonthGrid.tsx +2 -1
- package/src/react/components/calendar/WeekView.tsx +2 -1
- package/src/react/components/revealOnHover.ts +3 -2
- package/src/react/file-tree/FileTree.spec.tsx +248 -0
- package/src/react/file-tree/FileTree.tsx +304 -4
- package/src/react/file-tree/fileTree.css +52 -0
- package/src/react/file-tree/fileTreeGeometry.fixture.json +151 -0
- package/src/react/file-tree/fileTreeGeometry.spec.ts +33 -52
- package/src/react/filter-rail/FilterRail.spec.tsx +2 -1
- package/src/react/filter-rail/FilterRail.tsx +5 -4
- package/src/react/folder-tree/FolderTreeRail.tsx +2 -1
- package/src/react/index.ts +13 -3
- package/src/react/layoutEngine/index.ts +1 -1
- package/src/react/layoutEngine/types.ts +1 -1
- package/src/react/lib/field.ts +4 -2
- package/src/react/lib/floatingStyles.spec.ts +2 -1
- package/src/react/lib/floatingStyles.ts +3 -1
- package/src/react/lib/tapTarget.ts +100 -0
- package/src/react/media/MaskPainter.tsx +2 -2
- package/src/react/media/VideoTrimmer.tsx +1 -1
- package/src/react/media/denseGallery.css +3 -3
- package/src/react/media/ffmpegWasm.ts +2 -2
- package/src/react/media/videoPlayer.css +42 -0
- package/src/react/namespace/NamespaceTargetCapsule.tsx +2 -2
- package/src/react/nav/BreadcrumbTrail.tsx +4 -3
- package/src/react/nav/HubTileGrid.tsx +2 -1
- package/src/react/nav/SideNav.tsx +4 -3
- package/src/react/nav/SideNavCustomize.tsx +2 -1
- package/src/react/palette/CommandPalette.tsx +3 -2
- package/src/server/ctgr.ts +1 -1
- package/src/server/drivers/pooledControlStores.pg.test.ts +1 -0
- package/src/server/drivers/pooledIdentityStore.pg.test.ts +1 -0
- package/src/server/drivers/retentionPolicy.pg.test.ts +1 -0
- package/src/server/guard/argon2Binding.ts +1 -1
- package/src/server/incidents/store.ts +3 -3
- package/src/server/index.ts +1 -1
- package/src/server/master-lock/masterLock.spec.ts +18 -2
- package/src/server/master-lock/masterLock.ts +1 -1
- package/src/server/media-bun/image.spec.ts +1 -0
- package/src/server/media-bun/index.ts +1 -1
- package/src/server/media-bun/video.spec.ts +1 -0
- package/src/server/paths/appDataHome.spec.ts +87 -0
- package/src/server/paths/appDataHome.ts +84 -0
- package/src/server/retention/archive.spec.ts +26 -4
- package/src/server/retention/archive.ts +20 -15
- package/src/server/retention/index.ts +1 -1
- package/src/server/retention/policy.ts +2 -2
- package/src/server/retention/store.spec.ts +38 -10
- package/src/server/retention/store.ts +26 -19
- package/src/server/runtime/localHost.spec.ts +22 -3
- package/src/server/runtime/localHost.ts +12 -9
- package/src/server/runtime/meterRollup.pg.test.ts +1 -0
- package/src/server/runtime/recipe.ts +4 -4
- package/src/server/runtime/retentionJob.pg.test.ts +1 -0
- package/src/server/search/pgSearchProvider.pg.test.ts +1 -0
- package/src/server/storage/binaryServerProcessing.integration.spec.ts +6 -2
- package/src/server/storage/chunked.integration.spec.ts +5 -2
- package/src/server/storage/fileUploadClient-chunked.integration.spec.ts +5 -2
- package/src/server/storage/files/adapter.photo.display.spec.ts +1 -0
- package/src/server/storage/imageThumbnail.spec.ts +1 -0
- package/src/server/storage/transcode.integration.spec.ts +5 -2
- package/src/server/storage/videoProcessor.posterOnly.spec.ts +1 -0
- package/src/shared/errors.ts +1 -1
- package/src/skippedTestsAreAnswered.spec.ts +215 -0
- package/src/styles-static.css +391 -0
- package/src/styles.css +27 -0
- package/src/stylesStaticMatches.spec.ts +134 -0
- package/src/tapTargetStylesheet.spec.ts +150 -0
- package/src/testTmpRoot.spec.ts +102 -0
- package/dist/core/duplicates/index.d.ts +0 -116
- package/dist/core/duplicates/index.d.ts.map +0 -1
- package/dist/core/duplicates/index.js +0 -178
- package/dist/core/duplicates/index.js.map +0 -1
- package/dist/core/namespace/helpers.d.ts +0 -14
- package/dist/core/namespace/helpers.d.ts.map +0 -1
- package/dist/core/namespace/helpers.js +0 -29
- package/dist/core/namespace/helpers.js.map +0 -1
- package/dist/core/namespace/index.d.ts +0 -4
- package/dist/core/namespace/index.d.ts.map +0 -1
- package/dist/core/namespace/index.js +0 -6
- package/dist/core/namespace/index.js.map +0 -1
- package/dist/core/namespace/memoryStore.d.ts +0 -19
- package/dist/core/namespace/memoryStore.d.ts.map +0 -1
- package/dist/core/namespace/memoryStore.js +0 -70
- package/dist/core/namespace/memoryStore.js.map +0 -1
- package/dist/core/namespace/types.d.ts +0 -44
- package/dist/core/namespace/types.d.ts.map +0 -1
- package/dist/core/namespace/types.js +0 -8
- package/dist/core/namespace/types.js.map +0 -1
- package/dist/game/core/DisposeRegistry.d.ts +0 -42
- package/dist/game/core/DisposeRegistry.d.ts.map +0 -1
- package/dist/game/core/DisposeRegistry.js +0 -67
- package/dist/game/core/DisposeRegistry.js.map +0 -1
- package/dist/game/core/drawBudget.d.ts +0 -26
- package/dist/game/core/drawBudget.d.ts.map +0 -1
- package/dist/game/core/drawBudget.js +0 -55
- package/dist/game/core/drawBudget.js.map +0 -1
- package/dist/game/core/fixedTimestep.d.ts +0 -38
- package/dist/game/core/fixedTimestep.d.ts.map +0 -1
- package/dist/game/core/fixedTimestep.js +0 -47
- package/dist/game/core/fixedTimestep.js.map +0 -1
- package/dist/game/core/index.d.ts +0 -9
- package/dist/game/core/index.d.ts.map +0 -1
- package/dist/game/core/index.js +0 -8
- package/dist/game/core/index.js.map +0 -1
- package/dist/game/core/inputCollector.d.ts +0 -82
- package/dist/game/core/inputCollector.d.ts.map +0 -1
- package/dist/game/core/inputCollector.js +0 -176
- package/dist/game/core/inputCollector.js.map +0 -1
- package/dist/game/core/resizeCanvas.d.ts +0 -19
- package/dist/game/core/resizeCanvas.d.ts.map +0 -1
- package/dist/game/core/resizeCanvas.js +0 -29
- package/dist/game/core/resizeCanvas.js.map +0 -1
- package/dist/game/react/index.d.ts +0 -3
- package/dist/game/react/index.d.ts.map +0 -1
- package/dist/game/react/index.js +0 -5
- package/dist/game/react/index.js.map +0 -1
- package/dist/game/react/useGameEngine.d.ts +0 -64
- package/dist/game/react/useGameEngine.d.ts.map +0 -1
- package/dist/game/react/useGameEngine.js +0 -78
- package/dist/game/react/useGameEngine.js.map +0 -1
- package/dist/react/canvas/CanvasNavHUD.d.ts +0 -64
- package/dist/react/canvas/CanvasNavHUD.d.ts.map +0 -1
- package/dist/react/canvas/CanvasNavHUD.js +0 -58
- package/dist/react/canvas/CanvasNavHUD.js.map +0 -1
- package/dist/react/canvas/fitLabel.d.ts +0 -53
- package/dist/react/canvas/fitLabel.d.ts.map +0 -1
- package/dist/react/canvas/fitLabel.js +0 -104
- package/dist/react/canvas/fitLabel.js.map +0 -1
- package/dist/react/canvas/index.d.ts +0 -4
- package/dist/react/canvas/index.d.ts.map +0 -1
- package/dist/react/canvas/index.js +0 -4
- package/dist/react/canvas/index.js.map +0 -1
- package/dist/react/canvas/lod.d.ts +0 -40
- package/dist/react/canvas/lod.d.ts.map +0 -1
- package/dist/react/canvas/lod.js +0 -37
- package/dist/react/canvas/lod.js.map +0 -1
- package/dist/react/components/DataTable/FilterBuilder.d.ts +0 -38
- package/dist/react/components/DataTable/FilterBuilder.d.ts.map +0 -1
- package/dist/react/components/DataTable/FilterBuilder.js.map +0 -1
- package/dist/react/seo/HeadProvider.d.ts +0 -14
- package/dist/react/seo/HeadProvider.d.ts.map +0 -1
- package/dist/react/seo/HeadProvider.js +0 -44
- package/dist/react/seo/HeadProvider.js.map +0 -1
- package/dist/react/seo/applyHeadToDocument.d.ts +0 -3
- package/dist/react/seo/applyHeadToDocument.d.ts.map +0 -1
- package/dist/react/seo/applyHeadToDocument.js +0 -66
- package/dist/react/seo/applyHeadToDocument.js.map +0 -1
- package/dist/react/seo/index.d.ts +0 -3
- package/dist/react/seo/index.d.ts.map +0 -1
- package/dist/react/seo/index.js +0 -7
- package/dist/react/seo/index.js.map +0 -1
- package/dist/react/timeline/ClipView.d.ts +0 -29
- package/dist/react/timeline/ClipView.d.ts.map +0 -1
- package/dist/react/timeline/ClipView.js +0 -53
- package/dist/react/timeline/ClipView.js.map +0 -1
- package/dist/react/timeline/Playhead.d.ts +0 -16
- package/dist/react/timeline/Playhead.d.ts.map +0 -1
- package/dist/react/timeline/Playhead.js +0 -26
- package/dist/react/timeline/Playhead.js.map +0 -1
- package/dist/react/timeline/TimeRuler.d.ts +0 -21
- package/dist/react/timeline/TimeRuler.d.ts.map +0 -1
- package/dist/react/timeline/TimeRuler.js +0 -31
- package/dist/react/timeline/TimeRuler.js.map +0 -1
- package/dist/react/timeline/TimelineSurface.d.ts +0 -29
- package/dist/react/timeline/TimelineSurface.d.ts.map +0 -1
- package/dist/react/timeline/TimelineSurface.js +0 -56
- package/dist/react/timeline/TimelineSurface.js.map +0 -1
- package/dist/react/timeline/TrackLane.d.ts +0 -31
- package/dist/react/timeline/TrackLane.d.ts.map +0 -1
- package/dist/react/timeline/TrackLane.js +0 -32
- package/dist/react/timeline/TrackLane.js.map +0 -1
- package/dist/react/timeline/index.d.ts +0 -6
- package/dist/react/timeline/index.d.ts.map +0 -1
- package/dist/react/timeline/index.js +0 -12
- package/dist/react/timeline/index.js.map +0 -1
- package/dist/server/private-media/index.d.ts +0 -7
- package/dist/server/private-media/index.d.ts.map +0 -1
- package/dist/server/private-media/index.js +0 -13
- package/dist/server/private-media/index.js.map +0 -1
- package/dist/server/seo/index.d.ts +0 -3
- package/dist/server/seo/index.d.ts.map +0 -1
- package/dist/server/seo/index.js +0 -6
- package/dist/server/seo/index.js.map +0 -1
- package/dist/server/seo/mountSeoRoutes.d.ts +0 -9
- package/dist/server/seo/mountSeoRoutes.d.ts.map +0 -1
- package/dist/server/seo/mountSeoRoutes.js +0 -19
- package/dist/server/seo/mountSeoRoutes.js.map +0 -1
- package/dist/server/seo/serveHeadInjectedIndex.d.ts +0 -9
- package/dist/server/seo/serveHeadInjectedIndex.d.ts.map +0 -1
- package/dist/server/seo/serveHeadInjectedIndex.js +0 -20
- package/dist/server/seo/serveHeadInjectedIndex.js.map +0 -1
- package/dist/upload.d.ts +0 -148
- package/dist/upload.d.ts.map +0 -1
- package/dist/upload.js +0 -159
- package/dist/upload.js.map +0 -1
- package/dist/verify/runtime/index.d.ts +0 -16
- package/dist/verify/runtime/index.d.ts.map +0 -1
- package/dist/verify/runtime/index.js +0 -16
- package/dist/verify/runtime/index.js.map +0 -1
- package/scripts/demoCheck.ts +0 -42
- package/src/core/duplicates/index.spec.ts +0 -116
- package/src/core/duplicates/index.ts +0 -203
- package/src/core/namespace/helpers.ts +0 -31
- package/src/core/namespace/index.ts +0 -12
- package/src/core/namespace/memoryStore.ts +0 -77
- package/src/core/namespace/namespace.spec.ts +0 -117
- package/src/core/namespace/types.ts +0 -53
- package/src/game/core/DisposeRegistry.spec.ts +0 -53
- package/src/game/core/DisposeRegistry.ts +0 -70
- package/src/game/core/drawBudget.spec.ts +0 -51
- package/src/game/core/drawBudget.ts +0 -75
- package/src/game/core/fixedTimestep.spec.ts +0 -91
- package/src/game/core/fixedTimestep.ts +0 -79
- package/src/game/core/index.ts +0 -16
- package/src/game/core/inputCollector.spec.ts +0 -29
- package/src/game/core/inputCollector.ts +0 -245
- package/src/game/core/resizeCanvas.ts +0 -31
- package/src/game/react/index.ts +0 -10
- package/src/game/react/useGameEngine.ts +0 -125
- package/src/react/canvas/CanvasNavHUD.spec.tsx +0 -62
- package/src/react/canvas/CanvasNavHUD.tsx +0 -210
- package/src/react/canvas/fitLabel.spec.ts +0 -82
- package/src/react/canvas/fitLabel.ts +0 -150
- package/src/react/canvas/index.ts +0 -15
- package/src/react/canvas/lod.ts +0 -53
- package/src/react/seo/HeadProvider.tsx +0 -67
- package/src/react/seo/applyHeadToDocument.ts +0 -73
- package/src/react/seo/index.ts +0 -12
- package/src/react/seo/seo.spec.tsx +0 -101
- package/src/react/timeline/ClipView.tsx +0 -146
- package/src/react/timeline/Playhead.tsx +0 -63
- package/src/react/timeline/TimeRuler.tsx +0 -85
- package/src/react/timeline/TimelineSurface.tsx +0 -157
- package/src/react/timeline/TrackLane.tsx +0 -87
- package/src/react/timeline/index.ts +0 -12
- package/src/react/timeline/timeline.spec.tsx +0 -175
- package/src/server/private-media/index.ts +0 -45
- package/src/server/seo/index.ts +0 -8
- package/src/server/seo/mountSeoRoutes.ts +0 -37
- package/src/server/seo/seo.spec.ts +0 -128
- package/src/server/seo/serveHeadInjectedIndex.ts +0 -36
- package/src/upload.spec.ts +0 -300
- package/src/upload.ts +0 -288
- package/src/verify/runtime/index.ts +0 -64
|
@@ -360,6 +360,110 @@ export function resolveDrop({
|
|
|
360
360
|
});
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
export interface KeyboardMoveOptions {
|
|
364
|
+
nodes: readonly FileTreeNode[];
|
|
365
|
+
/** The rows on screen — siblings are read off these, which is every sibling by construction. */
|
|
366
|
+
rows: readonly FileTreeRow[];
|
|
367
|
+
/** The row the keyboard is on. */
|
|
368
|
+
row: FileTreeRow;
|
|
369
|
+
/** `in` demotes into the nearest folder above it; `out` promotes to its grandparent. */
|
|
370
|
+
direction: 'in' | 'out';
|
|
371
|
+
lockedReason?: string;
|
|
372
|
+
canDrop?: (dragId: string | null, target: FileTreeDrop) => string | null;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Where ⌥→ / ⌥← would put the focused row — the keyboard's half of the drag.
|
|
377
|
+
*
|
|
378
|
+
* 🔴 It exists because a gesture with no keyboard route is a gesture half the people using
|
|
379
|
+
* it cannot perform, and because TOUCH has no drag here at all: a sidebar row is a small
|
|
380
|
+
* target on a surface whose one constant gesture is the flick, so the pointer drag gives
|
|
381
|
+
* that up and this is what is left. `cursedesk`'s tree has carried it since it was written;
|
|
382
|
+
* this one shipped with the drag alone.
|
|
383
|
+
*
|
|
384
|
+
* The two directions are the only two a keyboard can mean unambiguously. **→ demotes into
|
|
385
|
+
* the nearest folder ABOVE it among its own siblings** — the one place a demotion can go
|
|
386
|
+
* without asking which of several folders was meant — and **← promotes out to the folder its
|
|
387
|
+
* own folder is in**, which is `root` at depth 1.
|
|
388
|
+
*
|
|
389
|
+
* 🔴 Every refusal the DRAG honours is honoured here, from the same fields: `acceptsDrop`,
|
|
390
|
+
* the consumer's `canDrop`, and the descendant rule. A keyboard route that can reach a
|
|
391
|
+
* destination the pointer refuses is a second, weaker opinion about what is legal — and it
|
|
392
|
+
* is the one nobody looks at, because the pointer is what gets tested by hand.
|
|
393
|
+
*
|
|
394
|
+
* There is deliberately no ⌥↑/⌥↓ here. Those would move a row among its siblings, which is
|
|
395
|
+
* only meaningful when the consumer stores an order — and when it does, the sibling before
|
|
396
|
+
* and after are already reachable as `before`/`after` drops. Offering them unconditionally
|
|
397
|
+
* would write a position nothing reads and then draw the row back where it started.
|
|
398
|
+
*/
|
|
399
|
+
export function resolveKeyboardMove({
|
|
400
|
+
nodes,
|
|
401
|
+
rows,
|
|
402
|
+
row,
|
|
403
|
+
direction,
|
|
404
|
+
lockedReason = 'That folder is locked',
|
|
405
|
+
canDrop,
|
|
406
|
+
}: KeyboardMoveOptions): DropOffer {
|
|
407
|
+
const vetted = (target: FileTreeDrop): DropOffer => {
|
|
408
|
+
const reason = canDrop?.(row.node.id, target);
|
|
409
|
+
return reason
|
|
410
|
+
? { target: null, refused: true, reason }
|
|
411
|
+
: { target, refused: false, reason: null };
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
if (direction === 'out') {
|
|
415
|
+
if (row.parentId === null)
|
|
416
|
+
return { target: null, refused: true, reason: 'It is already at the top level' };
|
|
417
|
+
const grandparentId = parentIdOf(nodes, row.parentId);
|
|
418
|
+
if (grandparentId === null) return vetted({ kind: 'root' });
|
|
419
|
+
const grandparent = findFileTreeNode(nodes, grandparentId);
|
|
420
|
+
if (grandparent?.acceptsDrop === false)
|
|
421
|
+
return { target: null, refused: true, reason: lockedReason };
|
|
422
|
+
return vetted({ kind: 'into', parentId: grandparentId });
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// A row's siblings are all on screen whenever the row itself is — a shut folder hides its
|
|
426
|
+
// CHILDREN, never its peers — so the visible rows are the whole level.
|
|
427
|
+
const level = rows.filter((entry) => entry.parentId === row.parentId);
|
|
428
|
+
const above = level
|
|
429
|
+
.filter((entry) => entry.index < row.index && entry.node.kind === 'folder')
|
|
430
|
+
.at(-1);
|
|
431
|
+
if (!above)
|
|
432
|
+
return { target: null, refused: true, reason: 'There is no folder above it to move into' };
|
|
433
|
+
if (above.node.acceptsDrop === false)
|
|
434
|
+
return { target: null, refused: true, reason: lockedReason };
|
|
435
|
+
// Belt and braces: a folder cannot go inside itself, and `above` is a sibling rather than
|
|
436
|
+
// a descendant — but the rule is the tree's, not this function's, and stating it once here
|
|
437
|
+
// is cheaper than discovering the day someone changes what `above` means.
|
|
438
|
+
if (descendantIdsOf(nodes, row.node.id).has(above.node.id))
|
|
439
|
+
return { target: null, refused: true, reason: 'A folder cannot go inside itself' };
|
|
440
|
+
return vetted({ kind: 'into', parentId: above.node.id });
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Where a drop would put the thing being carried, in words — for the chip under the cursor
|
|
445
|
+
* and for the screen reader that cannot see it.
|
|
446
|
+
*
|
|
447
|
+
* 🔴 A drag that says only WHAT is held answers half the question. Owner, of the tree this
|
|
448
|
+
* one was ported from: *"dragging an item from one place to another does not have any
|
|
449
|
+
* indication that it is working until you drop it somewhere new and see it move."* The chip
|
|
450
|
+
* following the cursor answers "it is working"; this answers "and it is going there", which
|
|
451
|
+
* is what stops a wrong drop before the pointer is lifted rather than after.
|
|
452
|
+
*
|
|
453
|
+
* Empty for no target — a refusal has its own words and this must never carry a second "no".
|
|
454
|
+
*/
|
|
455
|
+
export function describeDrop(nodes: readonly FileTreeNode[], drop: FileTreeDrop | null): string {
|
|
456
|
+
if (!drop) return '';
|
|
457
|
+
if (drop.kind === 'root') return 'To the top level';
|
|
458
|
+
if (drop.kind === 'into') {
|
|
459
|
+
const folder = findFileTreeNode(nodes, drop.parentId);
|
|
460
|
+
return folder ? `Into ${folder.name}` : 'Into this folder';
|
|
461
|
+
}
|
|
462
|
+
const sibling = findFileTreeNode(nodes, drop.siblingId);
|
|
463
|
+
const where = drop.kind === 'before' ? 'Above' : 'Below';
|
|
464
|
+
return sibling ? `${where} ${sibling.name}` : `${where} this row`;
|
|
465
|
+
}
|
|
466
|
+
|
|
363
467
|
/**
|
|
364
468
|
* The next row whose name starts with `query`, searching from `fromIndex` and wrapping.
|
|
365
469
|
*
|
package/src/core/layout/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//
|
|
1
|
+
// src/core/layout — re-export of `cwip/layout`, the single source of truth for the
|
|
2
2
|
// framework-agnostic layout/widget engine core (v2 node model + idempotent migration,
|
|
3
3
|
// pure treeOps, aggregates, container-query grid/style class maps, binding resolution,
|
|
4
4
|
// undo/redo history, wire version). cursedbelt's copy was absorbed into cwip
|
package/src/core/media/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cursedbelt/media-core — pure, dependency-free media contract + utilities shared by
|
|
2
|
-
// the Bun server adapter (`
|
|
2
|
+
// the Bun server adapter (`src/server/media-bun`), the React components
|
|
3
3
|
// (`cursedbelt/react/media`), and any consuming app/test. No React, no Bun, no npm
|
|
4
4
|
// dep — the same module loads identically everywhere.
|
|
5
5
|
|
package/src/core/media/types.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// cursedbelt media-core — the pure, dependency-free contract shared by the Bun
|
|
2
|
-
// server adapter (`
|
|
2
|
+
// server adapter (`src/server/media-bun`), the React components
|
|
3
3
|
// (`cursedbelt/react/media`), and any app/test. ZERO npm deps: types + pure
|
|
4
4
|
// functions only, so the exact same module loads identically in Bun, the browser,
|
|
5
5
|
// and a unit test.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Upload size limits — the ONE source of truth shared by the upload client
|
|
2
2
|
// (`cursedbelt/react/media` fileUploadClient) and the server file routes
|
|
3
|
-
// (`cursedbelt/server/storage
|
|
3
|
+
// (`cursedbelt/server/storage`). Pure constants, no deps, so both the browser
|
|
4
4
|
// and Bun sides import the same numbers and can never drift.
|
|
5
5
|
//
|
|
6
6
|
// The law: NO single HTTP request may carry more than MAX_SINGLE_UPLOAD_BYTES of body.
|
|
@@ -74,7 +74,7 @@ export interface ShareResource {
|
|
|
74
74
|
}
|
|
75
75
|
|
|
76
76
|
/** One grant. `grantedAt` is an ISO-8601 string, matching the fleet's audit
|
|
77
|
-
* vocabulary (`cursedbelt/server
|
|
77
|
+
* vocabulary (the audit module behind `cursedbelt/server`) rather than the epoch-millis apps use
|
|
78
78
|
* for their own rows — an audit trail is read by humans far more often than it is
|
|
79
79
|
* sorted by a machine. */
|
|
80
80
|
export interface ShareGrant {
|
package/src/ctgr/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*
|
|
2
|
-
*
|
|
2
|
+
* src/ctgr — the isomorphic ctgr codec, re-exported from `cursedbelt/server`; (browser + Bun, no runtime deps). The
|
|
3
3
|
* Hono transport binding (`unGETify` + `buildRoutes`) lives in `cursedbelt/server`;
|
|
4
4
|
* the browser guard + request converter live in `cursedbelt/client`.
|
|
5
5
|
*
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import pkg from '../package.json';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Every declared dependency must be reachable from an import (2026-09-13).
|
|
6
|
+
*
|
|
7
|
+
* `dependencies` is not advice — a consumer installs all of it to render one
|
|
8
|
+
* button. `peerDependencies` is a promise that installing the peer lights a
|
|
9
|
+
* feature up. Both rot silently: a component gets deleted, its package stays
|
|
10
|
+
* declared, and nothing anywhere reds. At the time this was written the manifest
|
|
11
|
+
* carried three runtime deps and five optional peers that no file imported.
|
|
12
|
+
*
|
|
13
|
+
* 🔴 The reason this matches an IMPORT SPECIFIER and scans EVERY file type,
|
|
14
|
+
* rather than grepping .ts for the bare name:
|
|
15
|
+
*
|
|
16
|
+
* - A .ts-only grep says `tw-animate-css` is dead. It is not — it is pulled in
|
|
17
|
+
* from CSS (`@import` in src/styles.css), and deleting it would have shipped
|
|
18
|
+
* a package whose stylesheet cannot resolve. That is the exact mistake this
|
|
19
|
+
* session made on its first pass.
|
|
20
|
+
* - A bare-name grep says everything is alive, because names like `diff` and
|
|
21
|
+
* `marked` appear in prose in hundreds of files. A gate that cannot fail is
|
|
22
|
+
* not a gate.
|
|
23
|
+
*
|
|
24
|
+
* devDependencies are deliberately NOT checked: several are installed only so
|
|
25
|
+
* this repo can typecheck and test an OPTIONAL peer's integration, so "nothing
|
|
26
|
+
* imports it by name" is the expected state there, not a defect.
|
|
27
|
+
*/
|
|
28
|
+
const ROOTS = ['src', 'scripts'] as const;
|
|
29
|
+
|
|
30
|
+
// Reading a font or a screenshot as text costs time and can never match.
|
|
31
|
+
const BINARY = /\.(png|jpe?g|gif|webp|avif|ico|mp4|webm|mp3|wav|pdf|zip|gz|woff2?|ttf|otf|eot|wasm)$/i;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `from 'x'` · `import 'x/y.css'` · `import('x')` · `require('x')` · `@import "x"`,
|
|
35
|
+
* with or without a subpath. Anchored on the specifier quote so a name mentioned
|
|
36
|
+
* in prose or in a comment does not count as a use.
|
|
37
|
+
*/
|
|
38
|
+
const RE_SPECIAL = /[.*+?^${}()|[\]\\]/g;
|
|
39
|
+
|
|
40
|
+
const importsPackage = (name: string): RegExp =>
|
|
41
|
+
new RegExp(`(?:from|import|require|@import)\\s*\\(?\\s*['"]${name.replace(RE_SPECIAL, '\\$&')}(?:/[^'"]*)?['"]`);
|
|
42
|
+
|
|
43
|
+
// 🔴 Bun.Glob + Bun.file, NOT node:fs — this package's test preload virtualizes
|
|
44
|
+
// node:fs, so a readdirSync/readFileSync scan reads MOCK data and passes against
|
|
45
|
+
// files it never opened. Same trap as publishShape.spec.ts; see its header.
|
|
46
|
+
let cache: Promise<string[]> | undefined;
|
|
47
|
+
const sources = (): Promise<string[]> => {
|
|
48
|
+
cache ??= (async () => {
|
|
49
|
+
const repo = `${import.meta.dir}/..`;
|
|
50
|
+
const texts: string[] = [];
|
|
51
|
+
for (const root of ROOTS) {
|
|
52
|
+
const dir = `${repo}/${root}`;
|
|
53
|
+
for (const rel of new Bun.Glob('**/*').scanSync({ cwd: dir, onlyFiles: true })) {
|
|
54
|
+
if (BINARY.test(rel)) continue;
|
|
55
|
+
texts.push(await Bun.file(`${dir}/${rel}`).text());
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return texts;
|
|
59
|
+
})();
|
|
60
|
+
return cache;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const unimported = async (block: Record<string, string> | undefined): Promise<string[]> => {
|
|
64
|
+
const texts = await sources();
|
|
65
|
+
return Object.keys(block ?? {}).filter((name) => {
|
|
66
|
+
const re = importsPackage(name);
|
|
67
|
+
return !texts.some((t) => re.test(t));
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
describe('declared dependencies are imported', () => {
|
|
72
|
+
it('has scanned a real source tree', async () => {
|
|
73
|
+
// Guards the whole file: if the glob or the fs mock ever returns nothing,
|
|
74
|
+
// every assertion below passes vacuously and the gate quietly dies.
|
|
75
|
+
expect((await sources()).length).toBeGreaterThan(100);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
for (const field of ['dependencies', 'peerDependencies'] as const) {
|
|
79
|
+
it(`${field} names nothing the code never imports`, async () => {
|
|
80
|
+
const dead = await unimported((pkg as Record<string, unknown>)[field] as Record<string, string>);
|
|
81
|
+
expect(
|
|
82
|
+
dead,
|
|
83
|
+
`declared in ${field} but imported nowhere in ${ROOTS.join('/')} — import it or drop it:\n ${dead.join('\n ')}`,
|
|
84
|
+
).toEqual([]);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
});
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import {
|
|
5
|
+
DEMO_COMMAND,
|
|
6
|
+
DEMO_DEV_SCRIPT,
|
|
7
|
+
DEMO_PACKAGE_DIR,
|
|
8
|
+
DEMO_PORT,
|
|
9
|
+
DEMO_ROOT,
|
|
10
|
+
DEMO_SERVE_SCRIPT,
|
|
11
|
+
DEMO_URL,
|
|
12
|
+
} from '../demo/devServer';
|
|
13
|
+
import { DEMO_TSCONFIG, derivePaths, readDemoTsconfig, renderPathsBlock } from '../demo/generatePaths';
|
|
14
|
+
import pkg from '../package.json';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* 🔴 The e2e suite must never be able to sit green while it drives nothing.
|
|
18
|
+
*
|
|
19
|
+
* For the whole life of this repo, `e2e/playwright.config.ts` pointed its `webServer`
|
|
20
|
+
* at `cursedbelt/demo` — a directory that did not exist, because the kitchen-sink demo
|
|
21
|
+
* was never carried across from `cursed-core` when the package was extracted. All 18
|
|
22
|
+
* specs failed at web-server startup, and NOTHING noticed: `verify` was
|
|
23
|
+
* `paths && typecheck && build && test`, and `bun test` only runs `src`. So 2,400 lines
|
|
24
|
+
* of real assertions — including the `DESIGN.md` §5 44×44 touch-target floor, which the
|
|
25
|
+
* `touch` Playwright project exists specifically to prove and which no unit test can
|
|
26
|
+
* check, because `(pointer: coarse)` is about the input DEVICE — read as coverage the
|
|
27
|
+
* repo did not have.
|
|
28
|
+
*
|
|
29
|
+
* `bun run verify` now ends in `bun run e2e`, which is the real proof. These assertions
|
|
30
|
+
* are the cheap half: they fail in milliseconds naming the exact missing path instead of
|
|
31
|
+
* after a 120s web-server timeout, and they would have caught the original defect on the
|
|
32
|
+
* day the package was extracted.
|
|
33
|
+
*
|
|
34
|
+
* Note the blunt case: if `demo/` is removed OUTRIGHT this file cannot even load
|
|
35
|
+
* (`Cannot find module '../demo/devServer'`) and `bun test` fails on it. That is the
|
|
36
|
+
* intended red — the imports above are the coupling, not an accident of layout.
|
|
37
|
+
*/
|
|
38
|
+
const repoRoot = resolve(import.meta.dir, '..');
|
|
39
|
+
const e2eDir = resolve(repoRoot, 'e2e');
|
|
40
|
+
|
|
41
|
+
describe('the e2e fixture exists', () => {
|
|
42
|
+
it('the demo directory Playwright starts is real', () => {
|
|
43
|
+
expect(existsSync(DEMO_ROOT), `demo root does not exist: ${DEMO_ROOT}`).toBe(true);
|
|
44
|
+
expect(existsSync(resolve(DEMO_ROOT, 'vite.config.ts')), 'demo/ has no vite.config.ts').toBe(true);
|
|
45
|
+
expect(DEMO_PACKAGE_DIR, 'the demo must be started from this package').toBe(repoRoot);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('the webServer command names a script this package defines', () => {
|
|
49
|
+
// A renamed or deleted script is the other way this config goes quietly dead —
|
|
50
|
+
// the suite would fail at startup with a shell error, not a diff.
|
|
51
|
+
const scripts = Object.keys(pkg.scripts);
|
|
52
|
+
expect(DEMO_COMMAND).toBe(`bun run ${DEMO_SERVE_SCRIPT}`);
|
|
53
|
+
for (const script of [DEMO_DEV_SCRIPT, DEMO_SERVE_SCRIPT]) {
|
|
54
|
+
expect(scripts, `package.json has no "${script}" script`).toContain(script);
|
|
55
|
+
}
|
|
56
|
+
expect(pkg.scripts.demo, 'the demo script must start vite on the demo config').toInclude('demo/vite.config.ts');
|
|
57
|
+
// Playwright drives the BUILT bundle, not the dev server — see demo/devServer.ts.
|
|
58
|
+
expect(pkg.scripts['demo:serve'], 'demo:serve must build before it previews').toInclude('demo:build');
|
|
59
|
+
expect(pkg.scripts['demo:serve']).toInclude('vite preview');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('both servers serve the port the specs are pointed at', () => {
|
|
63
|
+
// vite silently walks to the next free port unless `strictPort` is set, which is
|
|
64
|
+
// the same outage wearing a costume: a server that starts perfectly well and a
|
|
65
|
+
// suite that times out against the port nobody is listening on.
|
|
66
|
+
const viteConfig = readFileSync(resolve(DEMO_ROOT, 'vite.config.ts'), 'utf8');
|
|
67
|
+
expect(DEMO_URL).toBe(`http://localhost:${DEMO_PORT}`);
|
|
68
|
+
// Once for `server` (dev), once for `preview` (what e2e drives).
|
|
69
|
+
expect(viteConfig.match(/port: DEMO_PORT/g) ?? [], 'both server and preview must pin the port').toHaveLength(2);
|
|
70
|
+
expect(viteConfig.match(/strictPort: true/g) ?? [], 'both must set strictPort').toHaveLength(2);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('every page the specs navigate to is a real demo entry point', () => {
|
|
74
|
+
const specs = readdirSync(e2eDir).filter((f) => f.endsWith('.spec.ts'));
|
|
75
|
+
expect(specs.length, 'e2e/ holds no specs — delete the config and these assertions too').toBeGreaterThan(0);
|
|
76
|
+
|
|
77
|
+
const missing: string[] = [];
|
|
78
|
+
for (const spec of specs) {
|
|
79
|
+
const text = readFileSync(resolve(e2eDir, spec), 'utf8');
|
|
80
|
+
for (const [, url] of text.matchAll(/goto\('([^']+)'\)/g)) {
|
|
81
|
+
// '/' is index.html; '/table-layout.html' is that file in demo/.
|
|
82
|
+
const file = url === '/' ? 'index.html' : url.replace(/^\//, '');
|
|
83
|
+
if (!existsSync(resolve(DEMO_ROOT, file))) missing.push(`${spec} → ${url}`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
expect(missing, `specs navigate to pages demo/ does not serve:\n ${missing.join('\n ')}`).toEqual([]);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('a built demo still emits every fixture page', () => {
|
|
90
|
+
// vite's dev server hands out any .html under its root, but a build only emits
|
|
91
|
+
// what `rollupOptions.input` lists — so a fixture page can exist, pass the
|
|
92
|
+
// assertion above, and vanish from the bundle the suite actually drives.
|
|
93
|
+
const viteConfig = readFileSync(resolve(DEMO_ROOT, 'vite.config.ts'), 'utf8');
|
|
94
|
+
const unlisted = readdirSync(DEMO_ROOT)
|
|
95
|
+
.filter((f) => f.endsWith('.html'))
|
|
96
|
+
.filter((page) => !viteConfig.includes(`'${page}'`));
|
|
97
|
+
expect(unlisted, `demo/vite.config.ts's rollup input omits:\n ${unlisted.join('\n ')}`).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('verify actually runs the suite', () => {
|
|
101
|
+
// Everything above proves the fixture resolves. Only this proves anybody looks:
|
|
102
|
+
// without it, `e2e` drops back out of the gate and this whole file becomes a
|
|
103
|
+
// comment about a suite nothing runs.
|
|
104
|
+
expect(pkg.scripts.verify, '`verify` must end in the e2e suite').toInclude('bun run e2e');
|
|
105
|
+
expect(pkg.scripts.e2e).toInclude('e2e/playwright.config.ts');
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* The demo imports cursedbelt by package specifier, and nothing installs
|
|
111
|
+
* `node_modules/cursedbelt` for it to resolve against. `demo/vite.config.ts` derives its
|
|
112
|
+
* aliases from the `exports` map at startup; tsc cannot, so `demo/tsconfig.json` carries
|
|
113
|
+
* a generated `paths` block. This keeps the generated half honest — see
|
|
114
|
+
* `demo/generatePaths.ts` for why neither resolver is allowed a hand-written list.
|
|
115
|
+
*/
|
|
116
|
+
describe("the demo's tsconfig paths track the export map", () => {
|
|
117
|
+
it('matches what the exports map derives', () => {
|
|
118
|
+
expect(readDemoTsconfig().compilerOptions.paths, 'stale — run `bun run demo:paths`').toEqual(derivePaths());
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('is rendered in the exact form `bun run demo:paths` writes', () => {
|
|
122
|
+
// Compare TEXT too: an equal-but-reformatted block would make the writer report a
|
|
123
|
+
// diff forever, which is how a generated file stops being regenerated.
|
|
124
|
+
expect(readFileSync(DEMO_TSCONFIG, 'utf8')).toInclude(renderPathsBlock(derivePaths()));
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('names only source files that exist', () => {
|
|
128
|
+
const missing = Object.entries(derivePaths())
|
|
129
|
+
.filter(([, [target]]) => !existsSync(resolve(DEMO_ROOT, target as string)))
|
|
130
|
+
.map(([specifier, [target]]) => `${specifier} → ${target}`);
|
|
131
|
+
expect(missing, `demo/tsconfig.json paths point at missing files:\n ${missing.join('\n ')}`).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* `bun run gate` at the repo root fails when `dist` is older than `src`.
|
|
19
19
|
*/
|
|
20
20
|
// cursedbelt core (framework-agnostic utilities). The React UI kit lives in
|
|
21
|
-
// `cursedbelt/react`; the isomorphic ctgr codec in `cursedbelt/
|
|
21
|
+
// `cursedbelt/react`; the isomorphic ctgr codec in `cursedbelt/server`.
|
|
22
22
|
export const VERSION = '0.0.0';
|
|
23
23
|
|
|
24
24
|
// Color helpers — framework-agnostic, pure over hex strings. `contrastText` is the
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🔴 Rule 2 of this generation: `bun install --frozen-lockfile` must pass on a
|
|
3
|
+
* CLEAN CHECKOUT — not merely in the tree an agent happens to be sitting in.
|
|
4
|
+
*
|
|
5
|
+
* ── What this cost, 2026-09-13 ──────────────────────────────────────────────
|
|
6
|
+
* `.gitignore` carried a `bun.lock` stanza with a comment explaining it was
|
|
7
|
+
* temporary: the only lockfile that could exist at the time recorded a
|
|
8
|
+
* proof-only local pack of `cwip`, and committing that would have reintroduced
|
|
9
|
+
* the `file:` dependency the package had just removed. The reasoning was sound
|
|
10
|
+
* and the expiry condition — "until cwip is on the registry" — was real. It was
|
|
11
|
+
* also written in a comment, which nothing can evaluate. `cwip@3.0.2` went to
|
|
12
|
+
* the registry and the stanza stayed, so rule 2 was true of every working tree
|
|
13
|
+
* and false of every fresh clone: exactly the case the rule exists for, and the
|
|
14
|
+
* one nobody hits until they are the person doing the clone.
|
|
15
|
+
*
|
|
16
|
+
* A temporary ignore needs a check that notices when it stopped being true.
|
|
17
|
+
* This is that check. `git check-ignore` is the whole mechanism, because the
|
|
18
|
+
* failure is not "the lockfile is missing" — it is "git has been told to throw
|
|
19
|
+
* it away", which is invisible in a tree that already has one.
|
|
20
|
+
*
|
|
21
|
+
* Deliberately NOT `git ls-files bun.lock`: a regenerated lockfile is untracked
|
|
22
|
+
* until it is staged, and a check that reds on work-in-progress teaches people
|
|
23
|
+
* to skip it. Ignored-vs-not is the property that actually regresses.
|
|
24
|
+
*/
|
|
25
|
+
import { describe, expect, test } from 'bun:test'
|
|
26
|
+
import { spawnSync } from 'node:child_process'
|
|
27
|
+
import { existsSync } from 'node:fs'
|
|
28
|
+
import { fileURLToPath } from 'node:url'
|
|
29
|
+
|
|
30
|
+
const repoRoot = fileURLToPath(new URL('..', import.meta.url))
|
|
31
|
+
|
|
32
|
+
describe('bun.lock is part of the checkout', () => {
|
|
33
|
+
test('the lockfile exists', () => {
|
|
34
|
+
expect(
|
|
35
|
+
existsSync(new URL('../bun.lock', import.meta.url)),
|
|
36
|
+
'no bun.lock — regenerate it with `bun install`, or `bun install --frozen-lockfile` cannot pass for anyone cloning this',
|
|
37
|
+
).toBe(true)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('git is not told to ignore it', () => {
|
|
41
|
+
const git = spawnSync('git', ['check-ignore', '-q', 'bun.lock'], { cwd: repoRoot })
|
|
42
|
+
if (git.error) return // no git on this host: nothing to assert about ignore rules
|
|
43
|
+
|
|
44
|
+
// exit 0 = the path IS ignored; 1 = it is not; anything else = git could not tell us.
|
|
45
|
+
expect(
|
|
46
|
+
git.status,
|
|
47
|
+
'bun.lock is gitignored, so a fresh clone has no lockfile and `bun install --frozen-lockfile` cannot pass there. Delete the stanza from .gitignore rather than the assertion.',
|
|
48
|
+
).not.toBe(0)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import pkg from '../package.json';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Every `cursedbelt/<subpath>` this repo NAMES must be one it actually exports
|
|
6
|
+
* (2026-09-13).
|
|
7
|
+
*
|
|
8
|
+
* Removing the 23 subpaths nothing referenced left 19 sentences in `src` and
|
|
9
|
+
* `docs` still advertising them — `cursedbelt/ctgr`, `cursedbelt/server/media`,
|
|
10
|
+
* `cursedbelt/tokens` and friends, each one a copy-pasteable import that now
|
|
11
|
+
* throws ERR_PACKAGE_PATH_NOT_EXPORTED. Nothing could see them, because a
|
|
12
|
+
* subpath named in prose is invisible to `tsc` (it is a comment), invisible to
|
|
13
|
+
* Biome (it is a string), and invisible to `check-paths.ts` (it is not a
|
|
14
|
+
* filesystem path). The only reason those 19 were found is that one agent
|
|
15
|
+
* grepped for them by hand while deleting the exports.
|
|
16
|
+
*
|
|
17
|
+
* That is the drift this file exists to make impossible: the manifest and the
|
|
18
|
+
* prose describing it can no longer disagree without something going red.
|
|
19
|
+
*
|
|
20
|
+
* 🔴 Bun.Glob + Bun.file, NOT node:fs — this package's test preload virtualizes
|
|
21
|
+
* node:fs, so a readdirSync/readFileSync scan reads MOCK data and passes against
|
|
22
|
+
* files it never opened. Same trap as publishShape.spec.ts; see its header.
|
|
23
|
+
*
|
|
24
|
+
* Escape hatch: a line that must name a removed subpath (a changelog entry, a
|
|
25
|
+
* migration note) carries `subpath-check:ignore` and is skipped.
|
|
26
|
+
*/
|
|
27
|
+
const ROOTS = ['src', 'scripts', 'docs'] as const;
|
|
28
|
+
|
|
29
|
+
const BINARY = /\.(png|jpe?g|gif|webp|avif|ico|mp4|webm|mp3|wav|pdf|zip|gz|woff2?|ttf|otf|eot|wasm)$/i;
|
|
30
|
+
|
|
31
|
+
const IGNORE = 'subpath-check:ignore';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `cursedbelt/<subpath>` wherever it appears — code, comment or markdown. The
|
|
35
|
+
* trailing class stops `cursedbelt/react` from matching inside
|
|
36
|
+
* `cursedbelt/react/timeline`, so a removed deep subpath cannot hide behind a
|
|
37
|
+
* live shallow one.
|
|
38
|
+
*
|
|
39
|
+
* The leading `(?<![\w/.-])` is what separates an IMPORT SPECIFIER from a REPO
|
|
40
|
+
* PATH: `packages/cursedbelt/src/...` and `../cursedbelt/scripts/...` name files
|
|
41
|
+
* on disk (check-paths.ts' job), not subpaths, and flagging them would make this
|
|
42
|
+
* gate unpassable and therefore useless.
|
|
43
|
+
*/
|
|
44
|
+
const NAMED = /(?<![\w/.-])cursedbelt(\/[a-z0-9][a-z0-9._/-]*[a-z0-9])/gi;
|
|
45
|
+
|
|
46
|
+
/** First segment is a directory in THIS repo ⇒ the mention is a file path. */
|
|
47
|
+
const REPO_DIRS = /^\/(src|scripts|dist|docs|e2e|demo|node_modules|releases)(\/|$)/;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Ends in a file extension ⇒ a file path, not a subpath — except the three CSS
|
|
51
|
+
* entries, which genuinely ARE exports (`cursedbelt/styles.css`).
|
|
52
|
+
*/
|
|
53
|
+
const FILE_EXT = /\.(?!css$)[a-z0-9]+$/i;
|
|
54
|
+
|
|
55
|
+
const exported = new Set(Object.keys(pkg.exports).map((k) => k.replace(/^\./, '')));
|
|
56
|
+
|
|
57
|
+
// This file and the task/report prose quoting the removal are allowed to name them.
|
|
58
|
+
const SELF = 'namedSubpathsResolve.spec.ts';
|
|
59
|
+
|
|
60
|
+
type Offence = { file: string; line: number; subpath: string; text: string };
|
|
61
|
+
|
|
62
|
+
let cache: Promise<Offence[]> | undefined;
|
|
63
|
+
const offences = (): Promise<Offence[]> => {
|
|
64
|
+
cache ??= (async () => {
|
|
65
|
+
const repo = `${import.meta.dir}/..`;
|
|
66
|
+
const found: Offence[] = [];
|
|
67
|
+
for (const root of ROOTS) {
|
|
68
|
+
const dir = `${repo}/${root}`;
|
|
69
|
+
for (const rel of new Bun.Glob('**/*').scanSync({ cwd: dir, onlyFiles: true })) {
|
|
70
|
+
if (BINARY.test(rel) || rel.endsWith(SELF)) continue;
|
|
71
|
+
const text = await Bun.file(`${dir}/${rel}`).text();
|
|
72
|
+
const lines = text.split('\n');
|
|
73
|
+
for (let i = 0; i < lines.length; i++) {
|
|
74
|
+
const line = lines[i] ?? '';
|
|
75
|
+
if (line.includes(IGNORE)) continue;
|
|
76
|
+
for (const m of line.matchAll(NAMED)) {
|
|
77
|
+
const subpath = m[1] ?? '';
|
|
78
|
+
if (exported.has(subpath)) continue;
|
|
79
|
+
if (REPO_DIRS.test(subpath) || FILE_EXT.test(subpath)) continue;
|
|
80
|
+
found.push({ file: `${root}/${rel}`, line: i + 1, subpath, text: line.trim() });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return found;
|
|
86
|
+
})();
|
|
87
|
+
return cache;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
describe('subpaths named in this repo resolve', () => {
|
|
91
|
+
it('has scanned a real source tree', async () => {
|
|
92
|
+
// Guards the whole file: if the glob or the fs mock ever returns nothing,
|
|
93
|
+
// the assertion below passes vacuously and the gate quietly dies.
|
|
94
|
+
const repo = `${import.meta.dir}/..`;
|
|
95
|
+
let seen = 0;
|
|
96
|
+
for (const root of ROOTS) {
|
|
97
|
+
for (const _ of new Bun.Glob('**/*').scanSync({ cwd: `${repo}/${root}`, onlyFiles: true })) seen++;
|
|
98
|
+
}
|
|
99
|
+
expect(seen).toBeGreaterThan(100);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('knows what the manifest exports', () => {
|
|
103
|
+
// If `exports` is ever read wrong, every offence below silently becomes one.
|
|
104
|
+
expect(exported.has('')).toBe(true); // the "." root entry
|
|
105
|
+
expect(exported.has('/react')).toBe(true);
|
|
106
|
+
expect(exported.has('/game')).toBe(false); // removed 2026-09-13
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('names no subpath package.json does not export', async () => {
|
|
110
|
+
const dead = await offences();
|
|
111
|
+
expect(
|
|
112
|
+
dead,
|
|
113
|
+
`these name a cursedbelt subpath that is not in package.json exports — a consumer who\n` +
|
|
114
|
+
`copies one gets ERR_PACKAGE_PATH_NOT_EXPORTED. Point at the surviving subpath, or at\n` +
|
|
115
|
+
`the repo path, or mark the line \`${IGNORE}\`:\n` +
|
|
116
|
+
dead.map((d) => ` ${d.file}:${d.line} cursedbelt${d.subpath}\n ${d.text}`).join('\n'),
|
|
117
|
+
).toEqual([]);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { run } from '../scripts/publicSurface';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The public-surface ratchet, wired into `bun run verify` at its cheapest correct
|
|
6
|
+
* home: `verify` already ends in `bun test src`, so this needs no new script and
|
|
7
|
+
* cannot be a script somebody forgot to chain. The whole measurement is ~1.3s
|
|
8
|
+
* against a 4m41s suite.
|
|
9
|
+
*
|
|
10
|
+
* WHY it exists, WHAT a symbol count includes and excludes, why `bin` targets and
|
|
11
|
+
* the dependency list are deliberately NOT counted here, and what `--prune` may
|
|
12
|
+
* and may not do are all in the header of `scripts/publicSurface.ts` — one check,
|
|
13
|
+
* one baseline (`publicSurface.baseline`), one place that explains itself. This
|
|
14
|
+
* file is only the wiring.
|
|
15
|
+
*
|
|
16
|
+
* 🔴 Verified failing, 2026-09-13, before it was trusted — every branch, not just
|
|
17
|
+
* the easy one. Each probe was applied, observed, and removed again:
|
|
18
|
+
*
|
|
19
|
+
* · `export const ratchetProbe = 1` appended to `src/core/palette/index.ts`
|
|
20
|
+
* → this file's "ceiling" test red, and the CLI:
|
|
21
|
+
* "2 subpath(s) that gained exported symbols · ./react — baseline allows 1061,
|
|
22
|
+
* exports 1062 now · ./palette — baseline allows 10, exports 11 now".
|
|
23
|
+
* TWO, because the `./react` barrel re-exports the palette — which is the
|
|
24
|
+
* transitive `export *` walk this file's header promises, seen working.
|
|
25
|
+
* · a `"./probe"` subpath added to package.json `exports`
|
|
26
|
+
* → "1 export subpath(s) the baseline does not know · ./probe — NEW subpath, 10 exported symbol(s)"
|
|
27
|
+
* · `searchInitials` removed from `src/core/palette/index.ts`, no prune
|
|
28
|
+
* → "2 baseline entr(y|ies) that no longer match `exports` · ./palette — baseline
|
|
29
|
+
* says 10, exports 9 · ./react — baseline says 1061, exports 1060"
|
|
30
|
+
* · a `5 ./gone` line added to the baseline by hand
|
|
31
|
+
* → same failure, "./gone — baseline says 5, the subpath is gone". An entry
|
|
32
|
+
* matching nothing is the half that stops a deleted module leaving an allowance.
|
|
33
|
+
* · `--prune` run against the grown surface
|
|
34
|
+
* → "refuses while the surface has GROWN — it may only lower the baseline", exit 1
|
|
35
|
+
* · `--prune` run against the shrunk surface
|
|
36
|
+
* → "pruned to 85 subpath(s), 0 removed, 3806 symbols", exit 0 — `./palette`
|
|
37
|
+
* 10 → 9 and `./react` 1061 → 1060 in the file, which is the burn-down path
|
|
38
|
+
*
|
|
39
|
+
* A ratchet nobody has seen red is a ratchet nobody knows is wired up.
|
|
40
|
+
*/
|
|
41
|
+
const result = await run();
|
|
42
|
+
|
|
43
|
+
describe('public surface', () => {
|
|
44
|
+
it('has measured a real surface', () => {
|
|
45
|
+
// Guards the three assertions below: if `exports` were ever read as empty,
|
|
46
|
+
// every one of them passes vacuously and the ratchet quietly dies.
|
|
47
|
+
expect(result.surface.counts.size).toBeGreaterThan(50);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('publishes no export subpath the baseline does not know', () => {
|
|
51
|
+
expect(
|
|
52
|
+
result.fresh,
|
|
53
|
+
`new public subpath(s) — a permanent promise. Add the line to publicSurface.baseline and say why:\n ${result.fresh.join('\n ')}`,
|
|
54
|
+
).toEqual([]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('exports no more symbols per subpath than the baseline allows', () => {
|
|
58
|
+
expect(
|
|
59
|
+
result.grown,
|
|
60
|
+
`a baseline entry is a ceiling, not an amnesty:\n ${result.grown.join('\n ')}`,
|
|
61
|
+
).toEqual([]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('has no baseline entry that outlived the surface it recorded', () => {
|
|
65
|
+
// The half that makes it a RATCHET: a deleted module may not leave an
|
|
66
|
+
// allowance behind for the next one to grow into.
|
|
67
|
+
expect(
|
|
68
|
+
result.stale,
|
|
69
|
+
`surface came off and the ratchet was not tightened — run \`bun scripts/publicSurface.ts --prune\`:\n ${result.stale.join('\n ')}`,
|
|
70
|
+
).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -32,6 +32,23 @@
|
|
|
32
32
|
* brighter glyph — the same language the file tree's selected row uses, so a person learns
|
|
33
33
|
* it once.
|
|
34
34
|
*
|
|
35
|
+
* ── 🔴 The graduation verdict, 2026-09-13: THIS one, unchanged ──────────────────────────
|
|
36
|
+
* `cursedesk` has an `ActivityBar.tsx` of its own and the pair was compared to decide which
|
|
37
|
+
* should be the shared one. Nothing moved here, and the reason is that they are not two
|
|
38
|
+
* versions of one component: this is a RAIL that takes {@link ActivityBarProps.top} and
|
|
39
|
+
* {@link ActivityBarProps.bottom} and draws whatever is in them; the app's is one particular
|
|
40
|
+
* seven-button composition — Explorer, Search, ⌘P, the command menu, full screen, theme,
|
|
41
|
+
* settings — hard-wired to that app's `primitives.tsx` and `shortcuts.ts`, with every prop
|
|
42
|
+
* (`searchOpen`, `onSwitcher`, `shortcutPrefs`, `commandUnavailable`) naming one of them.
|
|
43
|
+
*
|
|
44
|
+
* A design system cannot hold that: `onSwitcher` is a door onto a file finder and
|
|
45
|
+
* `shortcutPrefs` is a preferences table, neither of which a rail has any business knowing.
|
|
46
|
+
* So the app's copy is deleted rather than moved, and its seven buttons become seven
|
|
47
|
+
* `ActivityBarItem`s built where the app's state already lives. The one thing it has that
|
|
48
|
+
* this does not — a command menu rendered by the rail itself — is already expressible:
|
|
49
|
+
* {@link ActivityBarItem.render} takes the consumer's own popover, and
|
|
50
|
+
* {@link ActivityBarItem.hasMenu}/{@link ActivityBarItem.menuOpen} say so to a screen reader.
|
|
51
|
+
*
|
|
35
52
|
* ── Plain CSS, deliberately ─────────────────────────────────────────────────────────────
|
|
36
53
|
* `activityBar.css` under the `cbab-` prefix, the same convention (and the same reason) as
|
|
37
54
|
* `fileTree.css` and `denseGallery.css`: this mounts inside apps whose Tailwind build cannot
|