github-issue-tower-defence-management 1.116.9 → 1.117.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/CHANGELOG.md +7 -0
- package/bin/adapter/entry-points/console/consoleReadApi.js +13 -1
- package/bin/adapter/entry-points/console/consoleReadApi.js.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-CJJsRF8i.js +101 -0
- package/bin/adapter/entry-points/console/ui-dist/assets/{index-D1V2fi29.css → index-DGVKvwux.css} +1 -1
- package/bin/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js +1 -0
- package/bin/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.js.map +1 -1
- package/package.json +1 -1
- package/src/adapter/entry-points/console/consoleReadApi.test.ts +4 -0
- package/src/adapter/entry-points/console/consoleReadApi.ts +18 -0
- package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.stories.tsx +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.test.tsx +3 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsoleItemDetail.tsx +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.test.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestDetail.tsx +1 -1
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.stories.tsx +15 -5
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.test.tsx +38 -8
- package/src/adapter/entry-points/console/ui/src/features/console/components/detail/ConsolePullRequestStatusBadges.tsx +35 -12
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.test.ts +117 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleDetailPrefetch.ts +83 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.test.ts +2 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleItemDetailData.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.test.ts +67 -0
- package/src/adapter/entry-points/console/ui/src/features/console/hooks/useConsoleOperations.ts +26 -6
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.test.ts +21 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/consoleApi.ts +14 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.test.ts +48 -0
- package/src/adapter/entry-points/console/ui/src/features/console/lib/resourceCache.ts +24 -0
- package/src/adapter/entry-points/console/ui/src/features/console/logic/types.ts +4 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsoleItemDetailContainer.test.tsx +1 -0
- package/src/adapter/entry-points/console/ui/src/features/console/pages/ConsolePage.tsx +9 -1
- package/src/adapter/entry-points/console/ui/src/features/console/testing/fixtures.ts +1 -0
- package/src/adapter/entry-points/console/ui/src/index.css +10 -1
- package/src/adapter/entry-points/console/ui-dist/assets/index-CJJsRF8i.js +101 -0
- package/src/adapter/entry-points/console/ui-dist/assets/{index-D1V2fi29.css → index-DGVKvwux.css} +1 -1
- package/src/adapter/entry-points/console/ui-dist/index.html +2 -2
- package/src/adapter/entry-points/console/webServer.test.ts +2 -0
- package/src/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.ts +1 -0
- package/src/domain/usecases/CheckIssueReviewReadinessUseCase.test.ts +1 -0
- package/src/domain/usecases/IssueRejectionEvaluator.test.ts +1 -0
- package/src/domain/usecases/RevertOrphanedPreparationUseCase.test.ts +1 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +9 -0
- package/src/domain/usecases/adapter-interfaces/IssueRepository.ts +1 -0
- package/types/adapter/entry-points/console/consoleReadApi.d.ts +4 -0
- package/types/adapter/entry-points/console/consoleReadApi.d.ts.map +1 -1
- package/types/adapter/repositories/issue/ApiV3CheerioRestIssueRepository.d.ts.map +1 -1
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts +1 -0
- package/types/domain/usecases/adapter-interfaces/IssueRepository.d.ts.map +1 -1
- package/bin/adapter/entry-points/console/ui-dist/assets/index-Bm0wt3Mn.js +0 -101
- package/src/adapter/entry-points/console/ui-dist/assets/index-Bm0wt3Mn.js +0 -101
|
@@ -37,6 +37,54 @@ describe('ResourceCache', () => {
|
|
|
37
37
|
expect(await cache.load('k', 'u')).toBe('ok');
|
|
38
38
|
expect(fetcher).toHaveBeenCalledTimes(2);
|
|
39
39
|
});
|
|
40
|
+
|
|
41
|
+
it('prefetches into the cache and serves the next load from it', async () => {
|
|
42
|
+
const fetcher = jest.fn(async (url: string) => `value:${url}`);
|
|
43
|
+
const cache = new ResourceCache(fetcher);
|
|
44
|
+
cache.prefetch('k', 'u');
|
|
45
|
+
await Promise.resolve();
|
|
46
|
+
await Promise.resolve();
|
|
47
|
+
expect(cache.peek('k')).toBe('value:u');
|
|
48
|
+
expect(await cache.load('k', 'u')).toBe('value:u');
|
|
49
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('does not prefetch a key that is already cached', async () => {
|
|
53
|
+
const fetcher = jest.fn(async (url: string) => `value:${url}`);
|
|
54
|
+
const cache = new ResourceCache(fetcher);
|
|
55
|
+
await cache.load('k', 'u');
|
|
56
|
+
cache.prefetch('k', 'u');
|
|
57
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('does not start a second prefetch while one is in flight', async () => {
|
|
61
|
+
const fetcher = jest.fn(() => new Promise<string>(() => {}));
|
|
62
|
+
const cache = new ResourceCache(fetcher);
|
|
63
|
+
cache.prefetch('k', 'u');
|
|
64
|
+
cache.prefetch('k', 'u');
|
|
65
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('swallows prefetch errors and leaves the key uncached', async () => {
|
|
69
|
+
const fetcher = jest
|
|
70
|
+
.fn<Promise<string>, [string]>()
|
|
71
|
+
.mockRejectedValue(new Error('boom'));
|
|
72
|
+
const cache = new ResourceCache(fetcher);
|
|
73
|
+
cache.prefetch('k', 'u');
|
|
74
|
+
await Promise.resolve();
|
|
75
|
+
await Promise.resolve();
|
|
76
|
+
expect(cache.peek('k')).toBeUndefined();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('invalidate removes a cached value so the next load refetches', async () => {
|
|
80
|
+
const fetcher = jest.fn(async (url: string) => `value:${url}`);
|
|
81
|
+
const cache = new ResourceCache(fetcher);
|
|
82
|
+
await cache.load('k', 'u');
|
|
83
|
+
cache.invalidate('k');
|
|
84
|
+
expect(cache.peek('k')).toBeUndefined();
|
|
85
|
+
await cache.load('k', 'u');
|
|
86
|
+
expect(fetcher).toHaveBeenCalledTimes(2);
|
|
87
|
+
});
|
|
40
88
|
});
|
|
41
89
|
|
|
42
90
|
describe('runWithConcurrency', () => {
|
|
@@ -3,6 +3,7 @@ export type ResourceFetcher<T> = (url: string) => Promise<T>;
|
|
|
3
3
|
export class ResourceCache<T> {
|
|
4
4
|
private readonly cache = new Map<string, T>();
|
|
5
5
|
private readonly inFlight = new Map<string, Promise<T>>();
|
|
6
|
+
private readonly prefetchInFlight = new Set<string>();
|
|
6
7
|
|
|
7
8
|
constructor(private readonly fetcher: ResourceFetcher<T>) {}
|
|
8
9
|
|
|
@@ -30,6 +31,29 @@ export class ResourceCache<T> {
|
|
|
30
31
|
this.inFlight.set(key, promise);
|
|
31
32
|
return promise;
|
|
32
33
|
};
|
|
34
|
+
|
|
35
|
+
prefetch = (key: string, url: string): void => {
|
|
36
|
+
if (
|
|
37
|
+
this.cache.has(key) ||
|
|
38
|
+
this.inFlight.has(key) ||
|
|
39
|
+
this.prefetchInFlight.has(key)
|
|
40
|
+
) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.prefetchInFlight.add(key);
|
|
44
|
+
this.fetcher(url)
|
|
45
|
+
.then((value) => {
|
|
46
|
+
this.cache.set(key, value);
|
|
47
|
+
})
|
|
48
|
+
.catch(() => {})
|
|
49
|
+
.finally(() => {
|
|
50
|
+
this.prefetchInFlight.delete(key);
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
invalidate = (key: string): void => {
|
|
55
|
+
this.cache.delete(key);
|
|
56
|
+
};
|
|
33
57
|
}
|
|
34
58
|
|
|
35
59
|
export const runWithConcurrency = async (
|
|
@@ -64,9 +64,12 @@ export type ConsoleIssueState = {
|
|
|
64
64
|
title: string;
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
+
export type ConsoleMergeableStatus = 'MERGEABLE' | 'CONFLICTING' | 'UNKNOWN';
|
|
68
|
+
|
|
67
69
|
export type ConsolePullRequestStatus = {
|
|
68
70
|
found: boolean;
|
|
69
71
|
isConflicted: boolean;
|
|
72
|
+
mergeableStatus: ConsoleMergeableStatus;
|
|
70
73
|
isPassedAllCiJob: boolean;
|
|
71
74
|
isCiStateSuccess: boolean;
|
|
72
75
|
isBranchOutOfDate: boolean;
|
|
@@ -100,6 +103,7 @@ export type ConsoleRelatedPullRequest = {
|
|
|
100
103
|
createdAt: string;
|
|
101
104
|
isDraft: boolean;
|
|
102
105
|
isConflicted: boolean;
|
|
106
|
+
mergeableStatus: ConsoleMergeableStatus;
|
|
103
107
|
isPassedAllCiJob: boolean;
|
|
104
108
|
isCiStateSuccess: boolean;
|
|
105
109
|
isResolvedAllReviewComments: boolean;
|
|
@@ -44,6 +44,7 @@ const buildCaches = (overrides: CachesOverrides = {}): ConsoleCaches => {
|
|
|
44
44
|
fetchPullRequestStatus: async () => ({
|
|
45
45
|
found: true,
|
|
46
46
|
isConflicted: false,
|
|
47
|
+
mergeableStatus: 'MERGEABLE' as const,
|
|
47
48
|
isPassedAllCiJob: true,
|
|
48
49
|
isCiStateSuccess: true,
|
|
49
50
|
isBranchOutOfDate: false,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from '../components/operations/ConsoleUndoToast';
|
|
8
8
|
import { useConsoleActionQueue } from '../hooks/useConsoleActionQueue';
|
|
9
9
|
import { useConsoleCaches } from '../hooks/useConsoleCaches';
|
|
10
|
+
import { useConsoleDetailPrefetch } from '../hooks/useConsoleDetailPrefetch';
|
|
10
11
|
import { useConsoleNavigation } from '../hooks/useConsoleNavigation';
|
|
11
12
|
import { useConsoleOperations } from '../hooks/useConsoleOperations';
|
|
12
13
|
import { useConsoleOverlay } from '../hooks/useConsoleOverlay';
|
|
@@ -78,7 +79,12 @@ export const ConsolePage = () => {
|
|
|
78
79
|
navigation;
|
|
79
80
|
|
|
80
81
|
const caches = useConsoleCaches();
|
|
81
|
-
const operations = useConsoleOperations(
|
|
82
|
+
const operations = useConsoleOperations(
|
|
83
|
+
pjcode,
|
|
84
|
+
activeTab,
|
|
85
|
+
overlayState,
|
|
86
|
+
caches,
|
|
87
|
+
);
|
|
82
88
|
const actionQueue = useConsoleActionQueue();
|
|
83
89
|
const now = Date.now();
|
|
84
90
|
|
|
@@ -120,6 +126,8 @@ export const ConsolePage = () => {
|
|
|
120
126
|
);
|
|
121
127
|
}, [selectedItemKey, activeSnapshot]);
|
|
122
128
|
|
|
129
|
+
useConsoleDetailPrefetch(caches, selectedItem, pendingItems);
|
|
130
|
+
|
|
123
131
|
useEffect(() => {
|
|
124
132
|
if (selectedItemKey === null) {
|
|
125
133
|
return;
|
|
@@ -343,6 +343,7 @@ export const consoleRelatedPullRequestsFixture: ConsoleRelatedPullRequest[] = [
|
|
|
343
343
|
createdAt: '2026-06-16T22:30:00.000Z',
|
|
344
344
|
isDraft: false,
|
|
345
345
|
isConflicted: false,
|
|
346
|
+
mergeableStatus: 'MERGEABLE',
|
|
346
347
|
isPassedAllCiJob: true,
|
|
347
348
|
isCiStateSuccess: true,
|
|
348
349
|
isResolvedAllReviewComments: true,
|
|
@@ -351,6 +351,15 @@ body {
|
|
|
351
351
|
opacity: 0.85;
|
|
352
352
|
}
|
|
353
353
|
|
|
354
|
+
.console-detail-mergeable-chip {
|
|
355
|
+
font-weight: 600;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
.console-detail-mergeable-chip-unknown {
|
|
359
|
+
opacity: 0.7;
|
|
360
|
+
font-weight: 400;
|
|
361
|
+
}
|
|
362
|
+
|
|
354
363
|
.console-detail-labels {
|
|
355
364
|
display: flex;
|
|
356
365
|
flex-wrap: wrap;
|
|
@@ -655,7 +664,7 @@ body {
|
|
|
655
664
|
}
|
|
656
665
|
|
|
657
666
|
.console-op-button {
|
|
658
|
-
padding:
|
|
667
|
+
padding: 9px 16px;
|
|
659
668
|
border: 1px solid #30363d;
|
|
660
669
|
border-radius: 6px;
|
|
661
670
|
background: #21262d;
|