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
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { Scanner } from '@tailwindcss/oxide';
|
|
3
|
+
import { compile } from 'tailwindcss';
|
|
4
|
+
import {
|
|
5
|
+
TAP_TARGET,
|
|
6
|
+
TAP_TARGET_ALWAYS,
|
|
7
|
+
TAP_TARGET_HALO,
|
|
8
|
+
TAP_TARGET_HEIGHT,
|
|
9
|
+
TAP_TARGET_SQUARE,
|
|
10
|
+
} from './react/lib/tapTarget';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 🔴 THE TAP FLOOR SURVIVES THE JIT.
|
|
14
|
+
*
|
|
15
|
+
* `src/react/lib/tapTarget.ts` collapsed 48 hand-written copies of `DESIGN.md`
|
|
16
|
+
* §5's 44px floor — and 12 of the hover-reveal fix that goes with it — into five
|
|
17
|
+
* constants. That refactor has one failure mode, and it is silent: **Tailwind's
|
|
18
|
+
* JIT scans source TEXT, it does not evaluate code.** A
|
|
19
|
+
* class that stops being a literal inside this package's own `@source "."` glob —
|
|
20
|
+
* moved to a file the glob does not cover, assembled from a variable, split
|
|
21
|
+
* across a template it cannot read — is a class Tailwind never emits. Nothing
|
|
22
|
+
* reds. The types are fine, the build is fine, every component still carries the
|
|
23
|
+
* class name in its `className`, and the rule it names simply does not exist in
|
|
24
|
+
* the stylesheet any more. Every tap target on every phone in the fleet quietly
|
|
25
|
+
* loses its floor.
|
|
26
|
+
*
|
|
27
|
+
* So this compiles the REAL `src/styles.css` — its own `@source` directive, its
|
|
28
|
+
* own scan of this package's tree, Tailwind's own candidate extractor — and
|
|
29
|
+
* asserts the emitted CSS still carries a rule for every utility the constants
|
|
30
|
+
* name. It is the one check that can tell "the constant is exported" apart from
|
|
31
|
+
* "the constant does anything".
|
|
32
|
+
*
|
|
33
|
+
* 🔴 NOTHING IN THIS FILE MAY SPELL A TAP UTILITY OUT. The `@source` glob covers
|
|
34
|
+
* `src/`, and a spec is source: the first draft of this header quoted the height
|
|
35
|
+
* class in its own prose, and that comment alone kept the class in the candidate
|
|
36
|
+
* set after the constant had been broken. The check passed against a stylesheet
|
|
37
|
+
* held up by a sentence about the check. Utilities come from the imports above
|
|
38
|
+
* and from nowhere else in here.
|
|
39
|
+
*
|
|
40
|
+
* Verified failing, 2026-09-14, before it was trusted — each probe applied,
|
|
41
|
+
* observed and removed again:
|
|
42
|
+
* · `TAP_TARGET_HEIGHT` rewritten as a template that interpolates its `11`
|
|
43
|
+
* → both the extraction test and "utilities the emitted stylesheet has no
|
|
44
|
+
* rule for: <the height class>". `TAP_TARGET` composes the same constant,
|
|
45
|
+
* so every button in the package loses the floor from this one edit.
|
|
46
|
+
* · `TAP_TARGET_HEIGHT` truncated to its scale-less prefix (the trailing `11`
|
|
47
|
+
* dropped) → same failure, which is the anchored-selector half. A plain
|
|
48
|
+
* `includes()` would have PASSED this one: the truncated name is a substring
|
|
49
|
+
* of the rule the untruncated class emits elsewhere in the same stylesheet.
|
|
50
|
+
* 🔴 Truncating by one DIGIT instead does not fail, and should not — the
|
|
51
|
+
* result is another real Tailwind utility with a real rule; what would be
|
|
52
|
+
* broken there is the design, not the pipeline this file measures.
|
|
53
|
+
* · the `@source` line removed from `src/styles.css` → "scanned no candidates",
|
|
54
|
+
* the guard below that stops every other assertion passing vacuously.
|
|
55
|
+
*
|
|
56
|
+
* ~1.5s: one Tailwind compile of ~118 KB over ~25k candidates.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
const REPO_ROOT = import.meta.dir.replace(/\/src$/, '');
|
|
60
|
+
|
|
61
|
+
/** Directory half of an absolute path — `node:path` is virtualized by the test preload. */
|
|
62
|
+
const dirOf = (path: string): string => path.slice(0, path.lastIndexOf('/'));
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Resolve a CSS `@import` the way a bundler would.
|
|
66
|
+
*
|
|
67
|
+
* 🔴 `Bun.file`, never `node:fs`: this package's test preload virtualizes node
|
|
68
|
+
* builtins, so a `readFileSync` here would read MOCK data and the whole
|
|
69
|
+
* measurement would be of nothing (same trap as `publishShape.spec.ts`). A CSS
|
|
70
|
+
* package announces its entry through the `style` condition, not `main`.
|
|
71
|
+
*/
|
|
72
|
+
const loadStylesheet = async (
|
|
73
|
+
id: string,
|
|
74
|
+
base: string,
|
|
75
|
+
): Promise<{ path: string; base: string; content: string }> => {
|
|
76
|
+
const read = async (path: string) => ({
|
|
77
|
+
path,
|
|
78
|
+
base: dirOf(path),
|
|
79
|
+
content: await Bun.file(path).text(),
|
|
80
|
+
});
|
|
81
|
+
if (id.startsWith('.')) return read(`${base}/${id}`);
|
|
82
|
+
const packageDir = `${REPO_ROOT}/node_modules/${id}`;
|
|
83
|
+
const manifest = (await Bun.file(`${packageDir}/package.json`).json()) as {
|
|
84
|
+
exports?: { '.'?: { style?: string } };
|
|
85
|
+
style?: string;
|
|
86
|
+
main?: string;
|
|
87
|
+
};
|
|
88
|
+
const entry = manifest.exports?.['.']?.style ?? manifest.style ?? manifest.main;
|
|
89
|
+
if (!entry) throw new Error(`no stylesheet entry for "${id}"`);
|
|
90
|
+
return read(`${packageDir}/${entry.replace(/^\.\//, '')}`);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
/** Every utility the five constants name, deduped — the thing that must survive. */
|
|
94
|
+
const UTILITIES = [
|
|
95
|
+
...new Set(
|
|
96
|
+
[TAP_TARGET_HEIGHT, TAP_TARGET, TAP_TARGET_SQUARE, TAP_TARGET_HALO, TAP_TARGET_ALWAYS]
|
|
97
|
+
.join(' ')
|
|
98
|
+
.split(/\s+/)
|
|
99
|
+
.filter(Boolean),
|
|
100
|
+
),
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
const emitted = await (async () => {
|
|
104
|
+
// An app imports Tailwind first, then this package's stylesheet — exactly the
|
|
105
|
+
// two lines `src/styles.css`'s own header tells a consumer to write.
|
|
106
|
+
const source = await Bun.file(`${REPO_ROOT}/src/styles.css`).text();
|
|
107
|
+
const compiler = await compile(`@import "tailwindcss";\n${source}`, {
|
|
108
|
+
base: `${REPO_ROOT}/src`,
|
|
109
|
+
loadStylesheet,
|
|
110
|
+
});
|
|
111
|
+
const candidates = new Scanner({ sources: compiler.sources }).scan();
|
|
112
|
+
return { candidates, css: compiler.build(candidates) };
|
|
113
|
+
})();
|
|
114
|
+
|
|
115
|
+
describe('the tap floor reaches the stylesheet', () => {
|
|
116
|
+
it('scanned this package for candidates at all', () => {
|
|
117
|
+
// Guards every assertion below: with no `@source` directive the scan returns
|
|
118
|
+
// nothing, `build([])` emits no utilities, and "the rule is missing" would be
|
|
119
|
+
// indistinguishable from "the refactor broke it".
|
|
120
|
+
expect(emitted.candidates.length).toBeGreaterThan(1000);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('names utilities Tailwind actually extracted from the source text', () => {
|
|
124
|
+
// The JIT half: each class is a literal in a file the `@source` glob covers.
|
|
125
|
+
expect(UTILITIES.filter((u) => !emitted.candidates.includes(u))).toEqual([]);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('emits a CSS rule for every utility the tap-target constants name', () => {
|
|
129
|
+
// A `:` is backslash-escaped inside a selector, so the class `a:b` is written
|
|
130
|
+
// `.a\:b` in the output — and a candidate that survived extraction but
|
|
131
|
+
// generated no rule (a utility Tailwind does not know) still fails here.
|
|
132
|
+
//
|
|
133
|
+
// 🔴 Anchored on the END of the class name, not a substring: a class truncated
|
|
134
|
+
// by one character is a prefix of the rule its untruncated self emits, so a
|
|
135
|
+
// plain `includes()` would have found it in a stylesheet that never had it.
|
|
136
|
+
const missing = UTILITIES.filter((u) => {
|
|
137
|
+
// The utility → its escaped selector text → a regex matching that text, with
|
|
138
|
+
// the CSS backslash itself escaped a second time for the regex.
|
|
139
|
+
const selectorText = `.${u.replace(/:/g, '\\:')}`;
|
|
140
|
+
const anchored = new RegExp(
|
|
141
|
+
`${selectorText.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\w\\\\-])`,
|
|
142
|
+
);
|
|
143
|
+
return !anchored.test(emitted.css);
|
|
144
|
+
});
|
|
145
|
+
expect(
|
|
146
|
+
missing,
|
|
147
|
+
`utilities the emitted stylesheet has no rule for: ${missing.join(', ')}`,
|
|
148
|
+
).toEqual([]);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test';
|
|
2
|
+
import { isAbsolute, resolve, sep } from 'node:path';
|
|
3
|
+
import { resolveTestTmpRoot } from '../scripts/testTmpRoot';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 🔴 THE CHECKER CANNOT SEE THIS ONE, WHICH IS WHY IT IS A TEST.
|
|
7
|
+
*
|
|
8
|
+
* `scripts/testTmpRoot.ts` is a bun test PRELOAD: it runs before the first spec in
|
|
9
|
+
* this repo, on every `bun test`, and it decides where every `mkdtemp` in the suite
|
|
10
|
+
* lands. Until 2026-09-13 it pointed `$TMPDIR` at the PREVIOUS generation's state
|
|
11
|
+
* root — 77 MB and 259 process directories had accumulated there — and
|
|
12
|
+
* `tools/check-paths.ts` reported nothing, because the path was interpolated from
|
|
13
|
+
* `$HOME` on one line and assembled from a variable on the next. Neither line
|
|
14
|
+
* carried a shape the trigger looks for.
|
|
15
|
+
*
|
|
16
|
+
* So the gate is here instead, and it asserts the RESOLVED root rather than the
|
|
17
|
+
* source text: a previous generation's directory cannot come back through a
|
|
18
|
+
* variable, a rename, or a constant without one of these failing.
|
|
19
|
+
*
|
|
20
|
+
* This spec lives in `src/` rather than beside the file it proves because
|
|
21
|
+
* `bun run test` is `bun test src` — a spec under `scripts/` would never run, and
|
|
22
|
+
* a gate that does not run is the exact failure mode above.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
/** Every directory name this generation must never write into. */
|
|
26
|
+
const PREVIOUS_GENERATIONS = ['app-managed', 'code-retiring', 'code-active', 'Master-Docs'];
|
|
27
|
+
|
|
28
|
+
const REPO_ROOT = resolve(import.meta.dir, '..');
|
|
29
|
+
|
|
30
|
+
describe('the test scratch root', () => {
|
|
31
|
+
it('lives under $FORGE_STATE when the generation supplies one', () => {
|
|
32
|
+
const root = resolveTestTmpRoot({ FORGE_STATE: '/state/cursedforge' });
|
|
33
|
+
expect(root.startsWith(`/state/cursedforge${sep}`)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('falls back to this repo, never to anything derived from $HOME', () => {
|
|
37
|
+
const root = resolveTestTmpRoot({ HOME: '/home/somebody' });
|
|
38
|
+
expect(root.startsWith(`${REPO_ROOT}${sep}`)).toBe(true);
|
|
39
|
+
expect(root).not.toContain('/home/somebody');
|
|
40
|
+
// Gitignored here, so a fallback run leaves nothing for git to report.
|
|
41
|
+
expect(root).toContain('.agent.noindex');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('treats a blank $FORGE_STATE as no $FORGE_STATE', () => {
|
|
45
|
+
expect(resolveTestTmpRoot({ FORGE_STATE: ' ', HOME: '/home/somebody' }).startsWith(`${REPO_ROOT}${sep}`)).toBe(
|
|
46
|
+
true,
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
* 🔴 `.noindex` is not decoration and is not cosmetic: it is the only marker that
|
|
52
|
+
* keeps Spotlight out of files written LATER into a tree, and a test root is
|
|
53
|
+
* written to constantly (measured 43× on this machine). Both branches carry it.
|
|
54
|
+
*/
|
|
55
|
+
it('keeps a .noindex segment on every branch', () => {
|
|
56
|
+
for (const env of [{ FORGE_STATE: '/state/cursedforge' }, { HOME: '/home/somebody' }]) {
|
|
57
|
+
expect(resolveTestTmpRoot(env).split(sep).some((segment) => segment.endsWith('.noindex'))).toBe(true);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('resolves an absolute path, whatever the caller cwd is', () => {
|
|
62
|
+
for (const env of [{ FORGE_STATE: '/state/cursedforge' }, {}]) {
|
|
63
|
+
expect(isAbsolute(resolveTestTmpRoot(env))).toBe(true);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
/*
|
|
68
|
+
* The assertion the whole file exists for. Note the envs that TRY to steer it
|
|
69
|
+
* back: the previous generation's own variable, and a home that already contains
|
|
70
|
+
* its folder. Neither may reach the answer.
|
|
71
|
+
*/
|
|
72
|
+
it('never resolves a previous generation, whatever the environment says', () => {
|
|
73
|
+
const envs: Array<Record<string, string | undefined>> = [
|
|
74
|
+
{},
|
|
75
|
+
{ HOME: '/home/somebody' },
|
|
76
|
+
{ FORGE_STATE: '/state/cursedforge', HOME: '/home/somebody' },
|
|
77
|
+
{ APP_MANAGED_HOME: '/nowhere/inherited', HOME: '/home/somebody' },
|
|
78
|
+
{ HOME: `/home/somebody/${PREVIOUS_GENERATIONS[0]}` },
|
|
79
|
+
];
|
|
80
|
+
for (const env of envs) {
|
|
81
|
+
const root = resolveTestTmpRoot(env);
|
|
82
|
+
for (const name of PREVIOUS_GENERATIONS) {
|
|
83
|
+
expect(root, `resolved "${root}" for env ${JSON.stringify(env)}`).not.toContain(name);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/*
|
|
89
|
+
* And the live one: whatever this very process ended up with. The preload has
|
|
90
|
+
* already run by the time any spec does, so `$TMPDIR` here is the real answer on
|
|
91
|
+
* the real machine — the measurement the source-text assertions above cannot make.
|
|
92
|
+
*/
|
|
93
|
+
it('has already pointed THIS process somewhere legitimate', () => {
|
|
94
|
+
const live = process.env.TMPDIR ?? '';
|
|
95
|
+
if (process.env.TMPDIR_TEST_PASSTHROUGH) return; // a suite asked for the system root
|
|
96
|
+
for (const name of PREVIOUS_GENERATIONS) {
|
|
97
|
+
expect(live, `$TMPDIR is "${live}"`).not.toContain(name);
|
|
98
|
+
}
|
|
99
|
+
const expected = resolveTestTmpRoot();
|
|
100
|
+
expect(live.startsWith(`${expected}${sep}`)).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* "Are these two files the same thing?" — the fleet's ONE answer, with no I/O in it.
|
|
3
|
-
*
|
|
4
|
-
* ── Why this is here and not in an app ──────────────────────────────────────────────────
|
|
5
|
-
* `binary-server` wrote these rules first (2026-08-13, the owner: *"make strategy to always
|
|
6
|
-
* check for duplicate binaries in same app for same user … mark likely the duplicates for
|
|
7
|
-
* users to confirm"*), and they were measured against a real store: the copy-counter cap, the
|
|
8
|
-
* camera-generic stem list and the 5% size window each exist because a wider rule proposed
|
|
9
|
-
* thousands of pairs nobody could ever review. `music` then needed the same question asked of
|
|
10
|
-
* a 5,400-track library — *"I want to see a duplicate next to the file that it is a possible
|
|
11
|
-
* duplicate of … The 'binary-server' app (bs) already has this feature for checking duplicate
|
|
12
|
-
* files. We can reuse it and enhance the customizability if needed so the main strategy is
|
|
13
|
-
* shared."*
|
|
14
|
-
*
|
|
15
|
-
* So the strategy lives once, here, and both consume it. What each app keeps for itself is the
|
|
16
|
-
* evidence only it has: bs's perceptual frame hashes, music's artist/title key.
|
|
17
|
-
*
|
|
18
|
-
* ── What is NOT here, deliberately ──────────────────────────────────────────────────────
|
|
19
|
-
* Anything that needs a database, a filesystem, a fetch or a picture decoder. This module is
|
|
20
|
-
* pure so both consumers can prove their behaviour with `bun test` instead of against a live
|
|
21
|
-
* store, and so a browser bundle may import it (music renders the pair side by side).
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* How far two sizes may differ and still be called the same thing.
|
|
25
|
-
*
|
|
26
|
-
* A re-encode at a different quality is the case this exists for, and those land within a few
|
|
27
|
-
* percent. Zero would make a name match a strict subset of "same name AND same size", which
|
|
28
|
-
* misses precisely the files worth asking about; much larger starts pairing a clip with its
|
|
29
|
-
* own trailer.
|
|
30
|
-
*/
|
|
31
|
-
export declare const SIZE_TOLERANCE = 0.05;
|
|
32
|
-
/**
|
|
33
|
-
* How far two playing times may differ and still be the same recording.
|
|
34
|
-
*
|
|
35
|
-
* Wide enough for a re-encode (a trim of a trailing black frame, a container that rounds), far
|
|
36
|
-
* too narrow for the owner's 2026-08-14 report of a 3:56 video paired with a 1:52 one.
|
|
37
|
-
*/
|
|
38
|
-
export declare const DURATION_TOLERANCE = 0.05;
|
|
39
|
-
/**
|
|
40
|
-
* The name to compare, reduced to what a human means by "the same file".
|
|
41
|
-
*
|
|
42
|
-
* Strips the directory, the extension, and the marks a copy picks up along the way —
|
|
43
|
-
* `Finder`'s ` copy`/` copy 2`, a browser's ` (1)`, a script's `_1`/`-1` — then collapses
|
|
44
|
-
* everything that is not a letter or a digit. `My Video (1).mp4`, `my_video.MP4` and
|
|
45
|
-
* `My-Video copy 2.mov` all reduce to `myvideo`.
|
|
46
|
-
*
|
|
47
|
-
* 🔴 The extension is stripped ON PURPOSE, so a re-encode (`.mov` → `.mp4`, `.wma` → `.mp3`)
|
|
48
|
-
* still pairs with its source. That is the single most common likely-duplicate in this fleet's
|
|
49
|
-
* stores and a rule that kept the extension would never see one.
|
|
50
|
-
*
|
|
51
|
-
* 🔴 **The copy-counter strip is capped at two digits**, and that cap is a measured bug fix
|
|
52
|
-
* (bs, 2026-08-14). It used to be `[-_]\d+$`, which eats a CAMERA SEQUENCE as readily as a copy
|
|
53
|
-
* marker: `IMG_4595` and `IMG_4646` both reduced to `img`, so every iPhone capture in a tenant
|
|
54
|
-
* shared one bucket and paired with every other one whose size was within 5%. `_1`…`_99` is
|
|
55
|
-
* what a copy marker actually looks like; four digits is a photo's identity, and it must
|
|
56
|
-
* survive.
|
|
57
|
-
*/
|
|
58
|
-
export declare function normalizeName(name: string | null | undefined): string;
|
|
59
|
-
/**
|
|
60
|
-
* Stems that name a CAMERA, not a file.
|
|
61
|
-
*
|
|
62
|
-
* `IMG`, `DSC`, `PXL`, `VID` are what a device calls everything it produces; two files whose
|
|
63
|
-
* names reduce to one of these have told us nothing except that they came from a phone. Even
|
|
64
|
-
* with the counter cap above, `IMG_1.MOV` still reduces to `img`, and a bucket called "img" is
|
|
65
|
-
* every photo the owner has ever taken.
|
|
66
|
-
*
|
|
67
|
-
* 🔴 This rejects a MATCH, never a file. Both copies stay wherever they are, and an
|
|
68
|
-
* identical-bytes pair is unaffected — a checksum match never consults the name.
|
|
69
|
-
*/
|
|
70
|
-
export declare const GENERIC_STEMS: ReadonlySet<string>;
|
|
71
|
-
/**
|
|
72
|
-
* A name that could belong to any file from that device tells us nothing about identity.
|
|
73
|
-
*
|
|
74
|
-
* 🔴 The trailing digit run is stripped BEFORE the lookup, and only here — never in
|
|
75
|
-
* {@link normalizeName}, where removing it is what made every camera capture one bucket. The
|
|
76
|
-
* two are asked at different moments: `normalizeName` decides whether two names are the SAME
|
|
77
|
-
* (so `img4595` and `img4646` must stay apart), and this decides whether a name that already
|
|
78
|
-
* matched is worth anything (so two files both called `Track01` have told us nothing, and a
|
|
79
|
-
* ripper names half a library that).
|
|
80
|
-
*/
|
|
81
|
-
export declare const isGenericName: (normalized: string) => boolean;
|
|
82
|
-
/** Within {@link SIZE_TOLERANCE} of each other. Two zero-byte files are not "close" — they
|
|
83
|
-
* are two files nothing is known about. */
|
|
84
|
-
export declare function sizesClose(a: number, b: number, tolerance?: number): boolean;
|
|
85
|
-
/**
|
|
86
|
-
* Within {@link DURATION_TOLERANCE} of each other.
|
|
87
|
-
*
|
|
88
|
-
* 🔴 A missing or zero duration answers `null`, never `false`: "nobody has measured this yet"
|
|
89
|
-
* and "these are different lengths" are opposite answers, and collapsing them makes an
|
|
90
|
-
* unprobed library look like a library with no duplicates in it.
|
|
91
|
-
*/
|
|
92
|
-
export declare function durationsClose(a: number | null | undefined, b: number | null | undefined, tolerance?: number): boolean | null;
|
|
93
|
-
/** One file, reduced to what this module can reason about. Units are the caller's — pass
|
|
94
|
-
* both durations in the same unit or not at all. */
|
|
95
|
-
export interface NamedFileFacts {
|
|
96
|
-
/** The name a person would recognise: a display title, else the last path segment. */
|
|
97
|
-
name: string | null | undefined;
|
|
98
|
-
sizeBytes: number;
|
|
99
|
-
/** Playing time, in whatever unit the caller uses consistently. Null = never measured. */
|
|
100
|
-
duration?: number | null;
|
|
101
|
-
}
|
|
102
|
-
export interface NameMatchVerdict {
|
|
103
|
-
match: boolean;
|
|
104
|
-
/** Written for the owner reading a review queue, never for a log. Empty when `match` is
|
|
105
|
-
* false — a non-match has nothing to explain. */
|
|
106
|
-
reason: string;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
* The shared "these look like the same thing" test: the NAME agrees after normalization, the
|
|
110
|
-
* sizes are close, and nothing about the length says otherwise.
|
|
111
|
-
*
|
|
112
|
-
* It is deliberately a QUESTION and never an action. Both consumers act only on a checksum,
|
|
113
|
-
* which is proof; this decides what is worth showing a person side by side.
|
|
114
|
-
*/
|
|
115
|
-
export declare function likelyTheSameFile(a: NamedFileFacts, b: NamedFileFacts): NameMatchVerdict;
|
|
116
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/duplicates/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,OAAO,CAAC;AAEnC;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,OAAO,CAAC;AAEvC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAUrE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,WAAW,CAAC,MAAM,CAgC5C,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,GAAI,YAAY,MAAM,KAAG,OAGD,CAAC;AAEnD;4CAC4C;AAC5C,wBAAgB,UAAU,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,SAAS,SAAiB,GAAG,OAAO,CAKpF;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC7B,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC5B,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC5B,SAAS,SAAqB,GAC5B,OAAO,GAAG,IAAI,CAGhB;AAED;qDACqD;AACrD,MAAM,WAAW,cAAc;IAC9B,sFAAsF;IACtF,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAChC,KAAK,EAAE,OAAO,CAAC;IACf;sDACkD;IAClD,MAAM,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,GAAG,gBAAgB,CAqBxF"}
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* "Are these two files the same thing?" — the fleet's ONE answer, with no I/O in it.
|
|
3
|
-
*
|
|
4
|
-
* ── Why this is here and not in an app ──────────────────────────────────────────────────
|
|
5
|
-
* `binary-server` wrote these rules first (2026-08-13, the owner: *"make strategy to always
|
|
6
|
-
* check for duplicate binaries in same app for same user … mark likely the duplicates for
|
|
7
|
-
* users to confirm"*), and they were measured against a real store: the copy-counter cap, the
|
|
8
|
-
* camera-generic stem list and the 5% size window each exist because a wider rule proposed
|
|
9
|
-
* thousands of pairs nobody could ever review. `music` then needed the same question asked of
|
|
10
|
-
* a 5,400-track library — *"I want to see a duplicate next to the file that it is a possible
|
|
11
|
-
* duplicate of … The 'binary-server' app (bs) already has this feature for checking duplicate
|
|
12
|
-
* files. We can reuse it and enhance the customizability if needed so the main strategy is
|
|
13
|
-
* shared."*
|
|
14
|
-
*
|
|
15
|
-
* So the strategy lives once, here, and both consume it. What each app keeps for itself is the
|
|
16
|
-
* evidence only it has: bs's perceptual frame hashes, music's artist/title key.
|
|
17
|
-
*
|
|
18
|
-
* ── What is NOT here, deliberately ──────────────────────────────────────────────────────
|
|
19
|
-
* Anything that needs a database, a filesystem, a fetch or a picture decoder. This module is
|
|
20
|
-
* pure so both consumers can prove their behaviour with `bun test` instead of against a live
|
|
21
|
-
* store, and so a browser bundle may import it (music renders the pair side by side).
|
|
22
|
-
*/
|
|
23
|
-
/**
|
|
24
|
-
* How far two sizes may differ and still be called the same thing.
|
|
25
|
-
*
|
|
26
|
-
* A re-encode at a different quality is the case this exists for, and those land within a few
|
|
27
|
-
* percent. Zero would make a name match a strict subset of "same name AND same size", which
|
|
28
|
-
* misses precisely the files worth asking about; much larger starts pairing a clip with its
|
|
29
|
-
* own trailer.
|
|
30
|
-
*/
|
|
31
|
-
export const SIZE_TOLERANCE = 0.05;
|
|
32
|
-
/**
|
|
33
|
-
* How far two playing times may differ and still be the same recording.
|
|
34
|
-
*
|
|
35
|
-
* Wide enough for a re-encode (a trim of a trailing black frame, a container that rounds), far
|
|
36
|
-
* too narrow for the owner's 2026-08-14 report of a 3:56 video paired with a 1:52 one.
|
|
37
|
-
*/
|
|
38
|
-
export const DURATION_TOLERANCE = 0.05;
|
|
39
|
-
/**
|
|
40
|
-
* The name to compare, reduced to what a human means by "the same file".
|
|
41
|
-
*
|
|
42
|
-
* Strips the directory, the extension, and the marks a copy picks up along the way —
|
|
43
|
-
* `Finder`'s ` copy`/` copy 2`, a browser's ` (1)`, a script's `_1`/`-1` — then collapses
|
|
44
|
-
* everything that is not a letter or a digit. `My Video (1).mp4`, `my_video.MP4` and
|
|
45
|
-
* `My-Video copy 2.mov` all reduce to `myvideo`.
|
|
46
|
-
*
|
|
47
|
-
* 🔴 The extension is stripped ON PURPOSE, so a re-encode (`.mov` → `.mp4`, `.wma` → `.mp3`)
|
|
48
|
-
* still pairs with its source. That is the single most common likely-duplicate in this fleet's
|
|
49
|
-
* stores and a rule that kept the extension would never see one.
|
|
50
|
-
*
|
|
51
|
-
* 🔴 **The copy-counter strip is capped at two digits**, and that cap is a measured bug fix
|
|
52
|
-
* (bs, 2026-08-14). It used to be `[-_]\d+$`, which eats a CAMERA SEQUENCE as readily as a copy
|
|
53
|
-
* marker: `IMG_4595` and `IMG_4646` both reduced to `img`, so every iPhone capture in a tenant
|
|
54
|
-
* shared one bucket and paired with every other one whose size was within 5%. `_1`…`_99` is
|
|
55
|
-
* what a copy marker actually looks like; four digits is a photo's identity, and it must
|
|
56
|
-
* survive.
|
|
57
|
-
*/
|
|
58
|
-
export function normalizeName(name) {
|
|
59
|
-
if (!name)
|
|
60
|
-
return "";
|
|
61
|
-
const leaf = name.split(/[\\/]/).pop() ?? "";
|
|
62
|
-
const stem = leaf.replace(/\.[a-z0-9]{1,5}$/i, "");
|
|
63
|
-
return stem
|
|
64
|
-
.toLowerCase()
|
|
65
|
-
.replace(/\s*\(\d+\)\s*$/, "")
|
|
66
|
-
.replace(/\s+copy(\s+\d+)?\s*$/, "")
|
|
67
|
-
.replace(/[-_]\d{1,2}\s*$/, "")
|
|
68
|
-
.replace(/[^a-z0-9]+/g, "");
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Stems that name a CAMERA, not a file.
|
|
72
|
-
*
|
|
73
|
-
* `IMG`, `DSC`, `PXL`, `VID` are what a device calls everything it produces; two files whose
|
|
74
|
-
* names reduce to one of these have told us nothing except that they came from a phone. Even
|
|
75
|
-
* with the counter cap above, `IMG_1.MOV` still reduces to `img`, and a bucket called "img" is
|
|
76
|
-
* every photo the owner has ever taken.
|
|
77
|
-
*
|
|
78
|
-
* 🔴 This rejects a MATCH, never a file. Both copies stay wherever they are, and an
|
|
79
|
-
* identical-bytes pair is unaffected — a checksum match never consults the name.
|
|
80
|
-
*/
|
|
81
|
-
export const GENERIC_STEMS = new Set([
|
|
82
|
-
"img",
|
|
83
|
-
"image",
|
|
84
|
-
"images",
|
|
85
|
-
"dsc",
|
|
86
|
-
"dscf",
|
|
87
|
-
"dscn",
|
|
88
|
-
"pxl",
|
|
89
|
-
"vid",
|
|
90
|
-
"video",
|
|
91
|
-
"movie",
|
|
92
|
-
"mov",
|
|
93
|
-
"photo",
|
|
94
|
-
"pic",
|
|
95
|
-
"picture",
|
|
96
|
-
"screenshot",
|
|
97
|
-
"screenrecording",
|
|
98
|
-
"untitled",
|
|
99
|
-
"unknown",
|
|
100
|
-
"file",
|
|
101
|
-
"download",
|
|
102
|
-
"scan",
|
|
103
|
-
"capture",
|
|
104
|
-
"copy",
|
|
105
|
-
"new",
|
|
106
|
-
"temp",
|
|
107
|
-
"tmp",
|
|
108
|
-
// Music's own version of the same problem: an untagged rip is called this by every ripper
|
|
109
|
-
// ever written, and `track01` from two albums is two different songs.
|
|
110
|
-
"track",
|
|
111
|
-
"audiotrack",
|
|
112
|
-
"unknownartist",
|
|
113
|
-
]);
|
|
114
|
-
/**
|
|
115
|
-
* A name that could belong to any file from that device tells us nothing about identity.
|
|
116
|
-
*
|
|
117
|
-
* 🔴 The trailing digit run is stripped BEFORE the lookup, and only here — never in
|
|
118
|
-
* {@link normalizeName}, where removing it is what made every camera capture one bucket. The
|
|
119
|
-
* two are asked at different moments: `normalizeName` decides whether two names are the SAME
|
|
120
|
-
* (so `img4595` and `img4646` must stay apart), and this decides whether a name that already
|
|
121
|
-
* matched is worth anything (so two files both called `Track01` have told us nothing, and a
|
|
122
|
-
* ripper names half a library that).
|
|
123
|
-
*/
|
|
124
|
-
export const isGenericName = (normalized) => normalized.length < 3 ||
|
|
125
|
-
GENERIC_STEMS.has(normalized) ||
|
|
126
|
-
GENERIC_STEMS.has(normalized.replace(/\d+$/, ""));
|
|
127
|
-
/** Within {@link SIZE_TOLERANCE} of each other. Two zero-byte files are not "close" — they
|
|
128
|
-
* are two files nothing is known about. */
|
|
129
|
-
export function sizesClose(a, b, tolerance = SIZE_TOLERANCE) {
|
|
130
|
-
if (a === b && a > 0)
|
|
131
|
-
return true;
|
|
132
|
-
const larger = Math.max(a, b);
|
|
133
|
-
if (larger <= 0)
|
|
134
|
-
return false;
|
|
135
|
-
return Math.abs(a - b) / larger <= tolerance;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Within {@link DURATION_TOLERANCE} of each other.
|
|
139
|
-
*
|
|
140
|
-
* 🔴 A missing or zero duration answers `null`, never `false`: "nobody has measured this yet"
|
|
141
|
-
* and "these are different lengths" are opposite answers, and collapsing them makes an
|
|
142
|
-
* unprobed library look like a library with no duplicates in it.
|
|
143
|
-
*/
|
|
144
|
-
export function durationsClose(a, b, tolerance = DURATION_TOLERANCE) {
|
|
145
|
-
if (!a || !b || a <= 0 || b <= 0)
|
|
146
|
-
return null;
|
|
147
|
-
return Math.abs(a - b) / Math.max(a, b) <= tolerance;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* The shared "these look like the same thing" test: the NAME agrees after normalization, the
|
|
151
|
-
* sizes are close, and nothing about the length says otherwise.
|
|
152
|
-
*
|
|
153
|
-
* It is deliberately a QUESTION and never an action. Both consumers act only on a checksum,
|
|
154
|
-
* which is proof; this decides what is worth showing a person side by side.
|
|
155
|
-
*/
|
|
156
|
-
export function likelyTheSameFile(a, b) {
|
|
157
|
-
const na = normalizeName(a.name);
|
|
158
|
-
const nb = normalizeName(b.name);
|
|
159
|
-
// Two empty names are not a match with each other — it means neither file has a name worth
|
|
160
|
-
// comparing (a bare digest key, an untitled upload), and pairing those proposes every
|
|
161
|
-
// unnamed object as a duplicate of every other.
|
|
162
|
-
if (!na || na !== nb)
|
|
163
|
-
return { match: false, reason: "" };
|
|
164
|
-
if (isGenericName(na))
|
|
165
|
-
return { match: false, reason: "" };
|
|
166
|
-
if (!sizesClose(a.sizeBytes, b.sizeBytes))
|
|
167
|
-
return { match: false, reason: "" };
|
|
168
|
-
if (durationsClose(a.duration, b.duration) === false)
|
|
169
|
-
return { match: false, reason: "" };
|
|
170
|
-
const pct = Math.round((Math.abs(a.sizeBytes - b.sizeBytes) / Math.max(a.sizeBytes, b.sizeBytes, 1)) * 100);
|
|
171
|
-
return {
|
|
172
|
-
match: true,
|
|
173
|
-
reason: a.sizeBytes === b.sizeBytes
|
|
174
|
-
? "same name, same size, different bytes"
|
|
175
|
-
: `same name, size within ${pct}%, different bytes`,
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/duplicates/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { Namespace } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Picks the next hue (0–359) for a new namespace by stepping around the color
|
|
4
|
-
* wheel by the golden angle once per existing namespace, then nudging past
|
|
5
|
-
* any exact collision with a hue already in use. Golden-angle stepping keeps
|
|
6
|
-
* consecutive hues far apart without tracking gaps explicitly.
|
|
7
|
-
*/
|
|
8
|
-
export declare function nextHue(existing: Namespace[]): number;
|
|
9
|
-
/**
|
|
10
|
-
* Trims and collapses internal whitespace in a namespace label, caps it at 40
|
|
11
|
-
* characters, and rejects a label that is empty/blank after normalization.
|
|
12
|
-
*/
|
|
13
|
-
export declare function normalizeNamespaceLabel(label: string): string;
|
|
14
|
-
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/core/namespace/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAOzC;;;;;GAKG;AACH,wBAAgB,OAAO,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,CAOrD;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI7D"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/** Irrational angle (in degrees) whose repeated steps around a 360° wheel
|
|
2
|
-
* never re-align on a short cycle, so successive hues stay maximally spread
|
|
3
|
-
* regardless of how many namespaces already exist. */
|
|
4
|
-
const GOLDEN_ANGLE = 137.50776405003785;
|
|
5
|
-
/**
|
|
6
|
-
* Picks the next hue (0–359) for a new namespace by stepping around the color
|
|
7
|
-
* wheel by the golden angle once per existing namespace, then nudging past
|
|
8
|
-
* any exact collision with a hue already in use. Golden-angle stepping keeps
|
|
9
|
-
* consecutive hues far apart without tracking gaps explicitly.
|
|
10
|
-
*/
|
|
11
|
-
export function nextHue(existing) {
|
|
12
|
-
const used = new Set(existing.map((n) => n.hue));
|
|
13
|
-
let hue = Math.round((existing.length * GOLDEN_ANGLE) % 360);
|
|
14
|
-
while (used.has(hue)) {
|
|
15
|
-
hue = (hue + 1) % 360;
|
|
16
|
-
}
|
|
17
|
-
return hue;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Trims and collapses internal whitespace in a namespace label, caps it at 40
|
|
21
|
-
* characters, and rejects a label that is empty/blank after normalization.
|
|
22
|
-
*/
|
|
23
|
-
export function normalizeNamespaceLabel(label) {
|
|
24
|
-
const normalized = label.trim().replace(/\s+/g, ' ').slice(0, 40);
|
|
25
|
-
if (!normalized)
|
|
26
|
-
throw new Error('namespace label must not be empty');
|
|
27
|
-
return normalized;
|
|
28
|
-
}
|
|
29
|
-
//# sourceMappingURL=helpers.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/core/namespace/helpers.ts"],"names":[],"mappings":"AAEA;;sDAEsD;AACtD,MAAM,YAAY,GAAG,kBAAkB,CAAC;AAExC;;;;;GAKG;AACH,MAAM,UAAU,OAAO,CAAC,QAAqB;IAC3C,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACjD,IAAI,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,MAAM,GAAG,YAAY,CAAC,GAAG,GAAG,CAAC,CAAC;IAC7D,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;IACxB,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAa;IACnD,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,UAAU;QAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACtE,OAAO,UAAU,CAAC;AACpB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/namespace/index.ts"],"names":[],"mappings":"AAIA,YAAY,EACV,SAAS,EACT,oBAAoB,EACpB,cAAc,EACd,oBAAoB,GACrB,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
// `cursedbelt/namespace` — reusable flat namespace/workspace model. Pure and
|
|
2
|
-
// React-free (the switcher/picker UI lives in the consuming app), so servers
|
|
3
|
-
// can import the contract + store without pulling any UI runtime.
|
|
4
|
-
export { nextHue, normalizeNamespaceLabel } from './helpers';
|
|
5
|
-
export { createMemoryNamespaceStore } from './memoryStore';
|
|
6
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/namespace/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6EAA6E;AAC7E,kEAAkE;AAQlE,OAAO,EAAE,OAAO,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import type { Namespace, NamespaceCreateInput, NamespaceUpdateInput } from './types';
|
|
2
|
-
/**
|
|
3
|
-
* Zero-config in-memory {@link NamespaceStore}: a single per-boot `Map`, so a
|
|
4
|
-
* host that wires nothing up still gets a working (non-persistent) namespace
|
|
5
|
-
* list — the same role `createMemoryCategoryStore` plays. Swap in a
|
|
6
|
-
* persistent store (f's `createSqliteNamespaceStore`) for real data.
|
|
7
|
-
*
|
|
8
|
-
* Deliberately not annotated `: NamespaceStore` — the interface allows async
|
|
9
|
-
* returns, which would force callers (and these tests) to `await` values that
|
|
10
|
-
* are always synchronous here. Structural typing keeps it assignable wherever
|
|
11
|
-
* a `NamespaceStore` is expected.
|
|
12
|
-
*/
|
|
13
|
-
export declare function createMemoryNamespaceStore(idFactory?: () => string, now?: () => number): {
|
|
14
|
-
list(): Namespace[];
|
|
15
|
-
create(input: NamespaceCreateInput): Namespace;
|
|
16
|
-
update(id: string, input: NamespaceUpdateInput): Namespace;
|
|
17
|
-
remove(id: string): void;
|
|
18
|
-
};
|
|
19
|
-
//# sourceMappingURL=memoryStore.d.ts.map
|