@atlaspack/inspector-frontend 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/.atlaspackrc +4 -0
  2. package/.eslintrc.json +19 -0
  3. package/CHANGELOG.md +12 -0
  4. package/dist/atlassian-dark-brand-refresh.91b786da.js +2 -0
  5. package/dist/atlassian-dark-brand-refresh.91b786da.js.map +1 -0
  6. package/dist/atlassian-dark-future.59ebadca.js +2 -0
  7. package/dist/atlassian-dark-future.59ebadca.js.map +1 -0
  8. package/dist/atlassian-dark-increased-contrast.ff6775f2.js +2 -0
  9. package/dist/atlassian-dark-increased-contrast.ff6775f2.js.map +1 -0
  10. package/dist/atlassian-dark.ad679134.js +2 -0
  11. package/dist/atlassian-dark.ad679134.js.map +1 -0
  12. package/dist/atlassian-legacy-dark.8aa27f7f.js +2 -0
  13. package/dist/atlassian-legacy-dark.8aa27f7f.js.map +1 -0
  14. package/dist/atlassian-legacy-light.2eb372ce.js +2 -0
  15. package/dist/atlassian-legacy-light.2eb372ce.js.map +1 -0
  16. package/dist/atlassian-light-brand-refresh.fadcab0a.js +2 -0
  17. package/dist/atlassian-light-brand-refresh.fadcab0a.js.map +1 -0
  18. package/dist/atlassian-light-future.612afe8a.js +2 -0
  19. package/dist/atlassian-light-future.612afe8a.js.map +1 -0
  20. package/dist/atlassian-light-increased-contrast.7161cd79.js +2 -0
  21. package/dist/atlassian-light-increased-contrast.7161cd79.js.map +1 -0
  22. package/dist/atlassian-light.bc343d4c.js +2 -0
  23. package/dist/atlassian-light.bc343d4c.js.map +1 -0
  24. package/dist/atlassian-shape.b92d69c0.js +2 -0
  25. package/dist/atlassian-shape.b92d69c0.js.map +1 -0
  26. package/dist/atlassian-spacing.60ddd8e7.js +2 -0
  27. package/dist/atlassian-spacing.60ddd8e7.js.map +1 -0
  28. package/dist/atlassian-typography-adg3.f88947f6.js +2 -0
  29. package/dist/atlassian-typography-adg3.f88947f6.js.map +1 -0
  30. package/dist/atlassian-typography-modernized.42016c51.js +2 -0
  31. package/dist/atlassian-typography-modernized.42016c51.js.map +1 -0
  32. package/dist/atlassian-typography-refreshed.ec0d111b.js +2 -0
  33. package/dist/atlassian-typography-refreshed.ec0d111b.js.map +1 -0
  34. package/dist/atlassian-typography.66d7e8f4.js +2 -0
  35. package/dist/atlassian-typography.66d7e8f4.js.map +1 -0
  36. package/dist/badge-light.7e55986a.png +0 -0
  37. package/dist/custom-theme.4680282a.js +2 -0
  38. package/dist/custom-theme.4680282a.js.map +1 -0
  39. package/dist/drag-handle.136830d3.js +2 -0
  40. package/dist/drag-handle.136830d3.js.map +1 -0
  41. package/dist/drag-handle.63bdb345.css +2 -0
  42. package/dist/drag-handle.63bdb345.css.map +1 -0
  43. package/dist/index.13289f53.js +28 -0
  44. package/dist/index.13289f53.js.map +1 -0
  45. package/dist/index.a41fafce.css +2 -0
  46. package/dist/index.a41fafce.css.map +1 -0
  47. package/dist/index.html +1 -0
  48. package/dist/index.runtime.3c39d71d.js +2 -0
  49. package/dist/index.runtime.3c39d71d.js.map +1 -0
  50. package/dist/refractor.2c1fd9a1.js +2 -0
  51. package/dist/refractor.2c1fd9a1.js.map +1 -0
  52. package/index.html +11 -0
  53. package/jest.config.js +16 -0
  54. package/package.json +64 -0
  55. package/src/APIError.test.ts +72 -0
  56. package/src/APIError.tsx +29 -0
  57. package/src/AppRoutes.tsx +56 -0
  58. package/src/hack-feature-flags.ts +6 -0
  59. package/src/main.tsx +50 -0
  60. package/src/test/stubCssModule.js +1 -0
  61. package/src/ui/App.module.css +122 -0
  62. package/src/ui/App.module.css.d.ts +8 -0
  63. package/src/ui/AppLayout/AppLayout.tsx +26 -0
  64. package/src/ui/AppLayout/SidebarNavigation/LinkItem.tsx +26 -0
  65. package/src/ui/AppLayout/SidebarNavigation/SidebarNavigation.tsx +45 -0
  66. package/src/ui/AppLayout/TopNavigation/Logo.module.css +12 -0
  67. package/src/ui/AppLayout/TopNavigation/Logo.module.css.d.ts +4 -0
  68. package/src/ui/AppLayout/TopNavigation/Logo.tsx +11 -0
  69. package/src/ui/AppLayout/TopNavigation/TopNavigation.module.css +14 -0
  70. package/src/ui/AppLayout/TopNavigation/TopNavigation.module.css.d.ts +3 -0
  71. package/src/ui/AppLayout/TopNavigation/TopNavigation.tsx +45 -0
  72. package/src/ui/AppLayout/TopNavigation/badge-light.png +0 -0
  73. package/src/ui/AppLayout/TopNavigation/logo-light.png +0 -0
  74. package/src/ui/DefaultLoadingIndicator/DefaultLoadingIndicator.module.css +9 -0
  75. package/src/ui/DefaultLoadingIndicator/DefaultLoadingIndicator.module.css.d.ts +3 -0
  76. package/src/ui/DefaultLoadingIndicator/DefaultLoadingIndicator.test.tsx +15 -0
  77. package/src/ui/DefaultLoadingIndicator/DefaultLoadingIndicator.tsx +14 -0
  78. package/src/ui/app/StatsPage.tsx +77 -0
  79. package/src/ui/app/cache/CacheKeysIndexPage.tsx +13 -0
  80. package/src/ui/app/cache/CacheKeysPage.module.css +11 -0
  81. package/src/ui/app/cache/CacheKeysPage.module.css.d.ts +4 -0
  82. package/src/ui/app/cache/CacheKeysPage.tsx +23 -0
  83. package/src/ui/app/cache/[key]/CacheValuePage.tsx +40 -0
  84. package/src/ui/app/cache/ui/CacheKeyList.module.css +40 -0
  85. package/src/ui/app/cache/ui/CacheKeyList.module.css.d.ts +7 -0
  86. package/src/ui/app/cache/ui/CacheKeyList.tsx +187 -0
  87. package/src/ui/app/cache-invalidation/CacheInvalidationPage.tsx +22 -0
  88. package/src/ui/app/cache-invalidation/[fileId]/CacheInvalidationFilePage.tsx +22 -0
  89. package/src/ui/app/cache-invalidation/ui/CacheFileList.module.css +40 -0
  90. package/src/ui/app/cache-invalidation/ui/CacheFileList.module.css.d.ts +7 -0
  91. package/src/ui/app/cache-invalidation/ui/CacheFileList.tsx +185 -0
  92. package/src/ui/app/treemap/BottomPanelResizeState.test.ts +25 -0
  93. package/src/ui/app/treemap/BottomPanelResizeState.tsx +48 -0
  94. package/src/ui/app/treemap/FoamTreemapPage.module.css +24 -0
  95. package/src/ui/app/treemap/FoamTreemapPage.module.css.d.ts +6 -0
  96. package/src/ui/app/treemap/FoamTreemapPage.tsx +47 -0
  97. package/src/ui/app/treemap/controllers/RelatedBundlesController.tsx +41 -0
  98. package/src/ui/app/treemap/controllers/UrlFocusController.tsx +33 -0
  99. package/src/ui/app/treemap/ui/BottomPanel/BottomPanel.module.css +24 -0
  100. package/src/ui/app/treemap/ui/BottomPanel/BottomPanel.module.css.d.ts +5 -0
  101. package/src/ui/app/treemap/ui/BottomPanel/BottomPanel.tsx +24 -0
  102. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AdvancedSettings.module.css +13 -0
  103. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AdvancedSettings.module.css.d.ts +5 -0
  104. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AdvancedSettings.tsx +53 -0
  105. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/AssetTable.tsx +135 -0
  106. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTable.module.css +7 -0
  107. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTable.module.css.d.ts +3 -0
  108. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTable.tsx +123 -0
  109. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTableModel.tsx +18 -0
  110. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTableRow.module.css +20 -0
  111. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTableRow.module.css.d.ts +6 -0
  112. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/CollapsibleTable/CollapsibleTableRow.tsx +79 -0
  113. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/getFileURL.test.ts +19 -0
  114. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/AssetTable/getFileURL.ts +24 -0
  115. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/FocusedGroupInfo.module.css +20 -0
  116. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/FocusedGroupInfo.module.css.d.ts +5 -0
  117. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/FocusedGroupInfo.tsx +42 -0
  118. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/FocusedGroupInfoInner.module.css +29 -0
  119. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/FocusedGroupInfoInner.module.css.d.ts +6 -0
  120. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/FocusedGroupInfoInner.tsx +107 -0
  121. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/GraphContainer.module.css +7 -0
  122. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/GraphContainer.module.css.d.ts +3 -0
  123. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/GraphContainer.tsx +20 -0
  124. package/src/ui/app/treemap/ui/BottomPanel/FocusedGroupInfo/SourceCodeURL.tsx +5 -0
  125. package/src/ui/app/treemap/ui/BundleGraphRenderer.module.css +13 -0
  126. package/src/ui/app/treemap/ui/BundleGraphRenderer.module.css.d.ts +4 -0
  127. package/src/ui/app/treemap/ui/BundleGraphRenderer.tsx +95 -0
  128. package/src/ui/app/treemap/ui/FocusBreadcrumbs/FocusBreadcrumbs.module.css +6 -0
  129. package/src/ui/app/treemap/ui/FocusBreadcrumbs/FocusBreadcrumbs.module.css.d.ts +3 -0
  130. package/src/ui/app/treemap/ui/FocusBreadcrumbs/FocusBreadcrumbs.tsx +49 -0
  131. package/src/ui/app/treemap/ui/SigmaGraph.module.css +5 -0
  132. package/src/ui/app/treemap/ui/SigmaGraph.module.css.d.ts +3 -0
  133. package/src/ui/app/treemap/ui/SigmaGraph.tsx +80 -0
  134. package/src/ui/app/treemap/ui/Treemap.tsx +14 -0
  135. package/src/ui/app/treemap/ui/TreemapRenderer/ImpactScore.module.css +32 -0
  136. package/src/ui/app/treemap/ui/TreemapRenderer/ImpactScore.module.css.d.ts +5 -0
  137. package/src/ui/app/treemap/ui/TreemapRenderer/ImpactScore.tsx +24 -0
  138. package/src/ui/app/treemap/ui/TreemapRenderer/TreemapRenderer.module.css +14 -0
  139. package/src/ui/app/treemap/ui/TreemapRenderer/TreemapRenderer.module.css.d.ts +4 -0
  140. package/src/ui/app/treemap/ui/TreemapRenderer/TreemapRenderer.tsx +271 -0
  141. package/src/ui/app/treemap/ui/TreemapRenderer/TreemapTooltip.module.css +15 -0
  142. package/src/ui/app/treemap/ui/TreemapRenderer/TreemapTooltip.module.css.d.ts +4 -0
  143. package/src/ui/app/treemap/ui/TreemapRenderer/TreemapTooltip.tsx +111 -0
  144. package/src/ui/app/treemap/ui/TreemapRenderer/controllers/useStableCallback.test.ts +27 -0
  145. package/src/ui/app/treemap/ui/TreemapRenderer/controllers/useStableCallback.ts +21 -0
  146. package/src/ui/app/treemap/ui/TreemapRenderer/useMouseMoveController.ts +20 -0
  147. package/src/ui/globals.css +26 -0
  148. package/src/ui/globals.css.d.ts +1 -0
  149. package/src/ui/globals.d.ts +9 -0
  150. package/src/ui/model/ViewModel.test.ts +31 -0
  151. package/src/ui/model/ViewModel.ts +62 -0
  152. package/src/ui/not-found/NotFoundPage.module.css +7 -0
  153. package/src/ui/not-found/NotFoundPage.module.css.d.ts +3 -0
  154. package/src/ui/not-found/NotFoundPage.tsx +9 -0
  155. package/src/ui/types/Graph.tsx +12 -0
  156. package/src/ui/util/ErrorBoundary.module.css +3 -0
  157. package/src/ui/util/ErrorBoundary.module.css.d.ts +3 -0
  158. package/src/ui/util/ErrorBoundary.test.tsx +65 -0
  159. package/src/ui/util/ErrorBoundary.tsx +75 -0
  160. package/src/ui/util/colorPalette.tsx +122 -0
  161. package/src/ui/util/formatBytes.test.ts +13 -0
  162. package/src/ui/util/formatBytes.tsx +9 -0
  163. package/src/ui/util/getRandomDarkerColor.tsx +31 -0
  164. package/tsconfig.json +12 -0
@@ -0,0 +1,14 @@
1
+ import Spinner from '@atlaskit/spinner';
2
+ import * as styles from './DefaultLoadingIndicator.module.css';
3
+
4
+ export function DefaultLoadingIndicator({message}: {message?: string}) {
5
+ return (
6
+ <div
7
+ className={styles.defaultLoadingIndicator}
8
+ data-testid="atlaspack-inspector-loading-indicator"
9
+ >
10
+ <Spinner size="xlarge" />
11
+ <h2>{message ?? 'Loading cache stats...'}</h2>
12
+ </div>
13
+ );
14
+ }
@@ -0,0 +1,77 @@
1
+ import {useSuspenseQuery} from '@tanstack/react-query';
2
+ import {Box, Stack} from '@atlaskit/primitives';
3
+
4
+ import {formatBytes} from '../util/formatBytes';
5
+
6
+ export function StatsPage() {
7
+ const {data} = useSuspenseQuery<{
8
+ size: number;
9
+ keySize: number;
10
+ count: number;
11
+ assetContentCount: number;
12
+ assetContentSize: number;
13
+ assetMapCount: number;
14
+ assetMapSize: number;
15
+ }>({
16
+ queryKey: ['/api/stats'],
17
+ });
18
+
19
+ return (
20
+ <Box padding="space.100">
21
+ <Stack space="space.100">
22
+ <Box>
23
+ <h1>Atlaspack cache stats</h1>
24
+ </Box>
25
+
26
+ <table>
27
+ <tbody>
28
+ <tr>
29
+ <td>
30
+ <strong>Cache size</strong>
31
+ </td>
32
+ <td>{formatBytes(data.size)}</td>
33
+ </tr>
34
+ <tr>
35
+ <td>
36
+ <strong>Total size of all keys</strong>
37
+ </td>
38
+ <td>{formatBytes(data.keySize)}</td>
39
+ </tr>
40
+ <tr>
41
+ <td>
42
+ <strong>Number of cache entries</strong>
43
+ </td>
44
+ <td>{data.count}</td>
45
+ </tr>
46
+
47
+ <tr>
48
+ <td>
49
+ <strong>Number of asset content entries</strong>
50
+ </td>
51
+ <td>{data.assetContentCount}</td>
52
+ </tr>
53
+ <tr>
54
+ <td>
55
+ <strong>Total size of all asset content entries</strong>
56
+ </td>
57
+ <td>{formatBytes(data.assetContentSize)}</td>
58
+ </tr>
59
+
60
+ <tr>
61
+ <td>
62
+ <strong>Number of asset map entries</strong>
63
+ </td>
64
+ <td>{data.assetMapCount}</td>
65
+ </tr>
66
+ <tr>
67
+ <td>
68
+ <strong>Total size of all asset map entries</strong>
69
+ </td>
70
+ <td>{formatBytes(data.assetMapSize)}</td>
71
+ </tr>
72
+ </tbody>
73
+ </table>
74
+ </Stack>
75
+ </Box>
76
+ );
77
+ }
@@ -0,0 +1,13 @@
1
+ export function CacheKeysIndexPage() {
2
+ return (
3
+ <>
4
+ <p>
5
+ <strong>Click a cache key to view its contents</strong>
6
+ </p>
7
+ <p>
8
+ You can see all keys in the cache on your left. Click sorting button to
9
+ switch the sorting key.
10
+ </p>
11
+ </>
12
+ );
13
+ }
@@ -0,0 +1,11 @@
1
+ .cacheKeysPage {
2
+ display: flex;
3
+ height: calc(100vh - 48px);
4
+ width: 100%;
5
+ }
6
+
7
+ .cacheKeysPageChild {
8
+ overflow: auto;
9
+ flex: 1;
10
+ max-height: 100%;
11
+ }
@@ -0,0 +1,4 @@
1
+ export const __esModule: true;
2
+ export const cacheKeysPage: string;
3
+ export const cacheKeysPageChild: string;
4
+
@@ -0,0 +1,23 @@
1
+ import {Outlet} from 'react-router';
2
+ import {Box} from '@atlaskit/primitives';
3
+ import {Suspense} from 'react';
4
+
5
+ import {CacheKeyList} from './ui/CacheKeyList';
6
+ import {DefaultLoadingIndicator} from '../../DefaultLoadingIndicator/DefaultLoadingIndicator';
7
+ import * as styles from './CacheKeysPage.module.css';
8
+
9
+ export function CacheKeysPage() {
10
+ return (
11
+ <div className={styles.cacheKeysPage}>
12
+ <CacheKeyList />
13
+
14
+ <div className={styles.cacheKeysPageChild}>
15
+ <Box padding="space.200">
16
+ <Suspense fallback={<DefaultLoadingIndicator />}>
17
+ <Outlet />
18
+ </Suspense>
19
+ </Box>
20
+ </div>
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,40 @@
1
+ import {useParams} from 'react-router';
2
+ import {useSuspenseQuery} from '@tanstack/react-query';
3
+ import {Code, CodeBlock} from '@atlaskit/code';
4
+ import {Box, Inline, Stack} from '@atlaskit/primitives';
5
+
6
+ import {formatBytes} from '../../../util/formatBytes';
7
+
8
+ interface CacheValueResponse {
9
+ size: number;
10
+ value: string;
11
+ }
12
+
13
+ export function CacheValuePage() {
14
+ const key = useParams().key;
15
+ if (!key) {
16
+ throw new Error('No key');
17
+ }
18
+
19
+ const {data: cacheValue} = useSuspenseQuery<CacheValueResponse>({
20
+ queryKey: [`/api/cache-value/${encodeURIComponent(key)}`],
21
+ });
22
+
23
+ return (
24
+ <Stack space="space.100">
25
+ <Box>
26
+ <strong>Cache entry</strong>
27
+ </Box>
28
+ <Box>
29
+ <Code>{key}</Code>
30
+ </Box>
31
+
32
+ <Inline space="space.100">
33
+ <Box>Cache entry size</Box>
34
+ <Code>{formatBytes(cacheValue.size)}</Code>
35
+ </Inline>
36
+
37
+ <CodeBlock text={cacheValue.value} />
38
+ </Stack>
39
+ );
40
+ }
@@ -0,0 +1,40 @@
1
+ .cacheKeyList {
2
+ padding: 8px;
3
+ height: 100%;
4
+ gap: 8px;
5
+ display: flex;
6
+ background-color: var(--ds-surface-sunken);
7
+ flex-direction: column;
8
+ border: 1px solid var(--ds-border);
9
+ width: 300px;
10
+ flex-shrink: 0;
11
+ }
12
+
13
+ .cacheKeyListInner {
14
+ background-color: var(--ds-background-input);
15
+ padding: 4px;
16
+ border: 1px solid var(--ds-border);
17
+ border-radius: 4px;
18
+ flex: 1;
19
+ overflow: hidden;
20
+ gap: 4px;
21
+ }
22
+
23
+ .cacheKeyPlaceholderContainer {
24
+ flex: 1;
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ }
29
+
30
+ .cacheKeyListItemsContainer {
31
+ flex: 1;
32
+ height: 100%;
33
+ width: 100%;
34
+ overflow-y: auto;
35
+ position: relative;
36
+ }
37
+
38
+ .cacheKeyListItemsContainerInner {
39
+ width: 100%;
40
+ }
@@ -0,0 +1,7 @@
1
+ export const __esModule: true;
2
+ export const cacheKeyList: string;
3
+ export const cacheKeyListInner: string;
4
+ export const cacheKeyListItemsContainer: string;
5
+ export const cacheKeyListItemsContainerInner: string;
6
+ export const cacheKeyPlaceholderContainer: string;
7
+
@@ -0,0 +1,187 @@
1
+ import {useNavigate, useSearchParams} from 'react-router';
2
+ import {useVirtualizer} from '@tanstack/react-virtual';
3
+ import {useEffect, useMemo, useRef} from 'react';
4
+ import {useInfiniteQuery} from '@tanstack/react-query';
5
+ import qs from 'qs';
6
+ import axios from 'axios';
7
+ import Button, {LinkButton} from '@atlaskit/button/new';
8
+ import {Box} from '@atlaskit/primitives';
9
+
10
+ import * as styles from './CacheKeyList.module.css';
11
+ import * as appStyles from '../../../App.module.css';
12
+
13
+ /**
14
+ * The list of cache keys is an infinite paginated, virtualized list.
15
+ *
16
+ * That is because there might be a very large amount of keys to render.
17
+ */
18
+ export function CacheKeyList() {
19
+ // sort by in querystring URL
20
+ const [searchParams, setSearchParams] = useSearchParams();
21
+ const sortBy = searchParams.get('sortBy') || 'order';
22
+ const setSortBy = (value: string) => {
23
+ searchParams.set('sortBy', value);
24
+ setSearchParams(searchParams);
25
+ };
26
+
27
+ const {
28
+ data: cacheKeys,
29
+ isLoading,
30
+ error,
31
+ fetchNextPage,
32
+ isFetchingNextPage,
33
+ hasNextPage,
34
+ } = useInfiniteQuery<{
35
+ keys: string[];
36
+ count: number;
37
+ hasNextPage: boolean;
38
+ nextPageCursor: string | null;
39
+ }>({
40
+ queryFn: async ({pageParam}) => {
41
+ const backendUrl = process.env.ATLASPACK_INSPECTOR_BACKEND_URL;
42
+ const {data} = await axios.get(
43
+ `${backendUrl}/api/cache-keys/?` +
44
+ qs.stringify({sortBy, cursor: pageParam}),
45
+ );
46
+ return data;
47
+ },
48
+ queryKey: ['/api/cache-keys/?' + qs.stringify({sortBy})],
49
+ initialPageParam: null,
50
+ getNextPageParam: (lastPage) => lastPage.nextPageCursor,
51
+ });
52
+
53
+ const allKeys = useMemo(
54
+ () => cacheKeys?.pages.flatMap((page) => page.keys) ?? [],
55
+ [cacheKeys?.pages],
56
+ );
57
+ const lastPage = cacheKeys?.pages[cacheKeys.pages.length - 1];
58
+ const parentRef = useRef<HTMLDivElement>(null);
59
+ const rowVirtualizer = useVirtualizer({
60
+ count: lastPage?.hasNextPage ? allKeys.length + 1 : allKeys.length,
61
+ getScrollElement: () => parentRef.current,
62
+ estimateSize: () => 35,
63
+ });
64
+
65
+ const virtualItems = rowVirtualizer.getVirtualItems();
66
+ useEffect(() => {
67
+ const lastVirtualItem = virtualItems[virtualItems.length - 1];
68
+ const loaderRowIsShown =
69
+ allKeys[lastVirtualItem?.index] == null && lastPage?.hasNextPage;
70
+
71
+ if (hasNextPage && loaderRowIsShown && !isFetchingNextPage) {
72
+ fetchNextPage();
73
+ }
74
+ }, [
75
+ allKeys,
76
+ allKeys.length,
77
+ fetchNextPage,
78
+ hasNextPage,
79
+ isFetchingNextPage,
80
+ lastPage,
81
+ lastPage?.hasNextPage,
82
+ virtualItems,
83
+ ]);
84
+
85
+ const navigate = useNavigate();
86
+
87
+ const renderItems = () => {
88
+ if (isLoading) {
89
+ return (
90
+ <div className={styles.cacheKeyPlaceholderContainer}>Loading...</div>
91
+ );
92
+ }
93
+
94
+ if (error) {
95
+ return (
96
+ <div className={styles.cacheKeyPlaceholderContainer}>
97
+ Error: {error.message}
98
+ </div>
99
+ );
100
+ }
101
+
102
+ if (!allKeys.length) {
103
+ return (
104
+ <div className={styles.cacheKeyPlaceholderContainer}>No cache keys</div>
105
+ );
106
+ }
107
+
108
+ return (
109
+ <div className={styles.cacheKeyListItemsContainer} ref={parentRef}>
110
+ <div
111
+ className={styles.cacheKeyListItemsContainerInner}
112
+ style={{
113
+ height: `${rowVirtualizer.getTotalSize()}px`,
114
+ }}
115
+ >
116
+ {rowVirtualizer.getVirtualItems().map((virtualItem) => {
117
+ const key = allKeys[virtualItem.index];
118
+ const isLoaderRow = key == null && lastPage?.hasNextPage;
119
+ const rowStyle = {
120
+ position: 'absolute',
121
+ top: 0,
122
+ left: 0,
123
+ width: '100%',
124
+ height: `${virtualItem.size}px`,
125
+ transform: `translateY(${virtualItem.start}px)`,
126
+ } as const;
127
+
128
+ if (isLoaderRow) {
129
+ return (
130
+ <div
131
+ key="loader"
132
+ className={appStyles.sidebarItem}
133
+ style={rowStyle}
134
+ >
135
+ Loading...
136
+ </div>
137
+ );
138
+ }
139
+ const href = `/app/cache/${encodeURIComponent(
140
+ key,
141
+ )}?${searchParams.toString()}`;
142
+
143
+ return (
144
+ <div
145
+ key={virtualItem.index}
146
+ className={appStyles.sidebarItem}
147
+ style={rowStyle}
148
+ >
149
+ <LinkButton
150
+ appearance="subtle"
151
+ href={href}
152
+ onClick={(e) => {
153
+ e.preventDefault();
154
+ navigate(href);
155
+ }}
156
+ >
157
+ {key}
158
+ </LinkButton>
159
+ </div>
160
+ );
161
+ })}
162
+ </div>
163
+ </div>
164
+ );
165
+ };
166
+
167
+ return (
168
+ <div className={styles.cacheKeyList}>
169
+ <Box>
170
+ <Button
171
+ appearance="subtle"
172
+ onClick={() => {
173
+ if (sortBy === 'order') {
174
+ setSortBy('size');
175
+ } else {
176
+ setSortBy('order');
177
+ }
178
+ }}
179
+ >
180
+ Sorting by {sortBy}
181
+ </Button>
182
+ </Box>
183
+
184
+ <div className={styles.cacheKeyListInner}>{renderItems()}</div>
185
+ </div>
186
+ );
187
+ }
@@ -0,0 +1,22 @@
1
+ import {CacheFileList} from './ui/CacheFileList';
2
+ import * as styles from '../cache/CacheKeysPage.module.css';
3
+ import {Box} from '@atlaskit/primitives';
4
+ import {DefaultLoadingIndicator} from '../../DefaultLoadingIndicator/DefaultLoadingIndicator';
5
+ import {Suspense} from 'react';
6
+ import {Outlet} from 'react-router';
7
+
8
+ export function CacheInvalidationPage() {
9
+ return (
10
+ <div className={styles.cacheKeysPage}>
11
+ <CacheFileList />
12
+
13
+ <div className={styles.cacheKeysPageChild}>
14
+ <Box padding="space.200">
15
+ <Suspense fallback={<DefaultLoadingIndicator />}>
16
+ <Outlet />
17
+ </Suspense>
18
+ </Box>
19
+ </div>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,22 @@
1
+ import {useSuspenseQuery} from '@tanstack/react-query';
2
+ import {useParams} from 'react-router';
3
+
4
+ export function CacheInvalidationFilePage() {
5
+ const {fileId} = useParams();
6
+
7
+ if (!fileId) {
8
+ throw new Error('Invalid request, missing file ID');
9
+ }
10
+
11
+ const {data} = useSuspenseQuery({
12
+ queryKey: [`/api/cache-invalidation-files/${encodeURIComponent(fileId)}`],
13
+ });
14
+
15
+ return (
16
+ <div>
17
+ <h1>Cache invalidation file {fileId}</h1>
18
+
19
+ <pre>{JSON.stringify(data, null, 2)}</pre>
20
+ </div>
21
+ );
22
+ }
@@ -0,0 +1,40 @@
1
+ .cacheFileList {
2
+ padding: 8px;
3
+ height: 100%;
4
+ gap: 8px;
5
+ display: flex;
6
+ background-color: var(--ds-surface-sunken);
7
+ flex-direction: column;
8
+ border: 1px solid var(--ds-border);
9
+ width: 300px;
10
+ flex-shrink: 0;
11
+ }
12
+
13
+ .cacheFileListInner {
14
+ background-color: var(--ds-background-input);
15
+ padding: 4px;
16
+ border: 1px solid var(--ds-border);
17
+ border-radius: 4px;
18
+ flex: 1;
19
+ overflow: hidden;
20
+ gap: 4px;
21
+ }
22
+
23
+ .cacheFilePlaceholderContainer {
24
+ flex: 1;
25
+ display: flex;
26
+ align-items: center;
27
+ justify-content: center;
28
+ }
29
+
30
+ .cacheFileListItemsContainer {
31
+ flex: 1;
32
+ height: 100%;
33
+ width: 100%;
34
+ overflow-y: auto;
35
+ position: relative;
36
+ }
37
+
38
+ .cacheFileListItemsContainerInner {
39
+ width: 100%;
40
+ }
@@ -0,0 +1,7 @@
1
+ export const __esModule: true;
2
+ export const cacheFileList: string;
3
+ export const cacheFileListInner: string;
4
+ export const cacheFileListItemsContainer: string;
5
+ export const cacheFileListItemsContainerInner: string;
6
+ export const cacheFilePlaceholderContainer: string;
7
+