github-issue-tower-defence-management 1.116.8 → 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 +14 -0
- package/README.md +1 -1
- 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/bin/domain/usecases/NotifySilentLiveSessionsUseCase.js +10 -2
- package/bin/domain/usecases/NotifySilentLiveSessionsUseCase.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/entry-points/handlers/notifySilentTmuxSessions.test.ts +30 -2
- 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/NotifySilentLiveSessionsUseCase.test.ts +194 -33
- package/src/domain/usecases/NotifySilentLiveSessionsUseCase.ts +19 -1
- 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/NotifySilentLiveSessionsUseCase.d.ts +1 -0
- package/types/domain/usecases/NotifySilentLiveSessionsUseCase.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
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
ConsoleComment,
|
|
4
4
|
ConsoleCommit,
|
|
5
5
|
ConsoleIssueState,
|
|
6
|
+
ConsoleMergeableStatus,
|
|
6
7
|
ConsolePullRequestStatus,
|
|
7
8
|
ConsoleRelatedPullRequest,
|
|
8
9
|
} from '../logic/types';
|
|
@@ -136,6 +137,16 @@ const parseStringArray = (value: unknown): string[] =>
|
|
|
136
137
|
? value.filter((name): name is string => typeof name === 'string')
|
|
137
138
|
: [];
|
|
138
139
|
|
|
140
|
+
const parseMergeableStatus = (value: unknown): ConsoleMergeableStatus => {
|
|
141
|
+
if (value === 'MERGEABLE') {
|
|
142
|
+
return 'MERGEABLE';
|
|
143
|
+
}
|
|
144
|
+
if (value === 'CONFLICTING') {
|
|
145
|
+
return 'CONFLICTING';
|
|
146
|
+
}
|
|
147
|
+
return 'UNKNOWN';
|
|
148
|
+
};
|
|
149
|
+
|
|
139
150
|
const parseRelatedPrs = (payload: unknown): ConsoleRelatedPullRequest[] => {
|
|
140
151
|
if (!isRecord(payload) || !Array.isArray(payload.relatedPullRequests)) {
|
|
141
152
|
return [];
|
|
@@ -146,6 +157,7 @@ const parseRelatedPrs = (payload: unknown): ConsoleRelatedPullRequest[] => {
|
|
|
146
157
|
createdAt: getString(pr.createdAt),
|
|
147
158
|
isDraft: getBoolean(pr.isDraft),
|
|
148
159
|
isConflicted: getBoolean(pr.isConflicted),
|
|
160
|
+
mergeableStatus: parseMergeableStatus(pr.mergeableStatus),
|
|
149
161
|
isPassedAllCiJob: getBoolean(pr.isPassedAllCiJob),
|
|
150
162
|
isCiStateSuccess: getBoolean(pr.isCiStateSuccess),
|
|
151
163
|
isResolvedAllReviewComments: getBoolean(pr.isResolvedAllReviewComments),
|
|
@@ -160,6 +172,7 @@ const parsePullRequestStatus = (payload: unknown): ConsolePullRequestStatus => {
|
|
|
160
172
|
return {
|
|
161
173
|
found: false,
|
|
162
174
|
isConflicted: false,
|
|
175
|
+
mergeableStatus: 'UNKNOWN',
|
|
163
176
|
isPassedAllCiJob: false,
|
|
164
177
|
isCiStateSuccess: false,
|
|
165
178
|
isBranchOutOfDate: false,
|
|
@@ -170,6 +183,7 @@ const parsePullRequestStatus = (payload: unknown): ConsolePullRequestStatus => {
|
|
|
170
183
|
return {
|
|
171
184
|
found: true,
|
|
172
185
|
isConflicted: getBoolean(status.isConflicted),
|
|
186
|
+
mergeableStatus: parseMergeableStatus(status.mergeableStatus),
|
|
173
187
|
isPassedAllCiJob: getBoolean(status.isPassedAllCiJob),
|
|
174
188
|
isCiStateSuccess: getBoolean(status.isCiStateSuccess),
|
|
175
189
|
isBranchOutOfDate: getBoolean(status.isBranchOutOfDate),
|
|
@@ -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;
|