github-issue-tower-defence-management 1.82.1 → 1.84.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/.github/workflows/commit-lint.yml +7 -2
- package/.github/workflows/create-pr.yml +23 -5
- package/CHANGELOG.md +14 -0
- package/README.md +67 -4
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +19 -2
- package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
- package/bin/adapter/entry-points/handlers/consoleListsWriter.js +43 -0
- package/bin/adapter/entry-points/handlers/consoleListsWriter.js.map +1 -0
- package/bin/domain/usecases/StartPreparationUseCase.js +60 -46
- package/bin/domain/usecases/StartPreparationUseCase.js.map +1 -1
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js +101 -0
- package/bin/domain/usecases/console/GenerateConsoleListsUseCase.js.map +1 -0
- package/package.json +1 -1
- package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +18 -0
- package/src/adapter/entry-points/handlers/consoleListsWriter.test.ts +167 -0
- package/src/adapter/entry-points/handlers/consoleListsWriter.ts +60 -0
- package/src/domain/usecases/StartPreparationUseCase.test.ts +265 -68
- package/src/domain/usecases/StartPreparationUseCase.ts +94 -73
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.test.ts +372 -0
- package/src/domain/usecases/console/GenerateConsoleListsUseCase.ts +206 -0
- package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
- package/types/adapter/entry-points/handlers/consoleListsWriter.d.ts +13 -0
- package/types/adapter/entry-points/handlers/consoleListsWriter.d.ts.map +1 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts +1 -0
- package/types/domain/usecases/StartPreparationUseCase.d.ts.map +1 -1
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts +63 -0
- package/types/domain/usecases/console/GenerateConsoleListsUseCase.d.ts.map +1 -0
|
@@ -0,0 +1,372 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
import { FieldOption, Project } from '../../entities/Project';
|
|
3
|
+
import { GenerateConsoleListsUseCase } from './GenerateConsoleListsUseCase';
|
|
4
|
+
|
|
5
|
+
const ASSIGNEE = 'owner-login';
|
|
6
|
+
|
|
7
|
+
const storyOption = (
|
|
8
|
+
id: string,
|
|
9
|
+
name: string,
|
|
10
|
+
color: FieldOption['color'],
|
|
11
|
+
): FieldOption => ({ id, name, color, description: '' });
|
|
12
|
+
|
|
13
|
+
const STORY_OPTIONS: FieldOption[] = [
|
|
14
|
+
storyOption('s1', 'regular / NO STORY; SET STORY FIELD', 'RED'),
|
|
15
|
+
storyOption('s2', 'Story Alpha', 'BLUE'),
|
|
16
|
+
storyOption('s3', 'Story Beta', 'GREEN'),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const STATUS_OPTIONS: FieldOption[] = [
|
|
20
|
+
storyOption('st-unread', 'Unread', 'ORANGE'),
|
|
21
|
+
storyOption('st-aw', 'Awaiting Workspace', 'BLUE'),
|
|
22
|
+
storyOption('st-prep', 'Preparation', 'YELLOW'),
|
|
23
|
+
storyOption('st-failed', 'Failed Preparation', 'RED'),
|
|
24
|
+
storyOption('st-aqc', 'Awaiting Quality Check', 'GREEN'),
|
|
25
|
+
storyOption('st-todo', 'Todo by human', 'PINK'),
|
|
26
|
+
storyOption('st-tmux', 'In Tmux by human', 'RED'),
|
|
27
|
+
storyOption('st-done', 'Done', 'PURPLE'),
|
|
28
|
+
storyOption('st-icebox', 'Icebox', 'GRAY'),
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const baseProject = (story: Project['story']): Project => ({
|
|
32
|
+
id: 'project-node-id',
|
|
33
|
+
url: 'https://github.com/orgs/demo/projects/1',
|
|
34
|
+
databaseId: 1,
|
|
35
|
+
name: 'demo',
|
|
36
|
+
status: {
|
|
37
|
+
name: 'Status',
|
|
38
|
+
fieldId: 'status-field',
|
|
39
|
+
statuses: STATUS_OPTIONS,
|
|
40
|
+
},
|
|
41
|
+
nextActionDate: null,
|
|
42
|
+
nextActionHour: null,
|
|
43
|
+
story,
|
|
44
|
+
remainingEstimationMinutes: null,
|
|
45
|
+
dependedIssueUrlSeparatedByComma: null,
|
|
46
|
+
completionDate50PercentConfidence: null,
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const projectWithStory: Project = baseProject({
|
|
50
|
+
name: 'story',
|
|
51
|
+
fieldId: 'story-field',
|
|
52
|
+
databaseId: 2,
|
|
53
|
+
stories: STORY_OPTIONS,
|
|
54
|
+
workflowManagementStory: { id: 'wm', name: 'workflow management' },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
let issueCounter = 0;
|
|
58
|
+
const makeIssue = (overrides: Partial<Issue>): Issue => {
|
|
59
|
+
issueCounter += 1;
|
|
60
|
+
return {
|
|
61
|
+
nameWithOwner: 'demo/repo',
|
|
62
|
+
number: issueCounter,
|
|
63
|
+
title: `Issue ${issueCounter}`,
|
|
64
|
+
state: 'OPEN',
|
|
65
|
+
status: null,
|
|
66
|
+
story: null,
|
|
67
|
+
nextActionDate: null,
|
|
68
|
+
nextActionHour: null,
|
|
69
|
+
estimationMinutes: null,
|
|
70
|
+
dependedIssueUrls: [],
|
|
71
|
+
completionDate50PercentConfidence: null,
|
|
72
|
+
url: `https://github.com/demo/repo/issues/${issueCounter}`,
|
|
73
|
+
assignees: [ASSIGNEE],
|
|
74
|
+
labels: [],
|
|
75
|
+
org: 'demo',
|
|
76
|
+
repo: 'repo',
|
|
77
|
+
body: 'should never be projected',
|
|
78
|
+
itemId: `item-${issueCounter}`,
|
|
79
|
+
isPr: false,
|
|
80
|
+
isInProgress: false,
|
|
81
|
+
isClosed: false,
|
|
82
|
+
createdAt: new Date('2026-06-13T08:18:45.000Z'),
|
|
83
|
+
author: 'someone',
|
|
84
|
+
...overrides,
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
describe('GenerateConsoleListsUseCase', () => {
|
|
89
|
+
const usecase = new GenerateConsoleListsUseCase();
|
|
90
|
+
const generatedAt = '2026-06-14T07:22:33Z';
|
|
91
|
+
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
issueCounter = 0;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const run = (issues: Issue[], project: Project = projectWithStory) =>
|
|
97
|
+
usecase.run({
|
|
98
|
+
project,
|
|
99
|
+
issues,
|
|
100
|
+
pjcode: 'demo',
|
|
101
|
+
assigneeLogin: ASSIGNEE,
|
|
102
|
+
generatedAt,
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('common actionable filter', () => {
|
|
106
|
+
it('rejects closed issues', () => {
|
|
107
|
+
const result = run([makeIssue({ status: 'Unread', isClosed: true })]);
|
|
108
|
+
expect(result.unread.items).toHaveLength(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('rejects issues not assigned to the assignee login', () => {
|
|
112
|
+
const result = run([
|
|
113
|
+
makeIssue({ status: 'Unread', assignees: ['other-person'] }),
|
|
114
|
+
]);
|
|
115
|
+
expect(result.unread.items).toHaveLength(0);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('rejects issues with a depended issue url', () => {
|
|
119
|
+
const result = run([
|
|
120
|
+
makeIssue({
|
|
121
|
+
status: 'Unread',
|
|
122
|
+
dependedIssueUrls: ['https://github.com/demo/repo/issues/99'],
|
|
123
|
+
}),
|
|
124
|
+
]);
|
|
125
|
+
expect(result.unread.items).toHaveLength(0);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('rejects issues with a next action date', () => {
|
|
129
|
+
const result = run([
|
|
130
|
+
makeIssue({
|
|
131
|
+
status: 'Unread',
|
|
132
|
+
nextActionDate: new Date('2026-07-01T00:00:00.000Z'),
|
|
133
|
+
}),
|
|
134
|
+
]);
|
|
135
|
+
expect(result.unread.items).toHaveLength(0);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('rejects issues with a next action hour', () => {
|
|
139
|
+
const result = run([makeIssue({ status: 'Unread', nextActionHour: 9 })]);
|
|
140
|
+
expect(result.unread.items).toHaveLength(0);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('accepts an issue that passes every actionable condition', () => {
|
|
144
|
+
const result = run([makeIssue({ status: 'Unread' })]);
|
|
145
|
+
expect(result.unread.items).toHaveLength(1);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
describe('per-tab selectors', () => {
|
|
150
|
+
it('selects awaiting quality check items case-insensitively for prs', () => {
|
|
151
|
+
const result = run([
|
|
152
|
+
makeIssue({ status: 'awaiting quality check' }),
|
|
153
|
+
makeIssue({ status: 'Awaiting Quality Check' }),
|
|
154
|
+
makeIssue({ status: 'Unread' }),
|
|
155
|
+
]);
|
|
156
|
+
expect(result.prs.items).toHaveLength(2);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('selects unread items case-insensitively', () => {
|
|
160
|
+
const result = run([
|
|
161
|
+
makeIssue({ status: 'UNREAD' }),
|
|
162
|
+
makeIssue({ status: 'Awaiting Quality Check' }),
|
|
163
|
+
]);
|
|
164
|
+
expect(result.unread.items).toHaveLength(1);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('selects failed preparation items only with exact case', () => {
|
|
168
|
+
const result = run([
|
|
169
|
+
makeIssue({ status: 'Failed Preparation' }),
|
|
170
|
+
makeIssue({ status: 'failed preparation' }),
|
|
171
|
+
makeIssue({ status: 'FAILED PREPARATION' }),
|
|
172
|
+
]);
|
|
173
|
+
expect(result['failed-preparation'].items).toHaveLength(1);
|
|
174
|
+
expect(result['failed-preparation'].items[0].number).toBe(1);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('selects no-story items case-insensitively for triage', () => {
|
|
178
|
+
const result = run([
|
|
179
|
+
makeIssue({ story: 'regular / NO STORY; SET STORY FIELD' }),
|
|
180
|
+
makeIssue({ story: 'no story please' }),
|
|
181
|
+
makeIssue({ story: 'Story Alpha' }),
|
|
182
|
+
makeIssue({ story: null }),
|
|
183
|
+
]);
|
|
184
|
+
expect(result.triage.items).toHaveLength(2);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('common item projection', () => {
|
|
189
|
+
it('projects the expected keys and never includes a body field', () => {
|
|
190
|
+
const result = run([
|
|
191
|
+
makeIssue({
|
|
192
|
+
status: 'Unread',
|
|
193
|
+
story: 'Story Alpha',
|
|
194
|
+
labels: ['bug', 'p1'],
|
|
195
|
+
isPr: true,
|
|
196
|
+
}),
|
|
197
|
+
]);
|
|
198
|
+
const item = result.unread.items[0];
|
|
199
|
+
expect(Object.keys(item).sort()).toEqual(
|
|
200
|
+
[
|
|
201
|
+
'createdAt',
|
|
202
|
+
'isPr',
|
|
203
|
+
'itemId',
|
|
204
|
+
'labels',
|
|
205
|
+
'nameWithOwner',
|
|
206
|
+
'number',
|
|
207
|
+
'projectItemId',
|
|
208
|
+
'repo',
|
|
209
|
+
'story',
|
|
210
|
+
'title',
|
|
211
|
+
'url',
|
|
212
|
+
].sort(),
|
|
213
|
+
);
|
|
214
|
+
expect(item).not.toHaveProperty('body');
|
|
215
|
+
expect(item.repo).toBe('demo/repo');
|
|
216
|
+
expect(item.nameWithOwner).toBe('demo/repo');
|
|
217
|
+
expect(item.projectItemId).toBe(item.itemId);
|
|
218
|
+
expect(item.isPr).toBe(true);
|
|
219
|
+
expect(item.story).toBe('Story Alpha');
|
|
220
|
+
expect(item.labels).toEqual(['bug', 'p1']);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('maps a null story to an empty string', () => {
|
|
224
|
+
const result = run([makeIssue({ status: 'Unread', story: null })]);
|
|
225
|
+
expect(result.unread.items[0].story).toBe('');
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it('serializes createdAt as an ISO string keeping milliseconds', () => {
|
|
229
|
+
const result = run([
|
|
230
|
+
makeIssue({
|
|
231
|
+
status: 'Unread',
|
|
232
|
+
createdAt: new Date('2026-06-13T08:18:45.000Z'),
|
|
233
|
+
}),
|
|
234
|
+
]);
|
|
235
|
+
expect(result.unread.items[0].createdAt).toBe('2026-06-13T08:18:45.000Z');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
describe('story order stable sort', () => {
|
|
240
|
+
it('sorts by story field order and places unknown stories last', () => {
|
|
241
|
+
const result = run([
|
|
242
|
+
makeIssue({ status: 'Unread', story: 'Story Beta' }),
|
|
243
|
+
makeIssue({ status: 'Unread', story: 'Unmapped Story' }),
|
|
244
|
+
makeIssue({ status: 'Unread', story: 'Story Alpha' }),
|
|
245
|
+
makeIssue({ status: 'Unread', story: 'Story Beta' }),
|
|
246
|
+
]);
|
|
247
|
+
expect(result.unread.items.map((i) => i.story)).toEqual([
|
|
248
|
+
'Story Alpha',
|
|
249
|
+
'Story Beta',
|
|
250
|
+
'Story Beta',
|
|
251
|
+
'Unmapped Story',
|
|
252
|
+
]);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('keeps original order between items sharing the same story (stable)', () => {
|
|
256
|
+
const result = run([
|
|
257
|
+
makeIssue({ status: 'Unread', story: 'Story Alpha', title: 'first' }),
|
|
258
|
+
makeIssue({ status: 'Unread', story: 'Story Alpha', title: 'second' }),
|
|
259
|
+
]);
|
|
260
|
+
expect(result.unread.items.map((i) => i.title)).toEqual([
|
|
261
|
+
'first',
|
|
262
|
+
'second',
|
|
263
|
+
]);
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
describe('options construction', () => {
|
|
268
|
+
it('excludes awaiting quality check and done from prs status options', () => {
|
|
269
|
+
const names = run([]).prs.statusOptions.map((o) => o.name);
|
|
270
|
+
expect(names).not.toContain('Awaiting Quality Check');
|
|
271
|
+
expect(names).not.toContain('Done');
|
|
272
|
+
expect(names).toContain('Unread');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('excludes unread and done from unread status options', () => {
|
|
276
|
+
const names = run([]).unread.statusOptions.map((o) => o.name);
|
|
277
|
+
expect(names).not.toContain('Unread');
|
|
278
|
+
expect(names).not.toContain('Done');
|
|
279
|
+
expect(names).toContain('Awaiting Workspace');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('excludes the failed-preparation routing-excluded set', () => {
|
|
283
|
+
const names = run([])['failed-preparation'].statusOptions.map(
|
|
284
|
+
(o) => o.name,
|
|
285
|
+
);
|
|
286
|
+
for (const excluded of [
|
|
287
|
+
'Failed Preparation',
|
|
288
|
+
'Done',
|
|
289
|
+
'Preparation',
|
|
290
|
+
'Icebox',
|
|
291
|
+
'Unread',
|
|
292
|
+
'In Tmux by human',
|
|
293
|
+
]) {
|
|
294
|
+
expect(names).not.toContain(excluded);
|
|
295
|
+
}
|
|
296
|
+
expect(names).toContain('Awaiting Workspace');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('emits all story options for triage and no statusOptions key', () => {
|
|
300
|
+
const triage = run([]).triage;
|
|
301
|
+
expect(triage.storyOptions.map((o) => o.name)).toEqual([
|
|
302
|
+
'regular / NO STORY; SET STORY FIELD',
|
|
303
|
+
'Story Alpha',
|
|
304
|
+
'Story Beta',
|
|
305
|
+
]);
|
|
306
|
+
expect(triage).not.toHaveProperty('statusOptions');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('builds storyOrder from story field option order', () => {
|
|
310
|
+
expect(run([]).prs.storyOrder).toEqual([
|
|
311
|
+
'regular / NO STORY; SET STORY FIELD',
|
|
312
|
+
'Story Alpha',
|
|
313
|
+
'Story Beta',
|
|
314
|
+
]);
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('storyColors shape per tab', () => {
|
|
319
|
+
it('uses object color values for prs, unread and failed-preparation', () => {
|
|
320
|
+
const result = run([]);
|
|
321
|
+
expect(result.prs.storyColors['Story Alpha']).toEqual({ color: 'BLUE' });
|
|
322
|
+
expect(result.unread.storyColors['Story Beta']).toEqual({
|
|
323
|
+
color: 'GREEN',
|
|
324
|
+
});
|
|
325
|
+
expect(result['failed-preparation'].storyColors['Story Alpha']).toEqual({
|
|
326
|
+
color: 'BLUE',
|
|
327
|
+
});
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it('uses plain string color values for triage', () => {
|
|
331
|
+
const result = run([]);
|
|
332
|
+
expect(result.triage.storyColors['Story Alpha']).toBe('BLUE');
|
|
333
|
+
expect(result.triage.storyColors['Story Beta']).toBe('GREEN');
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
describe('generatedAt and pjcode passthrough', () => {
|
|
338
|
+
it('writes the provided generatedAt without milliseconds on every tab', () => {
|
|
339
|
+
const result = run([]);
|
|
340
|
+
expect(result.prs.generatedAt).toBe(generatedAt);
|
|
341
|
+
expect(result.triage.generatedAt).toBe(generatedAt);
|
|
342
|
+
expect(result.unread.generatedAt).toBe(generatedAt);
|
|
343
|
+
expect(result['failed-preparation'].generatedAt).toBe(generatedAt);
|
|
344
|
+
expect(generatedAt).not.toMatch(/\.\d{3}Z$/);
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it('writes the configured pjcode on every tab', () => {
|
|
348
|
+
const result = run([]);
|
|
349
|
+
expect(result.prs.pjcode).toBe('demo');
|
|
350
|
+
expect(result.triage.pjcode).toBe('demo');
|
|
351
|
+
expect(result.unread.pjcode).toBe('demo');
|
|
352
|
+
expect(result['failed-preparation'].pjcode).toBe('demo');
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe('project without a story field', () => {
|
|
357
|
+
const projectNoStory = baseProject(null);
|
|
358
|
+
|
|
359
|
+
it('degrades story order, colors and triage options gracefully', () => {
|
|
360
|
+
const result = run(
|
|
361
|
+
[makeIssue({ status: 'Unread', story: 'no story' })],
|
|
362
|
+
projectNoStory,
|
|
363
|
+
);
|
|
364
|
+
expect(result.prs.storyOrder).toEqual([]);
|
|
365
|
+
expect(result.prs.storyColors).toEqual({});
|
|
366
|
+
expect(result.triage.storyOptions).toEqual([]);
|
|
367
|
+
expect(result.triage.storyColors).toEqual({});
|
|
368
|
+
expect(result.unread.items).toHaveLength(1);
|
|
369
|
+
expect(result.triage.items).toHaveLength(1);
|
|
370
|
+
});
|
|
371
|
+
});
|
|
372
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
import { FieldOption, Project } from '../../entities/Project';
|
|
3
|
+
|
|
4
|
+
export type ConsoleColor = FieldOption['color'];
|
|
5
|
+
|
|
6
|
+
export type ConsoleListItem = {
|
|
7
|
+
number: number;
|
|
8
|
+
title: string;
|
|
9
|
+
url: string;
|
|
10
|
+
repo: string;
|
|
11
|
+
nameWithOwner: string;
|
|
12
|
+
projectItemId: string;
|
|
13
|
+
itemId: string;
|
|
14
|
+
isPr: boolean;
|
|
15
|
+
story: string;
|
|
16
|
+
labels: string[];
|
|
17
|
+
createdAt: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
export type ConsoleFieldOption = {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
color: ConsoleColor;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type ConsoleStatusTab = {
|
|
27
|
+
pjcode: string;
|
|
28
|
+
generatedAt: string;
|
|
29
|
+
statusOptions: ConsoleFieldOption[];
|
|
30
|
+
storyOrder: string[];
|
|
31
|
+
storyColors: Record<string, { color: ConsoleColor }>;
|
|
32
|
+
items: ConsoleListItem[];
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export type ConsoleTriageTab = {
|
|
36
|
+
pjcode: string;
|
|
37
|
+
generatedAt: string;
|
|
38
|
+
storyOptions: ConsoleFieldOption[];
|
|
39
|
+
storyOrder: string[];
|
|
40
|
+
storyColors: Record<string, ConsoleColor>;
|
|
41
|
+
items: ConsoleListItem[];
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export type ConsoleTabName = 'prs' | 'triage' | 'unread' | 'failed-preparation';
|
|
45
|
+
|
|
46
|
+
export type ConsoleLists = {
|
|
47
|
+
prs: ConsoleStatusTab;
|
|
48
|
+
triage: ConsoleTriageTab;
|
|
49
|
+
unread: ConsoleStatusTab;
|
|
50
|
+
'failed-preparation': ConsoleStatusTab;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type GenerateConsoleListsInput = {
|
|
54
|
+
project: Project;
|
|
55
|
+
issues: Issue[];
|
|
56
|
+
pjcode: string;
|
|
57
|
+
assigneeLogin: string;
|
|
58
|
+
generatedAt: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const UNKNOWN_STORY_SORT_INDEX = 999999;
|
|
62
|
+
|
|
63
|
+
export class GenerateConsoleListsUseCase {
|
|
64
|
+
run = (input: GenerateConsoleListsInput): ConsoleLists => {
|
|
65
|
+
const { project, issues, pjcode, assigneeLogin, generatedAt } = input;
|
|
66
|
+
|
|
67
|
+
const storyOptions = project.story ? project.story.stories : [];
|
|
68
|
+
const storyOrder = storyOptions.map((option) => option.name);
|
|
69
|
+
const statusOptions = project.status.statuses;
|
|
70
|
+
|
|
71
|
+
const actionableIssues = issues.filter((issue) =>
|
|
72
|
+
this.isActionable(issue, assigneeLogin),
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
const buildStatusTab = (
|
|
76
|
+
selector: (issue: Issue) => boolean,
|
|
77
|
+
excludedStatusNames: string[],
|
|
78
|
+
): ConsoleStatusTab => ({
|
|
79
|
+
pjcode,
|
|
80
|
+
generatedAt,
|
|
81
|
+
statusOptions: this.buildFieldOptions(statusOptions, excludedStatusNames),
|
|
82
|
+
storyOrder,
|
|
83
|
+
storyColors: this.buildStoryColorsObject(storyOptions),
|
|
84
|
+
items: this.sortByStoryOrder(
|
|
85
|
+
actionableIssues
|
|
86
|
+
.filter(selector)
|
|
87
|
+
.map((issue) => this.projectItem(issue)),
|
|
88
|
+
storyOrder,
|
|
89
|
+
),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
prs: buildStatusTab(
|
|
94
|
+
(issue) =>
|
|
95
|
+
issue.status !== null &&
|
|
96
|
+
issue.status.toLowerCase() === 'awaiting quality check',
|
|
97
|
+
['awaiting quality check', 'done'],
|
|
98
|
+
),
|
|
99
|
+
unread: buildStatusTab(
|
|
100
|
+
(issue) =>
|
|
101
|
+
issue.status !== null && issue.status.toLowerCase() === 'unread',
|
|
102
|
+
['unread', 'done'],
|
|
103
|
+
),
|
|
104
|
+
'failed-preparation': buildStatusTab(
|
|
105
|
+
(issue) => issue.status === 'Failed Preparation',
|
|
106
|
+
[
|
|
107
|
+
'failed preparation',
|
|
108
|
+
'done',
|
|
109
|
+
'preparation',
|
|
110
|
+
'icebox',
|
|
111
|
+
'unread',
|
|
112
|
+
'in tmux by human',
|
|
113
|
+
],
|
|
114
|
+
),
|
|
115
|
+
triage: {
|
|
116
|
+
pjcode,
|
|
117
|
+
generatedAt,
|
|
118
|
+
storyOptions: this.buildFieldOptions(storyOptions, []),
|
|
119
|
+
storyOrder,
|
|
120
|
+
storyColors: this.buildStoryColorsString(storyOptions),
|
|
121
|
+
items: this.sortByStoryOrder(
|
|
122
|
+
actionableIssues
|
|
123
|
+
.filter(
|
|
124
|
+
(issue) =>
|
|
125
|
+
issue.story !== null &&
|
|
126
|
+
issue.story.toLowerCase().includes('no story'),
|
|
127
|
+
)
|
|
128
|
+
.map((issue) => this.projectItem(issue)),
|
|
129
|
+
storyOrder,
|
|
130
|
+
),
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
private isActionable = (issue: Issue, assigneeLogin: string): boolean =>
|
|
136
|
+
issue.isClosed === false &&
|
|
137
|
+
issue.assignees.includes(assigneeLogin) &&
|
|
138
|
+
issue.dependedIssueUrls.length === 0 &&
|
|
139
|
+
issue.nextActionDate === null &&
|
|
140
|
+
issue.nextActionHour === null;
|
|
141
|
+
|
|
142
|
+
private projectItem = (issue: Issue): ConsoleListItem => ({
|
|
143
|
+
number: issue.number,
|
|
144
|
+
title: issue.title,
|
|
145
|
+
url: issue.url,
|
|
146
|
+
repo: issue.nameWithOwner,
|
|
147
|
+
nameWithOwner: issue.nameWithOwner,
|
|
148
|
+
projectItemId: issue.itemId,
|
|
149
|
+
itemId: issue.itemId,
|
|
150
|
+
isPr: issue.isPr,
|
|
151
|
+
story: issue.story ?? '',
|
|
152
|
+
labels: issue.labels,
|
|
153
|
+
createdAt: issue.createdAt.toISOString(),
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
private buildFieldOptions = (
|
|
157
|
+
options: FieldOption[],
|
|
158
|
+
excludedLowerCaseNames: string[],
|
|
159
|
+
): ConsoleFieldOption[] =>
|
|
160
|
+
options
|
|
161
|
+
.filter(
|
|
162
|
+
(option) => !excludedLowerCaseNames.includes(option.name.toLowerCase()),
|
|
163
|
+
)
|
|
164
|
+
.map((option) => ({
|
|
165
|
+
id: option.id,
|
|
166
|
+
name: option.name,
|
|
167
|
+
color: option.color,
|
|
168
|
+
}));
|
|
169
|
+
|
|
170
|
+
private buildStoryColorsObject = (
|
|
171
|
+
options: FieldOption[],
|
|
172
|
+
): Record<string, { color: ConsoleColor }> => {
|
|
173
|
+
const result: Record<string, { color: ConsoleColor }> = {};
|
|
174
|
+
for (const option of options) {
|
|
175
|
+
result[option.name] = { color: option.color };
|
|
176
|
+
}
|
|
177
|
+
return result;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
private buildStoryColorsString = (
|
|
181
|
+
options: FieldOption[],
|
|
182
|
+
): Record<string, ConsoleColor> => {
|
|
183
|
+
const result: Record<string, ConsoleColor> = {};
|
|
184
|
+
for (const option of options) {
|
|
185
|
+
result[option.name] = option.color;
|
|
186
|
+
}
|
|
187
|
+
return result;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
private sortByStoryOrder = (
|
|
191
|
+
items: ConsoleListItem[],
|
|
192
|
+
storyOrder: string[],
|
|
193
|
+
): ConsoleListItem[] => {
|
|
194
|
+
const indexByStory = new Map(
|
|
195
|
+
storyOrder.map((name, index) => [name, index]),
|
|
196
|
+
);
|
|
197
|
+
return items
|
|
198
|
+
.map((item, position) => ({
|
|
199
|
+
item,
|
|
200
|
+
position,
|
|
201
|
+
sortKey: indexByStory.get(item.story) ?? UNKNOWN_STORY_SORT_INDEX,
|
|
202
|
+
}))
|
|
203
|
+
.sort((a, b) => a.sortKey - b.sortKey || a.position - b.position)
|
|
204
|
+
.map((entry) => entry.item);
|
|
205
|
+
};
|
|
206
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AAwBA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AA8B3D,qBAAa,kCAAkC;IAC7C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,KAChB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,eAAe,EAAE,IAAI,EAAE,CAAC;KACzB,GAAG,IAAI,CAAC,CAmUP;CACH"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Issue } from '../../../domain/entities/Issue';
|
|
2
|
+
import type { Project } from '../../../domain/entities/Project';
|
|
3
|
+
export type ConsoleListsWriterParams = {
|
|
4
|
+
consoleDataOutputDir: string | null | undefined;
|
|
5
|
+
pjcode: string | null | undefined;
|
|
6
|
+
assigneeLogin: string | null | undefined;
|
|
7
|
+
project: Project;
|
|
8
|
+
issues: Issue[];
|
|
9
|
+
generatedAt?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare const formatConsoleGeneratedAt: (date: Date) => string;
|
|
12
|
+
export declare const writeConsoleLists: (params: ConsoleListsWriterParams) => void;
|
|
13
|
+
//# sourceMappingURL=consoleListsWriter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consoleListsWriter.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/consoleListsWriter.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AAC5D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAOhE,MAAM,MAAM,wBAAwB,GAAG;IACrC,oBAAoB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAChD,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACzC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AASF,eAAO,MAAM,wBAAwB,GAAI,MAAM,IAAI,KAAG,MACR,CAAC;AAU/C,eAAO,MAAM,iBAAiB,GAAI,QAAQ,wBAAwB,KAAG,IAsBpE,CAAC"}
|
|
@@ -21,6 +21,7 @@ export declare class StartPreparationUseCase {
|
|
|
21
21
|
private weeklyLimitTypeForModel;
|
|
22
22
|
private isWithinCooldown;
|
|
23
23
|
private isModelWeeklyLimitRejected;
|
|
24
|
+
private selectModelForToken;
|
|
24
25
|
private secondsUntilSevenDayReset;
|
|
25
26
|
private compareBySevenDayDeadlineThenUtilization;
|
|
26
27
|
private taperedConcurrentLimit;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"StartPreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/StartPreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAShE,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAUhC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;gBAZ1B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,mBAAmB,GACnB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,yBAAyB,GACzB,oBAAoB,CACvB,EACgB,kBAAkB,EAAE,kBAAkB,EACtC,0BAA0B,EAAE,0BAA0B;IAGzE,OAAO,CAAC,uBAAuB,CAK7B;IAEF,OAAO,CAAC,gBAAgB,CAGgC;IAExD,OAAO,CAAC,0BAA0B,CAQhC;IAEF,OAAO,CAAC,yBAAyB,CAc/B;IAEF,OAAO,CAAC,wCAAwC,
|
|
1
|
+
{"version":3,"file":"StartPreparationUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/StartPreparationUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAC;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AAC7F,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAShE,eAAO,MAAM,+BAA+B,oBAAoB,CAAC;AAEjE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,qBAAa,uBAAuB;IAEhC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,eAAe;IAUhC,OAAO,CAAC,QAAQ,CAAC,kBAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,0BAA0B;gBAZ1B,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,EACtD,eAAe,EAAE,IAAI,CACpC,eAAe,EACb,mBAAmB,GACnB,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,yBAAyB,GACzB,oBAAoB,CACvB,EACgB,kBAAkB,EAAE,kBAAkB,EACtC,0BAA0B,EAAE,0BAA0B;IAGzE,OAAO,CAAC,uBAAuB,CAK7B;IAEF,OAAO,CAAC,gBAAgB,CAGgC;IAExD,OAAO,CAAC,0BAA0B,CAQhC;IAEF,OAAO,CAAC,mBAAmB,CAqBzB;IAEF,OAAO,CAAC,yBAAyB,CAc/B;IAEF,OAAO,CAAC,wCAAwC,CAqB9C;IAEF,OAAO,CAAC,sBAAsB,CAS5B;IAEF,uBAAuB,GACrB,qBAAqB,MAAM,EAC3B,qBAAqB,MAAM,KAC1B,MAAM,CAUP;IAEF,OAAO,CAAC,oBAAoB,CA4E1B;IAEF,kBAAkB,GAChB,aAAa,gBAAgB,EAAE,EAC/B,gCAAgC,MAAM,EACtC,WAAW,MAAM,GAAG,IAAI,KACvB,kBAAkB,EAAE,CAqDrB;IAEF,GAAG,GAAU,QAAQ;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,gBAAgB,EAAE,MAAM,CAAC;QACzB,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;QACpC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;QACnC,cAAc,EAAE,MAAM,CAAC;QACvB,2BAA2B,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3C,8BAA8B,EAAE,MAAM,CAAC;QACvC,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACrC,mBAAmB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;QACrC,sBAAsB,EAAE,MAAM,CAAC;QAC/B,oBAAoB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;KACvC,KAAG,OAAO,CAAC;QAAE,aAAa,EAAE,kBAAkB,EAAE,GAAG,IAAI,CAAA;KAAE,CAAC,CA6SzD;CACH"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Issue } from '../../entities/Issue';
|
|
2
|
+
import { FieldOption, Project } from '../../entities/Project';
|
|
3
|
+
export type ConsoleColor = FieldOption['color'];
|
|
4
|
+
export type ConsoleListItem = {
|
|
5
|
+
number: number;
|
|
6
|
+
title: string;
|
|
7
|
+
url: string;
|
|
8
|
+
repo: string;
|
|
9
|
+
nameWithOwner: string;
|
|
10
|
+
projectItemId: string;
|
|
11
|
+
itemId: string;
|
|
12
|
+
isPr: boolean;
|
|
13
|
+
story: string;
|
|
14
|
+
labels: string[];
|
|
15
|
+
createdAt: string;
|
|
16
|
+
};
|
|
17
|
+
export type ConsoleFieldOption = {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
color: ConsoleColor;
|
|
21
|
+
};
|
|
22
|
+
export type ConsoleStatusTab = {
|
|
23
|
+
pjcode: string;
|
|
24
|
+
generatedAt: string;
|
|
25
|
+
statusOptions: ConsoleFieldOption[];
|
|
26
|
+
storyOrder: string[];
|
|
27
|
+
storyColors: Record<string, {
|
|
28
|
+
color: ConsoleColor;
|
|
29
|
+
}>;
|
|
30
|
+
items: ConsoleListItem[];
|
|
31
|
+
};
|
|
32
|
+
export type ConsoleTriageTab = {
|
|
33
|
+
pjcode: string;
|
|
34
|
+
generatedAt: string;
|
|
35
|
+
storyOptions: ConsoleFieldOption[];
|
|
36
|
+
storyOrder: string[];
|
|
37
|
+
storyColors: Record<string, ConsoleColor>;
|
|
38
|
+
items: ConsoleListItem[];
|
|
39
|
+
};
|
|
40
|
+
export type ConsoleTabName = 'prs' | 'triage' | 'unread' | 'failed-preparation';
|
|
41
|
+
export type ConsoleLists = {
|
|
42
|
+
prs: ConsoleStatusTab;
|
|
43
|
+
triage: ConsoleTriageTab;
|
|
44
|
+
unread: ConsoleStatusTab;
|
|
45
|
+
'failed-preparation': ConsoleStatusTab;
|
|
46
|
+
};
|
|
47
|
+
export type GenerateConsoleListsInput = {
|
|
48
|
+
project: Project;
|
|
49
|
+
issues: Issue[];
|
|
50
|
+
pjcode: string;
|
|
51
|
+
assigneeLogin: string;
|
|
52
|
+
generatedAt: string;
|
|
53
|
+
};
|
|
54
|
+
export declare class GenerateConsoleListsUseCase {
|
|
55
|
+
run: (input: GenerateConsoleListsInput) => ConsoleLists;
|
|
56
|
+
private isActionable;
|
|
57
|
+
private projectItem;
|
|
58
|
+
private buildFieldOptions;
|
|
59
|
+
private buildStoryColorsObject;
|
|
60
|
+
private buildStoryColorsString;
|
|
61
|
+
private sortByStoryOrder;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=GenerateConsoleListsUseCase.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GenerateConsoleListsUseCase.d.ts","sourceRoot":"","sources":["../../../../src/domain/usecases/console/GenerateConsoleListsUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAE9D,MAAM,MAAM,YAAY,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;AAEhD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,YAAY,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,kBAAkB,EAAE,CAAC;IACpC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC,CAAC;IACrD,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAC1C,KAAK,EAAE,eAAe,EAAE,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,oBAAoB,CAAC;AAEhF,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,gBAAgB,CAAC;IACtB,MAAM,EAAE,gBAAgB,CAAC;IACzB,MAAM,EAAE,gBAAgB,CAAC;IACzB,oBAAoB,EAAE,gBAAgB,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAIF,qBAAa,2BAA2B;IACtC,GAAG,GAAI,OAAO,yBAAyB,KAAG,YAAY,CAqEpD;IAEF,OAAO,CAAC,YAAY,CAKY;IAEhC,OAAO,CAAC,WAAW,CAYhB;IAEH,OAAO,CAAC,iBAAiB,CAYjB;IAER,OAAO,CAAC,sBAAsB,CAQ5B;IAEF,OAAO,CAAC,sBAAsB,CAQ5B;IAEF,OAAO,CAAC,gBAAgB,CAetB;CACH"}
|