github-issue-tower-defence-management 1.28.0 → 1.29.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.
@@ -39,13 +39,13 @@ jobs:
39
39
  DOTENV_CONFIG_QUIET: true
40
40
 
41
41
  - name: Upload test results
42
- uses: actions/upload-artifact@v6
42
+ uses: actions/upload-artifact@v7
43
43
  with:
44
44
  name: jest-junit-report
45
45
  path: reports/jest-junit
46
46
 
47
47
  - name: Upload artifacts
48
- uses: actions/upload-artifact@v6
48
+ uses: actions/upload-artifact@v7
49
49
  with:
50
50
  name: reports
51
51
  path: reports
package/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ # [1.29.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.28.0...v1.29.0) (2026-03-08)
2
+
3
+
4
+ ### Features
5
+
6
+ * **core:** add story view link to top of story issue in ConvertCheckboxToIssueInStoryIssueUseCase ([6325145](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/6325145baed3e55783220a32dffcfa9e2d12768e))
7
+
1
8
  # [1.28.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.27.1...v1.28.0) (2026-03-04)
2
9
 
3
10
 
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ConvertCheckboxToIssueInStoryIssueUseCase = void 0;
4
+ const utils_1 = require("./utils");
4
5
  class ConvertCheckboxToIssueInStoryIssueUseCase {
5
6
  constructor(issueRepository) {
6
7
  this.issueRepository = issueRepository;
@@ -22,11 +23,19 @@ class ConvertCheckboxToIssueInStoryIssueUseCase {
22
23
  storyIssue.status === input.disabledStatus) {
23
24
  continue;
24
25
  }
25
- else if (!storyIssue.body.includes('- [ ] ')) {
26
+ const storyViewLink = this.buildStoryViewLink(input.urlOfStoryView, storyOption.name);
27
+ let newBody = storyIssue.body;
28
+ if (!storyIssue.body.includes(storyViewLink)) {
29
+ newBody = `${storyViewLink}\n\n${newBody}`;
30
+ await this.issueRepository.updateIssue({
31
+ ...storyIssue,
32
+ body: newBody,
33
+ });
34
+ }
35
+ if (!newBody.includes('- [ ] ')) {
26
36
  continue;
27
37
  }
28
- const checkboxTextsNotCreatedIssue = this.findCheckboxTextsNotCreatedIssue(storyIssue.body);
29
- let newBody = storyIssue.body;
38
+ const checkboxTextsNotCreatedIssue = this.findCheckboxTextsNotCreatedIssue(newBody);
30
39
  for (const checkboxText of checkboxTextsNotCreatedIssue) {
31
40
  const issueTitle = checkboxText.replace('STORYNAME', `${storyOption.name} #${storyIssue.number}`);
32
41
  const newIssueBody = `- Parent issue: ${storyIssue.url}`;
@@ -46,6 +55,9 @@ class ConvertCheckboxToIssueInStoryIssueUseCase {
46
55
  }
47
56
  }
48
57
  };
58
+ this.buildStoryViewLink = (urlOfStoryView, storyName) => {
59
+ return `${urlOfStoryView}?sliceBy%5Bvalue%5D=${(0, utils_1.encodeForURI)(storyName)}`;
60
+ };
49
61
  this.findCheckboxTextsNotCreatedIssue = (storyIssueBody) => {
50
62
  const regexToFindCheckboxes = /^- \[ ] (.*)$/gm;
51
63
  const match = storyIssueBody.match(regexToFindCheckboxes);
@@ -1 +1 @@
1
- {"version":3,"file":"ConvertCheckboxToIssueInStoryIssueUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ConvertCheckboxToIssueInStoryIssueUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.28.0",
3
+ "version": "1.29.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -10,19 +10,20 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
10
10
  const mockIssueRepository = mock<IssueRepository>();
11
11
 
12
12
  describe('run', () => {
13
+ const basicStory = {
14
+ name: 'Story Field',
15
+ databaseId: 1,
16
+ fieldId: 'storyFieldId',
17
+ stories: [
18
+ { ...mock<StoryOption>(), id: 'story1', name: 'Story 1' },
19
+ { ...mock<StoryOption>(), id: 'story2', name: 'Story 2' },
20
+ { ...mock<StoryOption>(), id: 'regular3', name: 'regular / Story 3' },
21
+ ],
22
+ workflowManagementStory: { id: 'workflow1', name: 'Workflow Story' },
23
+ };
13
24
  const basicProject: Project = {
14
25
  ...mock<Project>(),
15
- story: {
16
- name: 'Story Field',
17
- databaseId: 1,
18
- fieldId: 'storyFieldId',
19
- stories: [
20
- { ...mock<StoryOption>(), id: 'story1', name: 'Story 1' },
21
- { ...mock<StoryOption>(), id: 'story2', name: 'Story 2' },
22
- { ...mock<StoryOption>(), id: 'regular3', name: 'regular / Story 3' },
23
- ],
24
- workflowManagementStory: { id: 'workflow1', name: 'Workflow Story' },
25
- },
26
+ story: basicStory,
26
27
  };
27
28
 
28
29
  const basicStoryIssue1 = {
@@ -202,7 +203,7 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
202
203
  expectedGetIssueByUrlCalls: [],
203
204
  },
204
205
  {
205
- name: 'should create new issues for checkboxes and update story issue',
206
+ name: 'should add story view link and create new issues for checkboxes',
206
207
  input: {
207
208
  project: basicProject,
208
209
  issues: [basicStoryIssue1, basicStoryIssue2],
@@ -249,28 +250,54 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
249
250
  [
250
251
  {
251
252
  ...basicStoryIssue1,
252
- body: `- [ ] https://github.com/org/repo/issues/1
253
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
254
+
255
+ - [ ] Task 1
256
+ - [ ] Task 2`,
257
+ },
258
+ ],
259
+ [
260
+ {
261
+ ...basicStoryIssue1,
262
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
263
+
264
+ - [ ] https://github.com/org/repo/issues/1
253
265
  - [ ] Task 2`,
254
266
  },
255
267
  ],
256
268
  [
257
269
  {
258
270
  ...basicStoryIssue1,
259
- body: `- [ ] https://github.com/org/repo/issues/1
271
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
272
+
273
+ - [ ] https://github.com/org/repo/issues/1
260
274
  - [ ] https://github.com/org/repo/issues/2`,
261
275
  },
262
276
  ],
263
277
  [
264
278
  {
265
279
  ...basicStoryIssue2,
266
- body: `- [ ] https://github.com/org/repo/issues/3
280
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
281
+
282
+ - [ ] Task 3
267
283
  - [ ] Task 4`,
268
284
  },
269
285
  ],
270
286
  [
271
287
  {
272
288
  ...basicStoryIssue2,
273
- body: `- [ ] https://github.com/org/repo/issues/3
289
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
290
+
291
+ - [ ] https://github.com/org/repo/issues/3
292
+ - [ ] Task 4`,
293
+ },
294
+ ],
295
+ [
296
+ {
297
+ ...basicStoryIssue2,
298
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
299
+
300
+ - [ ] https://github.com/org/repo/issues/3
274
301
  - [ ] https://github.com/org/repo/issues/4`,
275
302
  },
276
303
  ],
@@ -316,6 +343,119 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
316
343
  ['https://github.com/org/repo/issues/4'],
317
344
  ],
318
345
  },
346
+ {
347
+ name: 'should not add story view link when it already exists',
348
+ input: {
349
+ project: {
350
+ ...basicProject,
351
+ story: {
352
+ ...basicStory,
353
+ stories: [
354
+ { ...mock<StoryOption>(), id: 'story1', name: 'Story 1' },
355
+ ],
356
+ },
357
+ },
358
+ issues: [
359
+ {
360
+ ...basicStoryIssue1,
361
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
362
+
363
+ - [ ] Task 1`,
364
+ },
365
+ ],
366
+ cacheUsed: false,
367
+ urlOfStoryView: 'https://example.com',
368
+ disabledStatus: 'Closed',
369
+ storyObjectMap: new Map([['Story 1', basicStoryObject1]]),
370
+ },
371
+ expectedCreateNewIssueCalls: [
372
+ [
373
+ 'org',
374
+ 'repo',
375
+ 'Task 1',
376
+ '- Parent issue: https://github.com/org/repo/issues/123',
377
+ [],
378
+ [],
379
+ ],
380
+ ],
381
+ expectedUpdateIssueCalls: [
382
+ [
383
+ {
384
+ ...basicStoryIssue1,
385
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
386
+
387
+ - [ ] https://github.com/org/repo/issues/1`,
388
+ },
389
+ ],
390
+ ],
391
+ expectedUpdateStoryCalls: [
392
+ [
393
+ {
394
+ ...basicProject,
395
+ story: {
396
+ ...basicStory,
397
+ stories: [
398
+ { ...mock<StoryOption>(), id: 'story1', name: 'Story 1' },
399
+ ],
400
+ },
401
+ },
402
+ {
403
+ ...mock<Issue>(),
404
+ url: 'https://github.com/org/repo/issues/1',
405
+ },
406
+ 'story1',
407
+ ],
408
+ ],
409
+ expectedGetIssueByUrlCalls: [['https://github.com/org/repo/issues/1']],
410
+ },
411
+ {
412
+ name: 'should add story view link even when no checkboxes exist',
413
+ input: {
414
+ project: {
415
+ ...basicProject,
416
+ story: {
417
+ ...basicStory,
418
+ stories: [
419
+ { ...mock<StoryOption>(), id: 'story1', name: 'Story 1' },
420
+ ],
421
+ },
422
+ },
423
+ issues: [
424
+ {
425
+ ...basicStoryIssue1,
426
+ body: 'Some description without checkboxes',
427
+ },
428
+ ],
429
+ cacheUsed: false,
430
+ urlOfStoryView: 'https://example.com',
431
+ disabledStatus: 'Closed',
432
+ storyObjectMap: new Map([
433
+ [
434
+ 'Story 1',
435
+ {
436
+ ...basicStoryObject1,
437
+ storyIssue: {
438
+ ...basicStoryIssue1,
439
+ body: 'Some description without checkboxes',
440
+ },
441
+ },
442
+ ],
443
+ ]),
444
+ },
445
+ expectedCreateNewIssueCalls: [],
446
+ expectedUpdateIssueCalls: [
447
+ [
448
+ {
449
+ ...basicStoryIssue1,
450
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
451
+
452
+ Some description without checkboxes`,
453
+ },
454
+ ],
455
+ ],
456
+ expectedUpdateStoryCalls: [],
457
+ expectedGetIssueByUrlCalls: [],
458
+ },
319
459
  {
320
460
  name: 'should create new issues with replaced STORYNAME for checkboxes and update story issue',
321
461
  input: {
@@ -371,28 +511,54 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
371
511
  [
372
512
  {
373
513
  ...basicStoryIssue1,
374
- body: `- [ ] https://github.com/org/repo/issues/1
514
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
515
+
516
+ - [ ] Task 1
375
517
  - [ ] Task 2 for \`STORYNAME\``,
376
518
  },
377
519
  ],
378
520
  [
379
521
  {
380
522
  ...basicStoryIssue1,
381
- body: `- [ ] https://github.com/org/repo/issues/1
523
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
524
+
525
+ - [ ] https://github.com/org/repo/issues/1
526
+ - [ ] Task 2 for \`STORYNAME\``,
527
+ },
528
+ ],
529
+ [
530
+ {
531
+ ...basicStoryIssue1,
532
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
533
+
534
+ - [ ] https://github.com/org/repo/issues/1
382
535
  - [ ] https://github.com/org/repo/issues/2`,
383
536
  },
384
537
  ],
385
538
  [
386
539
  {
387
540
  ...basicStoryIssue2,
388
- body: `- [ ] https://github.com/org/repo/issues/3
541
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
542
+
543
+ - [ ] Task 3
544
+ - [ ] Task 4`,
545
+ },
546
+ ],
547
+ [
548
+ {
549
+ ...basicStoryIssue2,
550
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
551
+
552
+ - [ ] https://github.com/org/repo/issues/3
389
553
  - [ ] Task 4`,
390
554
  },
391
555
  ],
392
556
  [
393
557
  {
394
558
  ...basicStoryIssue2,
395
- body: `- [ ] https://github.com/org/repo/issues/3
559
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
560
+
561
+ - [ ] https://github.com/org/repo/issues/3
396
562
  - [ ] https://github.com/org/repo/issues/4`,
397
563
  },
398
564
  ],
@@ -520,7 +686,29 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
520
686
  ...basicStoryIssue1,
521
687
  org: 'orgA',
522
688
  repo: 'repoA',
523
- body: `- [ ] https://github.com/orgA/repoA/issues/1`,
689
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
690
+
691
+ - [ ] Task 1`,
692
+ },
693
+ ],
694
+ [
695
+ {
696
+ ...basicStoryIssue1,
697
+ org: 'orgA',
698
+ repo: 'repoA',
699
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%201
700
+
701
+ - [ ] https://github.com/orgA/repoA/issues/1`,
702
+ },
703
+ ],
704
+ [
705
+ {
706
+ ...basicStoryIssue2,
707
+ org: 'orgB',
708
+ repo: 'repoB',
709
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
710
+
711
+ - [ ] Task 2`,
524
712
  },
525
713
  ],
526
714
  [
@@ -528,7 +716,9 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
528
716
  ...basicStoryIssue2,
529
717
  org: 'orgB',
530
718
  repo: 'repoB',
531
- body: `- [ ] https://github.com/orgB/repoB/issues/2`,
719
+ body: `https://example.com?sliceBy%5Bvalue%5D=Story%202
720
+
721
+ - [ ] https://github.com/orgB/repoB/issues/2`,
532
722
  },
533
723
  ],
534
724
  ],
@@ -610,6 +800,47 @@ describe('ConvertCheckboxToIssueInStoryIssueUseCase', () => {
610
800
  );
611
801
  });
612
802
 
803
+ describe('buildStoryViewLink', () => {
804
+ const testCases: {
805
+ name: string;
806
+ urlOfStoryView: string;
807
+ storyName: string;
808
+ expected: string;
809
+ }[] = [
810
+ {
811
+ name: 'should build story view link with encoded story name',
812
+ urlOfStoryView: 'https://github.com/users/TestUser/projects/1/views/1',
813
+ storyName: 'Story 1',
814
+ expected:
815
+ 'https://github.com/users/TestUser/projects/1/views/1?sliceBy%5Bvalue%5D=Story%201',
816
+ },
817
+ {
818
+ name: 'should handle story name with special characters',
819
+ urlOfStoryView: 'https://github.com/users/TestUser/projects/1/views/1',
820
+ storyName: 'planning business trip for next spring',
821
+ expected:
822
+ 'https://github.com/users/TestUser/projects/1/views/1?sliceBy%5Bvalue%5D=planning%20business%20trip%20for%20next%20spring',
823
+ },
824
+ {
825
+ name: 'should handle story name with hash',
826
+ urlOfStoryView: 'https://github.com/users/TestUser/projects/1/views/1',
827
+ storyName: 'Story #1',
828
+ expected:
829
+ 'https://github.com/users/TestUser/projects/1/views/1?sliceBy%5Bvalue%5D=Story%20%231',
830
+ },
831
+ ];
832
+
833
+ testCases.forEach(({ name, urlOfStoryView, storyName, expected }) => {
834
+ it(name, () => {
835
+ const useCase = new ConvertCheckboxToIssueInStoryIssueUseCase(
836
+ mockIssueRepository,
837
+ );
838
+ const result = useCase.buildStoryViewLink(urlOfStoryView, storyName);
839
+ expect(result).toEqual(expected);
840
+ });
841
+ });
842
+ });
843
+
613
844
  describe('findCheckboxTextsNotCreatedIssue', () => {
614
845
  const testCases: {
615
846
  name: string;
@@ -2,6 +2,7 @@ import { Issue } from '../entities/Issue';
2
2
  import { IssueRepository } from './adapter-interfaces/IssueRepository';
3
3
  import { Project } from '../entities/Project';
4
4
  import { StoryObjectMap } from '../entities/StoryObjectMap';
5
+ import { encodeForURI } from './utils';
5
6
 
6
7
  export class ConvertCheckboxToIssueInStoryIssueUseCase {
7
8
  constructor(
@@ -38,12 +39,24 @@ export class ConvertCheckboxToIssueInStoryIssueUseCase {
38
39
  storyIssue.status === input.disabledStatus
39
40
  ) {
40
41
  continue;
41
- } else if (!storyIssue.body.includes('- [ ] ')) {
42
+ }
43
+ const storyViewLink = this.buildStoryViewLink(
44
+ input.urlOfStoryView,
45
+ storyOption.name,
46
+ );
47
+ let newBody = storyIssue.body;
48
+ if (!storyIssue.body.includes(storyViewLink)) {
49
+ newBody = `${storyViewLink}\n\n${newBody}`;
50
+ await this.issueRepository.updateIssue({
51
+ ...storyIssue,
52
+ body: newBody,
53
+ });
54
+ }
55
+ if (!newBody.includes('- [ ] ')) {
42
56
  continue;
43
57
  }
44
58
  const checkboxTextsNotCreatedIssue =
45
- this.findCheckboxTextsNotCreatedIssue(storyIssue.body);
46
- let newBody = storyIssue.body;
59
+ this.findCheckboxTextsNotCreatedIssue(newBody);
47
60
  for (const checkboxText of checkboxTextsNotCreatedIssue) {
48
61
  const issueTitle = checkboxText.replace(
49
62
  'STORYNAME',
@@ -80,6 +93,9 @@ export class ConvertCheckboxToIssueInStoryIssueUseCase {
80
93
  }
81
94
  }
82
95
  };
96
+ buildStoryViewLink = (urlOfStoryView: string, storyName: string): string => {
97
+ return `${urlOfStoryView}?sliceBy%5Bvalue%5D=${encodeForURI(storyName)}`;
98
+ };
83
99
  findCheckboxTextsNotCreatedIssue = (storyIssueBody: string): string[] => {
84
100
  const regexToFindCheckboxes = /^- \[ ] (.*)$/gm;
85
101
  const match = storyIssueBody.match(regexToFindCheckboxes);
@@ -13,6 +13,7 @@ export declare class ConvertCheckboxToIssueInStoryIssueUseCase {
13
13
  disabledStatus: string;
14
14
  storyObjectMap: StoryObjectMap;
15
15
  }) => Promise<void>;
16
+ buildStoryViewLink: (urlOfStoryView: string, storyName: string) => string;
16
17
  findCheckboxTextsNotCreatedIssue: (storyIssueBody: string) => string[];
17
18
  }
18
19
  //# sourceMappingURL=ConvertCheckboxToIssueInStoryIssueUseCase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ConvertCheckboxToIssueInStoryIssueUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,qBAAa,yCAAyC;IAElD,QAAQ,CAAC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,aAAa,GAAG,eAAe,CACnE;gBAHQ,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,aAAa,GAAG,eAAe,CACnE;IAGH,GAAG,UAAiB;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,cAAc,CAAC;KAChC,KAAG,OAAO,CAAC,IAAI,CAAC,CA6Df;IACF,gCAAgC,mBAAoB,MAAM,KAAG,MAAM,EAAE,CAcnE;CACH"}
1
+ {"version":3,"file":"ConvertCheckboxToIssueInStoryIssueUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ConvertCheckboxToIssueInStoryIssueUseCase.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AACvE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAG5D,qBAAa,yCAAyC;IAElD,QAAQ,CAAC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,aAAa,GAAG,eAAe,CACnE;gBAHQ,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,gBAAgB,GAAG,aAAa,GAAG,aAAa,GAAG,eAAe,CACnE;IAGH,GAAG,UAAiB;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,cAAc,CAAC;KAChC,KAAG,OAAO,CAAC,IAAI,CAAC,CAyEf;IACF,kBAAkB,mBAAoB,MAAM,aAAa,MAAM,KAAG,MAAM,CAEtE;IACF,gCAAgC,mBAAoB,MAAM,KAAG,MAAM,EAAE,CAcnE;CACH"}