github-issue-tower-defence-management 1.109.0 → 1.109.2

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 (38) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +6 -7
  3. package/bin/adapter/entry-points/cli/index.js +10 -10
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/console/dashboardComposeService.js +7 -6
  6. package/bin/adapter/entry-points/console/dashboardComposeService.js.map +1 -1
  7. package/bin/adapter/entry-points/console/webServer.js +5 -4
  8. package/bin/adapter/entry-points/console/webServer.js.map +1 -1
  9. package/bin/adapter/repositories/GraphqlProjectRepository.js +3 -58
  10. package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
  11. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +1 -1
  12. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
  13. package/bin/domain/usecases/dashboard/DashboardProjectCode.js +19 -0
  14. package/bin/domain/usecases/dashboard/DashboardProjectCode.js.map +1 -0
  15. package/package.json +1 -1
  16. package/src/adapter/entry-points/cli/index.ts +14 -14
  17. package/src/adapter/entry-points/console/dashboardComposeService.test.ts +31 -28
  18. package/src/adapter/entry-points/console/dashboardComposeService.ts +13 -10
  19. package/src/adapter/entry-points/console/dashboardEmitterComposerKey.test.ts +175 -0
  20. package/src/adapter/entry-points/console/ui/e2e/consoleTestHarness.ts +1 -1
  21. package/src/adapter/entry-points/console/webServer.test.ts +23 -23
  22. package/src/adapter/entry-points/console/webServer.ts +5 -4
  23. package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +5 -105
  24. package/src/adapter/repositories/GraphqlProjectRepository.ts +1 -73
  25. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +3 -6
  26. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +1 -1
  27. package/src/domain/usecases/dashboard/DashboardProjectCode.test.ts +32 -0
  28. package/src/domain/usecases/dashboard/DashboardProjectCode.ts +20 -0
  29. package/types/adapter/entry-points/console/dashboardComposeService.d.ts +1 -1
  30. package/types/adapter/entry-points/console/dashboardComposeService.d.ts.map +1 -1
  31. package/types/adapter/entry-points/console/webServer.d.ts +2 -2
  32. package/types/adapter/entry-points/console/webServer.d.ts.map +1 -1
  33. package/types/adapter/repositories/GraphqlProjectRepository.d.ts +1 -5
  34. package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
  35. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +1 -1
  36. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
  37. package/types/domain/usecases/dashboard/DashboardProjectCode.d.ts +4 -0
  38. package/types/domain/usecases/dashboard/DashboardProjectCode.d.ts.map +1 -0
@@ -182,7 +182,7 @@ describe('webServer integration', () => {
182
182
  inTmuxDataDir: null,
183
183
  dashboardDir: null,
184
184
  dashboardDataDir: null,
185
- dashboardProjectCodes: [],
185
+ dashboardProjectNames: [],
186
186
  port: 0,
187
187
  });
188
188
  const address = server.address();
@@ -201,7 +201,7 @@ describe('webServer integration', () => {
201
201
  inTmuxDataDir: null,
202
202
  dashboardDir: null,
203
203
  dashboardDataDir: null,
204
- dashboardProjectCodes: [],
204
+ dashboardProjectNames: [],
205
205
  port: 0,
206
206
  });
207
207
  try {
@@ -229,7 +229,7 @@ describe('webServer integration', () => {
229
229
  inTmuxDataDir: null,
230
230
  dashboardDir: null,
231
231
  dashboardDataDir: null,
232
- dashboardProjectCodes: [],
232
+ dashboardProjectNames: [],
233
233
  port: 0,
234
234
  });
235
235
  try {
@@ -261,7 +261,7 @@ describe('webServer integration', () => {
261
261
  inTmuxDataDir: null,
262
262
  dashboardDir: null,
263
263
  dashboardDataDir: null,
264
- dashboardProjectCodes: [],
264
+ dashboardProjectNames: [],
265
265
  port: 0,
266
266
  });
267
267
  try {
@@ -295,7 +295,7 @@ describe('webServer integration', () => {
295
295
  inTmuxDataDir: null,
296
296
  dashboardDir: null,
297
297
  dashboardDataDir: null,
298
- dashboardProjectCodes: [],
298
+ dashboardProjectNames: [],
299
299
  port: 0,
300
300
  });
301
301
  try {
@@ -326,7 +326,7 @@ describe('webServer integration', () => {
326
326
  inTmuxDataDir: null,
327
327
  dashboardDir: null,
328
328
  dashboardDataDir: null,
329
- dashboardProjectCodes: [],
329
+ dashboardProjectNames: [],
330
330
  port: 0,
331
331
  });
332
332
  try {
@@ -351,7 +351,7 @@ describe('webServer integration', () => {
351
351
  inTmuxDataDir: null,
352
352
  dashboardDir: null,
353
353
  dashboardDataDir: null,
354
- dashboardProjectCodes: [],
354
+ dashboardProjectNames: [],
355
355
  port: 0,
356
356
  });
357
357
  try {
@@ -379,7 +379,7 @@ describe('webServer integration', () => {
379
379
  inTmuxDataDir: null,
380
380
  dashboardDir: null,
381
381
  dashboardDataDir: null,
382
- dashboardProjectCodes: [],
382
+ dashboardProjectNames: [],
383
383
  port: 0,
384
384
  });
385
385
  try {
@@ -519,7 +519,7 @@ describe('webServer new routes integration', () => {
519
519
  inTmuxDataDir: null,
520
520
  dashboardDir: null,
521
521
  dashboardDataDir: null,
522
- dashboardProjectCodes: [],
522
+ dashboardProjectNames: [],
523
523
  port: 0,
524
524
  });
525
525
  try {
@@ -558,7 +558,7 @@ describe('webServer new routes integration', () => {
558
558
  inTmuxDataDir: null,
559
559
  dashboardDir: null,
560
560
  dashboardDataDir: null,
561
- dashboardProjectCodes: [],
561
+ dashboardProjectNames: [],
562
562
  issueRepository,
563
563
  issueTitleStateCache: new IssueTitleStateCache(),
564
564
  port: 0,
@@ -594,7 +594,7 @@ describe('webServer new routes integration', () => {
594
594
  inTmuxDataDir: null,
595
595
  dashboardDir: null,
596
596
  dashboardDataDir: null,
597
- dashboardProjectCodes: [],
597
+ dashboardProjectNames: [],
598
598
  issueRepository,
599
599
  resolveProject: async (pjcode) =>
600
600
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -645,7 +645,7 @@ describe('webServer new routes integration', () => {
645
645
  inTmuxDataDir: null,
646
646
  dashboardDir: null,
647
647
  dashboardDataDir: null,
648
- dashboardProjectCodes: [],
648
+ dashboardProjectNames: [],
649
649
  issueRepository,
650
650
  resolveProject: async (pjcode) =>
651
651
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -693,7 +693,7 @@ describe('webServer new routes integration', () => {
693
693
  inTmuxDataDir: null,
694
694
  dashboardDir: null,
695
695
  dashboardDataDir: null,
696
- dashboardProjectCodes: [],
696
+ dashboardProjectNames: [],
697
697
  issueRepository,
698
698
  resolveProject: async (pjcode) =>
699
699
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -731,7 +731,7 @@ describe('webServer new routes integration', () => {
731
731
  inTmuxDataDir: null,
732
732
  dashboardDir: null,
733
733
  dashboardDataDir: null,
734
- dashboardProjectCodes: [],
734
+ dashboardProjectNames: [],
735
735
  issueRepository,
736
736
  resolveProject: async (pjcode) =>
737
737
  pjcode === 'umino' ? { pjcode, project: buildProject() } : null,
@@ -784,7 +784,7 @@ describe('webServer new routes integration', () => {
784
784
  inTmuxDataDir: null,
785
785
  dashboardDir: null,
786
786
  dashboardDataDir: null,
787
- dashboardProjectCodes: [],
787
+ dashboardProjectNames: [],
788
788
  port: 0,
789
789
  });
790
790
  try {
@@ -932,7 +932,7 @@ describe('webServer flat in-tmux-by-human route integration', () => {
932
932
  inTmuxDataDir,
933
933
  dashboardDir: null,
934
934
  dashboardDataDir: null,
935
- dashboardProjectCodes: [],
935
+ dashboardProjectNames: [],
936
936
  port: 0,
937
937
  });
938
938
  return { server, tmpDir, inTmuxDataDir };
@@ -1048,7 +1048,7 @@ describe('webServer flat in-tmux-by-human route integration', () => {
1048
1048
  inTmuxDataDir: null,
1049
1049
  dashboardDir: null,
1050
1050
  dashboardDataDir: null,
1051
- dashboardProjectCodes: [],
1051
+ dashboardProjectNames: [],
1052
1052
  port: 0,
1053
1053
  });
1054
1054
  try {
@@ -1121,9 +1121,9 @@ describe('webServer dashboard /tdpm.txt route integration', () => {
1121
1121
  const writeDataFiles = (dataDir: string): void => {
1122
1122
  fs.mkdirSync(path.join(dataDir, 'projects'), { recursive: true });
1123
1123
  fs.writeFileSync(
1124
- path.join(dataDir, 'projects', 'um.json'),
1124
+ path.join(dataDir, 'projects', 'umino.json'),
1125
1125
  JSON.stringify({
1126
- pjcode: 'um',
1126
+ pjcode: 'umino',
1127
1127
  capturedAt: '2026-06-26T00:00:00.000Z',
1128
1128
  unread: 3,
1129
1129
  todo: 1,
@@ -1136,9 +1136,9 @@ describe('webServer dashboard /tdpm.txt route integration', () => {
1136
1136
  }),
1137
1137
  );
1138
1138
  fs.writeFileSync(
1139
- path.join(dataDir, 'projects', 'xc.json'),
1139
+ path.join(dataDir, 'projects', 'xcare.json'),
1140
1140
  JSON.stringify({
1141
- pjcode: 'xc',
1141
+ pjcode: 'xcare',
1142
1142
  capturedAt: '2026-06-26T00:00:00.000Z',
1143
1143
  unread: 0,
1144
1144
  todo: 0,
@@ -1209,7 +1209,7 @@ describe('webServer dashboard /tdpm.txt route integration', () => {
1209
1209
  inTmuxDataDir: null,
1210
1210
  dashboardDir: overrides.dashboardDir,
1211
1211
  dashboardDataDir: overrides.dashboardDataDir,
1212
- dashboardProjectCodes: ['um', 'xc'],
1212
+ dashboardProjectNames: ['umino', 'xcare'],
1213
1213
  port: 0,
1214
1214
  });
1215
1215
  return { server, tmpDir };
@@ -1453,7 +1453,7 @@ describe('webServer image proxy', () => {
1453
1453
  inTmuxDataDir: null,
1454
1454
  dashboardDir: null,
1455
1455
  dashboardDataDir: null,
1456
- dashboardProjectCodes: [],
1456
+ dashboardProjectNames: [],
1457
1457
  githubToken: token,
1458
1458
  imageFetcher: fetcher,
1459
1459
  port: 0,
@@ -30,10 +30,11 @@ import {
30
30
  composeDashboardText,
31
31
  dashboardComposeFilesPresent,
32
32
  } from './dashboardComposeService';
33
+ import { DASHBOARD_PROJECT_NAMES } from '../../../domain/usecases/dashboard/DashboardProjectCode';
33
34
 
34
35
  export const DEFAULT_WEB_PORT = 9981;
35
36
 
36
- export const DEFAULT_DASHBOARD_PROJECT_CODES = ['um', 'xm', 'xc', 'ut'];
37
+ export const DEFAULT_DASHBOARD_PROJECT_NAMES = DASHBOARD_PROJECT_NAMES;
37
38
 
38
39
  export const CONSOLE_TOKEN_HEADER = 'x-pv-token';
39
40
 
@@ -165,7 +166,7 @@ export type WebServerOptions = {
165
166
  inTmuxDataDir: string | null;
166
167
  dashboardDir: string | null;
167
168
  dashboardDataDir: string | null;
168
- dashboardProjectCodes: string[];
169
+ dashboardProjectNames: string[];
169
170
  githubToken?: string | null;
170
171
  imageFetcher?: ImageFetcher | null;
171
172
  issueRepository?: IssueRepository | null;
@@ -555,12 +556,12 @@ export const resolveDashboardContent = (
555
556
  options.dashboardDataDir !== null &&
556
557
  dashboardComposeFilesPresent({
557
558
  dashboardDataDir: options.dashboardDataDir,
558
- projectCodes: options.dashboardProjectCodes,
559
+ projectNames: options.dashboardProjectNames,
559
560
  })
560
561
  ) {
561
562
  const dashboardText = composeDashboardText({
562
563
  dashboardDataDir: options.dashboardDataDir,
563
- projectCodes: options.dashboardProjectCodes,
564
+ projectNames: options.dashboardProjectNames,
564
565
  });
565
566
  return Buffer.from(dashboardText, 'utf-8');
566
567
  }
@@ -14,10 +14,7 @@ jest.mock('ky', () => ({
14
14
  __esModule: true,
15
15
  }));
16
16
 
17
- import {
18
- GraphqlProjectRepository,
19
- resolveProjectCacheTtlMs,
20
- } from './GraphqlProjectRepository';
17
+ import { GraphqlProjectRepository } from './GraphqlProjectRepository';
21
18
  import { LocalStorageCacheRepository } from './LocalStorageCacheRepository';
22
19
  import { LocalStorageRepository } from './LocalStorageRepository';
23
20
  import { Project } from '../../domain/entities/Project';
@@ -98,7 +95,6 @@ const buildCacheStub = (overrides?: {
98
95
 
99
96
  describe('GraphqlProjectRepository disk cache', () => {
100
97
  const localStorageRepository = new LocalStorageRepository();
101
- const thirtyMinutesMs = 30 * 60 * 1000;
102
98
 
103
99
  beforeEach(() => {
104
100
  jest.useFakeTimers();
@@ -109,23 +105,8 @@ describe('GraphqlProjectRepository disk cache', () => {
109
105
  jest.useRealTimers();
110
106
  });
111
107
 
112
- describe('resolveProjectCacheTtlMs', () => {
113
- it('defaults to 30 minutes when unset', () => {
114
- expect(resolveProjectCacheTtlMs(undefined)).toBe(thirtyMinutesMs);
115
- });
116
-
117
- it('uses the configured millisecond value when valid', () => {
118
- expect(resolveProjectCacheTtlMs('60000')).toBe(60000);
119
- });
120
-
121
- it('falls back to the default for non-numeric or negative values', () => {
122
- expect(resolveProjectCacheTtlMs('abc')).toBe(thirtyMinutesMs);
123
- expect(resolveProjectCacheTtlMs('-1')).toBe(thirtyMinutesMs);
124
- });
125
- });
126
-
127
108
  describe('getProject', () => {
128
- it('fetches via GraphQL and writes the cache on a cache miss', async () => {
109
+ it('always fetches the project fresh via GraphQL without reading or writing the disk cache', async () => {
129
110
  const cache = buildCacheStub();
130
111
  mockPost.mockReturnValueOnce(mockJsonResponse(getProjectResponse));
131
112
  const repository = new GraphqlProjectRepository(
@@ -138,75 +119,14 @@ describe('GraphqlProjectRepository disk cache', () => {
138
119
 
139
120
  expect(project).toEqual(expectedProject);
140
121
  expect(mockPost).toHaveBeenCalledTimes(1);
141
- expect(cache.set).toHaveBeenCalledWith(
142
- `project-${projectId}`,
143
- expectedProject,
144
- );
145
- });
146
-
147
- it('returns the cached project without any GraphQL call when the cache is fresh', async () => {
148
- const cache = buildCacheStub({
149
- getLatest: jest.fn().mockResolvedValue({
150
- value: expectedProject,
151
- timestamp: new Date(),
152
- } satisfies CacheEntry),
153
- });
154
- const repository = new GraphqlProjectRepository(
155
- localStorageRepository,
156
- 'dummy',
157
- cache,
158
- );
159
-
160
- const project = await repository.getProject(projectId);
161
-
162
- expect(project).toEqual(expectedProject);
163
- expect(mockPost).not.toHaveBeenCalled();
122
+ expect(cache.getLatest).not.toHaveBeenCalled();
164
123
  expect(cache.set).not.toHaveBeenCalled();
165
124
  });
166
125
 
167
- it('refetches via GraphQL when the cached entry is older than the TTL', async () => {
168
- const staleTimestamp = new Date(Date.now() - thirtyMinutesMs - 1);
126
+ it('does not serve a project from the disk cache even when one is present', async () => {
169
127
  const cache = buildCacheStub({
170
128
  getLatest: jest.fn().mockResolvedValue({
171
129
  value: expectedProject,
172
- timestamp: staleTimestamp,
173
- } satisfies CacheEntry),
174
- });
175
- mockPost.mockReturnValueOnce(mockJsonResponse(getProjectResponse));
176
- const repository = new GraphqlProjectRepository(
177
- localStorageRepository,
178
- 'dummy',
179
- cache,
180
- );
181
-
182
- const project = await repository.getProject(projectId);
183
-
184
- expect(project).toEqual(expectedProject);
185
- expect(mockPost).toHaveBeenCalledTimes(1);
186
- expect(cache.set).toHaveBeenCalledTimes(1);
187
- });
188
-
189
- it('falls back to a live GraphQL fetch when the cache read throws', async () => {
190
- const cache = buildCacheStub({
191
- getLatest: jest.fn().mockRejectedValue(new Error('corrupted file')),
192
- });
193
- mockPost.mockReturnValueOnce(mockJsonResponse(getProjectResponse));
194
- const repository = new GraphqlProjectRepository(
195
- localStorageRepository,
196
- 'dummy',
197
- cache,
198
- );
199
-
200
- const project = await repository.getProject(projectId);
201
-
202
- expect(project).toEqual(expectedProject);
203
- expect(mockPost).toHaveBeenCalledTimes(1);
204
- });
205
-
206
- it('falls back to a live GraphQL fetch when the cached value is malformed', async () => {
207
- const cache = buildCacheStub({
208
- getLatest: jest.fn().mockResolvedValue({
209
- value: { unexpected: 'shape' },
210
130
  timestamp: new Date(),
211
131
  } satisfies CacheEntry),
212
132
  });
@@ -221,27 +141,7 @@ describe('GraphqlProjectRepository disk cache', () => {
221
141
 
222
142
  expect(project).toEqual(expectedProject);
223
143
  expect(mockPost).toHaveBeenCalledTimes(1);
224
- });
225
-
226
- it('respects a custom TTL passed to the constructor', async () => {
227
- const cache = buildCacheStub({
228
- getLatest: jest.fn().mockResolvedValue({
229
- value: expectedProject,
230
- timestamp: new Date(Date.now() - 2000),
231
- } satisfies CacheEntry),
232
- });
233
- mockPost.mockReturnValueOnce(mockJsonResponse(getProjectResponse));
234
- const repository = new GraphqlProjectRepository(
235
- localStorageRepository,
236
- 'dummy',
237
- cache,
238
- 1000,
239
- );
240
-
241
- const project = await repository.getProject(projectId);
242
-
243
- expect(project).toEqual(expectedProject);
244
- expect(mockPost).toHaveBeenCalledTimes(1);
144
+ expect(cache.set).not.toHaveBeenCalled();
245
145
  });
246
146
  });
247
147
 
@@ -1,5 +1,4 @@
1
1
  import ky from 'ky';
2
- import typia from 'typia';
3
2
  import { BaseGitHubRepository } from './BaseGitHubRepository';
4
3
  import { LocalStorageCacheRepository } from './LocalStorageCacheRepository';
5
4
  import { LocalStorageRepository } from './LocalStorageRepository';
@@ -9,23 +8,7 @@ import { normalizeFieldName } from './utils';
9
8
 
10
9
  const ONE_HOUR_MS = 60 * 60 * 1000;
11
10
 
12
- const DEFAULT_PROJECT_CACHE_TTL_MS = 30 * 60 * 1000;
13
-
14
- export const resolveProjectCacheTtlMs = (
15
- rawValue: string | undefined,
16
- ): number => {
17
- if (rawValue === undefined) {
18
- return DEFAULT_PROJECT_CACHE_TTL_MS;
19
- }
20
- const parsed = Number(rawValue);
21
- if (!Number.isFinite(parsed) || parsed < 0) {
22
- return DEFAULT_PROJECT_CACHE_TTL_MS;
23
- }
24
- return parsed;
25
- };
26
-
27
11
  const PROJECT_ID_DISK_CACHE_KEY_PREFIX = 'projectId';
28
- const PROJECT_DISK_CACHE_KEY_PREFIX = 'project';
29
12
 
30
13
  export const convertToFieldOptionColor = (
31
14
  color: string,
@@ -63,19 +46,14 @@ export class GraphqlProjectRepository
63
46
  LocalStorageCacheRepository,
64
47
  'getLatest' | 'set'
65
48
  >;
66
- private readonly projectCacheTtlMs: number;
67
49
 
68
50
  constructor(
69
51
  localStorageRepository: LocalStorageRepository,
70
52
  ghToken: string = process.env.GH_TOKEN || 'dummy',
71
53
  projectCache?: Pick<LocalStorageCacheRepository, 'getLatest' | 'set'>,
72
- projectCacheTtlMs: number = resolveProjectCacheTtlMs(
73
- process.env.TDPM_PROJECT_CACHE_TTL_MS,
74
- ),
75
54
  ) {
76
55
  super(localStorageRepository, ghToken);
77
56
  this.projectCache = projectCache;
78
- this.projectCacheTtlMs = projectCacheTtlMs;
79
57
  }
80
58
 
81
59
  private readProjectIdFromDiskCache = async (
@@ -121,50 +99,6 @@ export class GraphqlProjectRepository
121
99
  }
122
100
  };
123
101
 
124
- private readProjectFromDiskCache = async (
125
- projectId: string,
126
- ): Promise<Project | null> => {
127
- if (!this.projectCache) {
128
- return null;
129
- }
130
- let cache: { value: object; timestamp: Date } | null;
131
- try {
132
- cache = await this.projectCache.getLatest(
133
- `${PROJECT_DISK_CACHE_KEY_PREFIX}-${projectId}`,
134
- );
135
- } catch (error) {
136
- return null;
137
- }
138
- if (!cache) {
139
- return null;
140
- }
141
- const age = Date.now() - cache.timestamp.getTime();
142
- if (age >= this.projectCacheTtlMs) {
143
- return null;
144
- }
145
- if (!typia.is<Project>(cache.value)) {
146
- return null;
147
- }
148
- return cache.value;
149
- };
150
-
151
- private writeProjectToDiskCache = async (
152
- projectId: string,
153
- project: Project,
154
- ): Promise<void> => {
155
- if (!this.projectCache) {
156
- return;
157
- }
158
- try {
159
- await this.projectCache.set(
160
- `${PROJECT_DISK_CACHE_KEY_PREFIX}-${projectId}`,
161
- project,
162
- );
163
- } catch (error) {
164
- return;
165
- }
166
- };
167
-
168
102
  extractProjectFromUrl = (
169
103
  projectUrl: string,
170
104
  ): {
@@ -280,10 +214,6 @@ export class GraphqlProjectRepository
280
214
  return await this.fetchProjectId(owner, projectNumber);
281
215
  };
282
216
  getProject = async (projectId: Project['id']): Promise<Project | null> => {
283
- const diskCached = await this.readProjectFromDiskCache(projectId);
284
- if (diskCached) {
285
- return diskCached;
286
- }
287
217
  const query = `query GetProjectV2($projectId: ID!) {
288
218
  node(id: $projectId) {
289
219
  ... on ProjectV2 {
@@ -428,7 +358,7 @@ export class GraphqlProjectRepository
428
358
  const completionDate50PercentConfidence = project.fields.nodes.find(
429
359
  (field) => normalizeFieldName(field.name).startsWith('completiondate'),
430
360
  );
431
- const result: Project = {
361
+ return {
432
362
  id: project.id,
433
363
  url: project.url,
434
364
  databaseId: project.databaseId,
@@ -489,8 +419,6 @@ export class GraphqlProjectRepository
489
419
  }
490
420
  : null,
491
421
  };
492
- await this.writeProjectToDiskCache(projectId, result);
493
- return result;
494
422
  };
495
423
  getByUrl = async (url: string): Promise<Project> => {
496
424
  const projectId = await this.findProjectIdByUrl(url);
@@ -466,7 +466,7 @@ describe('GraphqlProjectItemRepository', () => {
466
466
  expect(requestedFirstSeries).toEqual([100, 50, 25, 12, 6, 3, 1]);
467
467
  });
468
468
 
469
- it('should not sleep the legacy 5000ms blanket delay between pages', async () => {
469
+ it('should sleep the 5000ms blanket delay between pages', async () => {
470
470
  const localStorageRepository = new LocalStorageRepository();
471
471
  const repository = new GraphqlProjectItemRepository(
472
472
  localStorageRepository,
@@ -483,11 +483,8 @@ describe('GraphqlProjectItemRepository', () => {
483
483
  const result = await resultPromise;
484
484
 
485
485
  expect(result).toHaveLength(2);
486
- expect(PAGINATION_DELAY_MS).toBeLessThanOrEqual(500);
487
- expect(setTimeoutSpy).not.toHaveBeenCalledWith(
488
- expect.any(Function),
489
- 5000,
490
- );
486
+ expect(PAGINATION_DELAY_MS).toBe(5000);
487
+ expect(setTimeoutSpy).toHaveBeenCalledWith(expect.any(Function), 5000);
491
488
  setTimeoutSpy.mockRestore();
492
489
  });
493
490
 
@@ -20,7 +20,7 @@ export type ProjectItem = {
20
20
  value: string | null;
21
21
  }[];
22
22
  };
23
- export const PAGINATION_DELAY_MS = 500;
23
+ export const PAGINATION_DELAY_MS = 5000;
24
24
  export const FETCH_PROJECT_ITEMS_INITIAL_PAGE_SIZE = 100;
25
25
  export const FETCH_PROJECT_ITEMS_GRAPHQL_ERROR_PAYLOAD_MAX_LENGTH = 4000;
26
26
  export const RATE_LIMIT_MAX_RETRIES = 6;
@@ -0,0 +1,32 @@
1
+ import {
2
+ DASHBOARD_DISPLAY_LABEL_BY_PROJECT_NAME,
3
+ DASHBOARD_PROJECT_NAMES,
4
+ toDashboardDisplayLabel,
5
+ } from './DashboardProjectCode';
6
+
7
+ describe('DashboardProjectCode', () => {
8
+ it('maps each full project name to its 2-char display label', () => {
9
+ expect(toDashboardDisplayLabel('umino')).toBe('um');
10
+ expect(toDashboardDisplayLabel('xmile')).toBe('xm');
11
+ expect(toDashboardDisplayLabel('xcare')).toBe('xc');
12
+ expect(toDashboardDisplayLabel('utage3')).toBe('ut');
13
+ });
14
+
15
+ it('exposes the project names in the same order as the label mapping', () => {
16
+ expect(DASHBOARD_PROJECT_NAMES).toEqual([
17
+ 'umino',
18
+ 'xmile',
19
+ 'xcare',
20
+ 'utage3',
21
+ ]);
22
+ expect(DASHBOARD_PROJECT_NAMES).toEqual(
23
+ Object.keys(DASHBOARD_DISPLAY_LABEL_BY_PROJECT_NAME),
24
+ );
25
+ });
26
+
27
+ it('throws for an unknown project name rather than guessing a label', () => {
28
+ expect(() => toDashboardDisplayLabel('unknown')).toThrow(
29
+ 'Unknown dashboard project name: unknown',
30
+ );
31
+ });
32
+ });
@@ -0,0 +1,20 @@
1
+ export const DASHBOARD_DISPLAY_LABEL_BY_PROJECT_NAME: Record<string, string> = {
2
+ umino: 'um',
3
+ xmile: 'xm',
4
+ xcare: 'xc',
5
+ utage3: 'ut',
6
+ };
7
+
8
+ export const DASHBOARD_PROJECT_NAMES: string[] = Object.keys(
9
+ DASHBOARD_DISPLAY_LABEL_BY_PROJECT_NAME,
10
+ );
11
+
12
+ export const toDashboardDisplayLabel = (projectName: string): string => {
13
+ const label = DASHBOARD_DISPLAY_LABEL_BY_PROJECT_NAME[projectName];
14
+ if (label === undefined) {
15
+ throw new Error(
16
+ `Unknown dashboard project name: ${projectName}. Add it to DASHBOARD_DISPLAY_LABEL_BY_PROJECT_NAME.`,
17
+ );
18
+ }
19
+ return label;
20
+ };
@@ -1,7 +1,7 @@
1
1
  import { ComposeDashboardInput } from '../../../domain/usecases/dashboard/ComposeDashboardUseCase';
2
2
  export type DashboardComposeOptions = {
3
3
  dashboardDataDir: string;
4
- projectCodes: string[];
4
+ projectNames: string[];
5
5
  };
6
6
  export declare const buildComposeDashboardInput: (options: DashboardComposeOptions) => ComposeDashboardInput;
7
7
  export declare const dashboardComposeFilesPresent: (options: DashboardComposeOptions) => boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"dashboardComposeService.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/console/dashboardComposeService.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,qBAAqB,EAItB,MAAM,4DAA4D,CAAC;AAOpE,MAAM,MAAM,uBAAuB,GAAG;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AA4IF,eAAO,MAAM,0BAA0B,GACrC,SAAS,uBAAuB,KAC/B,qBAYF,CAAC;AAUF,eAAO,MAAM,4BAA4B,GACvC,SAAS,uBAAuB,KAC/B,OAYF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,SAAS,uBAAuB,KAC/B,MACqE,CAAC"}
1
+ {"version":3,"file":"dashboardComposeService.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/console/dashboardComposeService.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,qBAAqB,EAItB,MAAM,4DAA4D,CAAC;AAQpE,MAAM,MAAM,uBAAuB,GAAG;IACpC,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,CAAC;AA8IF,eAAO,MAAM,0BAA0B,GACrC,SAAS,uBAAuB,KAC/B,qBAYF,CAAC;AAUF,eAAO,MAAM,4BAA4B,GACvC,SAAS,uBAAuB,KAC/B,OAYF,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAC/B,SAAS,uBAAuB,KAC/B,MACqE,CAAC"}
@@ -4,7 +4,7 @@ import { IssueTitleStateCache } from './consoleReadApi';
4
4
  import { ConsoleProjectResolver } from './consoleOperationApi';
5
5
  import { ImageFetcher } from './consoleImageProxy';
6
6
  export declare const DEFAULT_WEB_PORT = 9981;
7
- export declare const DEFAULT_DASHBOARD_PROJECT_CODES: string[];
7
+ export declare const DEFAULT_DASHBOARD_PROJECT_NAMES: string[];
8
8
  export declare const CONSOLE_TOKEN_HEADER = "x-pv-token";
9
9
  export declare const hasDotSegment: (requestPath: string) => boolean;
10
10
  export declare const requiresToken: (requestPath: string) => boolean;
@@ -18,7 +18,7 @@ export type WebServerOptions = {
18
18
  inTmuxDataDir: string | null;
19
19
  dashboardDir: string | null;
20
20
  dashboardDataDir: string | null;
21
- dashboardProjectCodes: string[];
21
+ dashboardProjectNames: string[];
22
22
  githubToken?: string | null;
23
23
  imageFetcher?: ImageFetcher | null;
24
24
  issueRepository?: IssueRepository | null;
@@ -1 +1 @@
1
- {"version":3,"file":"webServer.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/console/webServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAM9F,OAAO,EACL,oBAAoB,EAOrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,sBAAsB,EAMvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;AAMtE,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,eAAO,MAAM,+BAA+B,UAA2B,CAAC;AAExE,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAmCjD,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAGiB,CAAC;AAEtE,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAGrB,CAAC;AAIhC,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,KAAG,OAmBvD,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,eAAe,MAAM,EACrB,eAAe,MAAM,GAAG,IAAI,KAC3B,OAAoE,CAAC;AAExE,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EACpC,aAAa,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KACzC,MAAM,GAAG,IAQX,CAAC;AAuCF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC/C,oBAAoB,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACpD,CAAC;AAMF,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAElD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAInD,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,EACpB,aAAa,MAAM,KAClB,MAAM,GAAG,IAWX,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,eAAe,MAAM,EACrB,aAAa,MAAM,KAClB,MAAM,GAAG,IAeX,CAAC;AA0UF,eAAO,MAAM,uBAAuB,GAClC,SAAS,gBAAgB,EACzB,aAAa,MAAM,KAClB,MAAM,GAAG,IAeX,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,gBAAgB,EACzB,SAAS,IAAI,CAAC,eAAe,EAC7B,UAAU,IAAI,CAAC,cAAc,KAC5B,OAAO,CAAC,IAAI,CAwEd,CAAC;AAcF,eAAO,MAAM,eAAe,GAAI,SAAS,gBAAgB,KAAG,IAAI,CAAC,MAM7D,CAAC;AAEL,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG;IACrD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,IAAI,CAAC,MAAM,CAQlB,CAAC"}
1
+ {"version":3,"file":"webServer.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/console/webServer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,6DAA6D,CAAC;AAM9F,OAAO,EACL,oBAAoB,EAOrB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,sBAAsB,EAMvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;AAOtE,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC,eAAO,MAAM,+BAA+B,UAA0B,CAAC;AAEvE,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAmCjD,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAGiB,CAAC;AAEtE,eAAO,MAAM,aAAa,GAAI,aAAa,MAAM,KAAG,OAGrB,CAAC;AAIhC,eAAO,MAAM,iBAAiB,GAAI,aAAa,MAAM,KAAG,OAmBvD,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,eAAe,MAAM,EACrB,eAAe,MAAM,GAAG,IAAI,KAC3B,OAAoE,CAAC;AAExE,eAAO,MAAM,oBAAoB,GAC/B,YAAY,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,EACpC,aAAa,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,KACzC,MAAM,GAAG,IAQX,CAAC;AAuCF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,qBAAqB,EAAE,MAAM,EAAE,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,YAAY,CAAC,EAAE,YAAY,GAAG,IAAI,CAAC;IACnC,eAAe,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACzC,cAAc,CAAC,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC/C,oBAAoB,CAAC,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACpD,CAAC;AAMF,eAAO,MAAM,sBAAsB,cAAc,CAAC;AAElD,eAAO,MAAM,wBAAwB,aAAa,CAAC;AAInD,eAAO,MAAM,wBAAwB,GACnC,cAAc,MAAM,EACpB,aAAa,MAAM,KAClB,MAAM,GAAG,IAWX,CAAC;AAEF,eAAO,MAAM,yBAAyB,GACpC,eAAe,MAAM,EACrB,aAAa,MAAM,KAClB,MAAM,GAAG,IAeX,CAAC;AA0UF,eAAO,MAAM,uBAAuB,GAClC,SAAS,gBAAgB,EACzB,aAAa,MAAM,KAClB,MAAM,GAAG,IAeX,CAAC;AAEF,eAAO,MAAM,gBAAgB,GAC3B,SAAS,gBAAgB,EACzB,SAAS,IAAI,CAAC,eAAe,EAC7B,UAAU,IAAI,CAAC,cAAc,KAC5B,OAAO,CAAC,IAAI,CAwEd,CAAC;AAcF,eAAO,MAAM,eAAe,GAAI,SAAS,gBAAgB,KAAG,IAAI,CAAC,MAM7D,CAAC;AAEL,MAAM,MAAM,qBAAqB,GAAG,gBAAgB,GAAG;IACrD,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,eAAO,MAAM,cAAc,GACzB,SAAS,qBAAqB,KAC7B,OAAO,CAAC,IAAI,CAAC,MAAM,CAQlB,CAAC"}
@@ -3,18 +3,14 @@ import { LocalStorageCacheRepository } from './LocalStorageCacheRepository';
3
3
  import { LocalStorageRepository } from './LocalStorageRepository';
4
4
  import { ProjectRepository } from '../../domain/usecases/adapter-interfaces/ProjectRepository';
5
5
  import { FieldOption, Project } from '../../domain/entities/Project';
6
- export declare const resolveProjectCacheTtlMs: (rawValue: string | undefined) => number;
7
6
  export declare const convertToFieldOptionColor: (color: string) => FieldOption["color"];
8
7
  export declare class GraphqlProjectRepository extends BaseGitHubRepository implements Pick<ProjectRepository, 'getProject' | 'findProjectIdByUrl' | 'getByUrl' | 'updateStoryList' | 'updateStatusList'> {
9
8
  private readonly projectIdCache;
10
9
  private readonly fetchProjectIdFailedAt;
11
10
  private readonly projectCache?;
12
- private readonly projectCacheTtlMs;
13
- constructor(localStorageRepository: LocalStorageRepository, ghToken?: string, projectCache?: Pick<LocalStorageCacheRepository, 'getLatest' | 'set'>, projectCacheTtlMs?: number);
11
+ constructor(localStorageRepository: LocalStorageRepository, ghToken?: string, projectCache?: Pick<LocalStorageCacheRepository, 'getLatest' | 'set'>);
14
12
  private readProjectIdFromDiskCache;
15
13
  private writeProjectIdToDiskCache;
16
- private readProjectFromDiskCache;
17
- private writeProjectToDiskCache;
18
14
  extractProjectFromUrl: (projectUrl: string) => {
19
15
  owner: string;
20
16
  projectNumber: number;
@@ -1 +1 @@
1
- {"version":3,"file":"GraphqlProjectRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GraphqlProjectRepository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAOrE,eAAO,MAAM,wBAAwB,GACnC,UAAU,MAAM,GAAG,SAAS,KAC3B,MASF,CAAC;AAKF,eAAO,MAAM,yBAAyB,GACpC,OAAO,MAAM,KACZ,WAAW,CAAC,OAAO,CAcrB,CAAC;AAEF,qBAAa,wBACX,SAAQ,oBACR,YACE,IAAI,CACF,iBAAiB,EACf,YAAY,GACZ,oBAAoB,GACpB,UAAU,GACV,iBAAiB,GACjB,kBAAkB,CACrB;IAEH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA6B;IACpE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAG5B;IACF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGzC,sBAAsB,EAAE,sBAAsB,EAC9C,OAAO,GAAE,MAAwC,EACjD,YAAY,CAAC,EAAE,IAAI,CAAC,2BAA2B,EAAE,WAAW,GAAG,KAAK,CAAC,EACrE,iBAAiB,GAAE,MAElB;IAOH,OAAO,CAAC,0BAA0B,CAwBhC;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,OAAO,CAAC,wBAAwB,CAyB9B;IAEF,OAAO,CAAC,uBAAuB,CAe7B;IAEF,qBAAqB,GACnB,YAAY,MAAM,KACjB;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;KACvB,CAMC;IACF,cAAc,GACZ,OAAO,MAAM,EACb,eAAe,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,CA4FhB;IACF,kBAAkB,GAChB,YAAY,MAAM,KACjB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAG9B;IACF,UAAU,GAAU,WAAW,OAAO,CAAC,IAAI,CAAC,KAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAoNpE;IACF,QAAQ,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAU9C;IACF,eAAe,GACb,SAAS,OAAO,EAChB,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG;QACvC,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC9B,CAAC,EAAE,KACH,OAAO,CAAC,WAAW,EAAE,CAAC,CA+CvB;IACF,gBAAgB,GACd,SAAS,OAAO,EAChB,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG;QACxC,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC9B,CAAC,EAAE,KACH,OAAO,CAAC,WAAW,EAAE,CAAC,CA4CvB;CACH"}
1
+ {"version":3,"file":"GraphqlProjectRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/GraphqlProjectRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAOrE,eAAO,MAAM,yBAAyB,GACpC,OAAO,MAAM,KACZ,WAAW,CAAC,OAAO,CAcrB,CAAC;AAEF,qBAAa,wBACX,SAAQ,oBACR,YACE,IAAI,CACF,iBAAiB,EACf,YAAY,GACZ,oBAAoB,GACpB,UAAU,GACV,iBAAiB,GACjB,kBAAkB,CACrB;IAEH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA6B;IAC5D,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAA6B;IACpE,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAG5B;gBAGA,sBAAsB,EAAE,sBAAsB,EAC9C,OAAO,GAAE,MAAwC,EACjD,YAAY,CAAC,EAAE,IAAI,CAAC,2BAA2B,EAAE,WAAW,GAAG,KAAK,CAAC;IAMvE,OAAO,CAAC,0BAA0B,CAwBhC;IAEF,OAAO,CAAC,yBAAyB,CAe/B;IAEF,qBAAqB,GACnB,YAAY,MAAM,KACjB;QACD,KAAK,EAAE,MAAM,CAAC;QACd,aAAa,EAAE,MAAM,CAAC;KACvB,CAMC;IACF,cAAc,GACZ,OAAO,MAAM,EACb,eAAe,MAAM,KACpB,OAAO,CAAC,MAAM,CAAC,CA4FhB;IACF,kBAAkB,GAChB,YAAY,MAAM,KACjB,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAG9B;IACF,UAAU,GAAU,WAAW,OAAO,CAAC,IAAI,CAAC,KAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA8MpE;IACF,QAAQ,GAAU,KAAK,MAAM,KAAG,OAAO,CAAC,OAAO,CAAC,CAU9C;IACF,eAAe,GACb,SAAS,OAAO,EAChB,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG;QACvC,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC9B,CAAC,EAAE,KACH,OAAO,CAAC,WAAW,EAAE,CAAC,CA+CvB;IACF,gBAAgB,GACd,SAAS,OAAO,EAChB,eAAe,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,GAAG;QACxC,EAAE,EAAE,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAC9B,CAAC,EAAE,KACH,OAAO,CAAC,WAAW,EAAE,CAAC,CA4CvB;CACH"}