github-issue-tower-defence-management 1.166.0 → 1.166.1

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 CHANGED
@@ -1,3 +1,10 @@
1
+ ## [1.166.1](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.166.0...v1.166.1) (2026-08-21)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **depended-issue:** clear closed depended issue urls on every slow sweep ([#1647](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/issues/1647)) ([e796a40](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/commit/e796a40514f53380c504e62930df9343fdfac2e2))
7
+
1
8
  # [1.166.0](https://github.com/HiromiShikata/npm-cli-github-issue-tower-defence-management/compare/v1.165.1...v1.166.0) (2026-08-21)
2
9
 
3
10
 
@@ -6,47 +6,35 @@ class ClearDependedIssueURLUseCase {
6
6
  this.issueRepository = issueRepository;
7
7
  this.run = async (input) => {
8
8
  const dependedIssueUrlSeparatedByComma = input.project.dependedIssueUrlSeparatedByComma;
9
- if (!dependedIssueUrlSeparatedByComma || input.cacheUsed) {
9
+ if (!dependedIssueUrlSeparatedByComma) {
10
10
  return;
11
11
  }
12
+ const absentDependedIssueIsResolvable = !input.cacheUsed;
12
13
  for (const issue of input.issues) {
13
14
  if (issue.dependedIssueUrls.length <= 0 || issue.isClosed) {
14
15
  continue;
15
16
  }
16
- const circularDependedIssueUrls = issue.dependedIssueUrls.filter((dependedIssueUrl) => {
17
- // get all depended issues circularly
18
- const circularDependedIssues = new Set();
19
- const stack = [dependedIssueUrl];
20
- while (stack.length > 0) {
21
- const url = stack.pop();
22
- if (!url) {
23
- throw new Error('url is undefined');
24
- }
25
- if (circularDependedIssues.has(url)) {
26
- continue;
27
- }
28
- circularDependedIssues.add(url);
29
- const dependedIssue = input.issues.find((issue) => issue.url === url);
30
- if (!dependedIssue) {
31
- continue;
32
- }
33
- stack.push(...dependedIssue.dependedIssueUrls);
34
- }
35
- return circularDependedIssues.has(issue.url);
36
- });
17
+ const circularDependedIssueUrls = absentDependedIssueIsResolvable
18
+ ? this.findCircularDependedIssueUrls(issue, input.issues)
19
+ : [];
37
20
  if (circularDependedIssueUrls.length > 0) {
38
21
  await this.issueRepository.clearProjectField(input.project, dependedIssueUrlSeparatedByComma.fieldId, issue);
39
22
  await this.issueRepository.createComment(issue, `Circular dependency removed:
40
23
  ${circularDependedIssueUrls.map((url) => `- ${url}`).join('\n')}`);
41
24
  continue;
42
25
  }
43
- const notFoundDependedIssueUrls = issue.dependedIssueUrls.filter((dependedIssueUrl) => !input.issues.some((depIssue) => depIssue.url === dependedIssueUrl));
44
- const remainingDependedIssueUrls = issue.dependedIssueUrls.filter((dependedIssueUrl) => input.issues.some((depIssue) => depIssue.url === dependedIssueUrl && !depIssue.isClosed));
26
+ const notFoundDependedIssueUrls = absentDependedIssueIsResolvable
27
+ ? issue.dependedIssueUrls.filter((dependedIssueUrl) => !input.issues.some((depIssue) => depIssue.url === dependedIssueUrl))
28
+ : [];
29
+ const openDependedIssueUrls = issue.dependedIssueUrls.filter((dependedIssueUrl) => input.issues.some((depIssue) => depIssue.url === dependedIssueUrl && !depIssue.isClosed));
45
30
  const closedDependedIssueUrls = issue.dependedIssueUrls.filter((dependedIssueUrl) => input.issues.some((depIssue) => depIssue.url === dependedIssueUrl && depIssue.isClosed));
46
31
  if (notFoundDependedIssueUrls.length === 0 &&
47
32
  closedDependedIssueUrls.length === 0) {
48
33
  continue;
49
34
  }
35
+ const remainingDependedIssueUrls = absentDependedIssueIsResolvable
36
+ ? openDependedIssueUrls
37
+ : issue.dependedIssueUrls.filter((dependedIssueUrl) => !closedDependedIssueUrls.includes(dependedIssueUrl));
50
38
  if (remainingDependedIssueUrls.length === 0) {
51
39
  await this.issueRepository.clearProjectField(input.project, dependedIssueUrlSeparatedByComma.fieldId, issue);
52
40
  }
@@ -65,6 +53,26 @@ ${notFoundDependedIssueUrls.map((url) => `- ${url}`).join('\n')}`);
65
53
  }
66
54
  }
67
55
  };
56
+ this.findCircularDependedIssueUrls = (issue, issues) => issue.dependedIssueUrls.filter((dependedIssueUrl) => {
57
+ const reachableIssueUrls = new Set();
58
+ const stack = [dependedIssueUrl];
59
+ while (stack.length > 0) {
60
+ const url = stack.pop();
61
+ if (!url) {
62
+ throw new Error('url is undefined');
63
+ }
64
+ if (reachableIssueUrls.has(url)) {
65
+ continue;
66
+ }
67
+ reachableIssueUrls.add(url);
68
+ const dependedIssue = issues.find((candidate) => candidate.url === url);
69
+ if (!dependedIssue) {
70
+ continue;
71
+ }
72
+ stack.push(...dependedIssue.dependedIssueUrls);
73
+ }
74
+ return reachableIssueUrls.has(issue.url);
75
+ });
68
76
  }
69
77
  }
70
78
  exports.ClearDependedIssueURLUseCase = ClearDependedIssueURLUseCase;
@@ -1 +1 @@
1
- {"version":3,"file":"ClearDependedIssueURLUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ClearDependedIssueURLUseCase.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ClearDependedIssueURLUseCase.js","sourceRoot":"","sources":["../../../src/domain/usecases/ClearDependedIssueURLUseCase.ts"],"names":[],"mappings":";;;AAIA,MAAa,4BAA4B;IACvC,YACW,eAGR;QAHQ,oBAAe,GAAf,eAAe,CAGvB;QAGH,QAAG,GAAG,KAAK,EAAE,KAIZ,EAAiB,EAAE;YAClB,MAAM,gCAAgC,GACpC,KAAK,CAAC,OAAO,CAAC,gCAAgC,CAAC;YACjD,IAAI,CAAC,gCAAgC,EAAE,CAAC;gBACtC,OAAO;YACT,CAAC;YACD,MAAM,+BAA+B,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;YACzD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;gBACjC,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,IAAI,CAAC,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;oBAC1D,SAAS;gBACX,CAAC;gBACD,MAAM,yBAAyB,GAAG,+BAA+B;oBAC/D,CAAC,CAAC,IAAI,CAAC,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC;oBACzD,CAAC,CAAC,EAAE,CAAC;gBACP,IAAI,yBAAyB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACzC,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAC1C,KAAK,CAAC,OAAO,EACb,gCAAgC,CAAC,OAAO,EACxC,KAAK,CACN,CAAC;oBACF,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CACtC,KAAK,EACL;EACR,yBAAyB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACxD,CAAC;oBACF,SAAS;gBACX,CAAC;gBACD,MAAM,yBAAyB,GAAG,+BAA+B;oBAC/D,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAC5B,CAAC,gBAAgB,EAAE,EAAE,CACnB,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAChB,CAAC,QAAQ,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,KAAK,gBAAgB,CAChD,CACJ;oBACH,CAAC,CAAC,EAAE,CAAC;gBACP,MAAM,qBAAqB,GAAG,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAC1D,CAAC,gBAAgB,EAAE,EAAE,CACnB,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,CAAC,QAAQ,EAAE,EAAE,CACX,QAAQ,CAAC,GAAG,KAAK,gBAAgB,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAC1D,CACJ,CAAC;gBACF,MAAM,uBAAuB,GAAG,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAC5D,CAAC,gBAAgB,EAAE,EAAE,CACnB,KAAK,CAAC,MAAM,CAAC,IAAI,CACf,CAAC,QAAQ,EAAE,EAAE,CACX,QAAQ,CAAC,GAAG,KAAK,gBAAgB,IAAI,QAAQ,CAAC,QAAQ,CACzD,CACJ,CAAC;gBACF,IACE,yBAAyB,CAAC,MAAM,KAAK,CAAC;oBACtC,uBAAuB,CAAC,MAAM,KAAK,CAAC,EACpC,CAAC;oBACD,SAAS;gBACX,CAAC;gBACD,MAAM,0BAA0B,GAAG,+BAA+B;oBAChE,CAAC,CAAC,qBAAqB;oBACvB,CAAC,CAAC,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAC5B,CAAC,gBAAgB,EAAE,EAAE,CACnB,CAAC,uBAAuB,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CACtD,CAAC;gBACN,IAAI,0BAA0B,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;oBAC5C,MAAM,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAC1C,KAAK,CAAC,OAAO,EACb,gCAAgC,CAAC,OAAO,EACxC,KAAK,CACN,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,MAAM,IAAI,CAAC,eAAe,CAAC,sBAAsB,CAC/C,KAAK,CAAC,OAAO,EACb,gCAAgC,CAAC,OAAO,EACxC,KAAK,EACL,0BAA0B,CAAC,IAAI,CAAC,GAAG,CAAC,CACrC,CAAC;gBACJ,CAAC;gBACD,IAAI,uBAAuB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACvC,MAAM,UAAU,GACd,0BAA0B,CAAC,MAAM,KAAK,CAAC;wBACvC,yBAAyB,CAAC,MAAM,KAAK,CAAC,CAAC;oBACzC,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CACtC,KAAK,EACL,GAAG,UAAU,CAAC,CAAC,CAAC,kEAAkE,CAAC,CAAC,CAAC,wEAAwE;EACrK,uBAAuB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtD,CAAC;gBACJ,CAAC;gBACD,IAAI,yBAAyB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBACzC,MAAM,IAAI,CAAC,eAAe,CAAC,aAAa,CACtC,KAAK,EACL;EACR,yBAAyB,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACxD,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,CAAC;QAEM,kCAA6B,GAAG,CACtC,KAAY,EACZ,MAAe,EACL,EAAE,CACZ,KAAK,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,gBAAgB,EAAE,EAAE;YAClD,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;YAC7C,MAAM,KAAK,GAAG,CAAC,gBAAgB,CAAC,CAAC;YACjC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACxB,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;gBACxB,IAAI,CAAC,GAAG,EAAE,CAAC;oBACT,MAAM,IAAI,KAAK,CAAC,kBAAkB,CAAC,CAAC;gBACtC,CAAC;gBACD,IAAI,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChC,SAAS;gBACX,CAAC;gBACD,kBAAkB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC5B,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC;gBACxE,IAAI,CAAC,aAAa,EAAE,CAAC;oBACnB,SAAS;gBACX,CAAC;gBACD,KAAK,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAC;YACjD,CAAC;YACD,OAAO,kBAAkB,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;IA5HF,CAAC;CA6HL;AAnID,oEAmIC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "github-issue-tower-defence-management",
3
- "version": "1.166.0",
3
+ "version": "1.166.1",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "scripts": {
@@ -259,12 +259,91 @@ describe('ClearDependedIssueURLUseCase', () => {
259
259
  ],
260
260
  },
261
261
  {
262
- name: 'should not call clearProjectField and createComment when dependedIssue is closed and cacheUsed is true',
262
+ name: 'should call clearProjectField and createComment when dependedIssue is closed and the issue list came from the incremental cache',
263
263
  input: {
264
264
  project: basicProject,
265
265
  issues: [basicIssueOne, basicIssueTwo, basicIssueThree],
266
266
  cacheUsed: true,
267
267
  },
268
+ expectedIssueRepositoryClearProjectFieldCalls: [
269
+ [basicProject, 'fieldId', basicIssueTwo],
270
+ ],
271
+ expectedIssueRepositoryUpdateTextFieldCalls: [
272
+ [basicProject, 'fieldId', basicIssueThree, 'url2'],
273
+ ],
274
+ expectedIssueRepositoryCreateCommentCalls: [
275
+ [
276
+ basicIssueTwo,
277
+ 'All depended issues are already closed, dependency field cleared:\n- url1',
278
+ ],
279
+ [
280
+ basicIssueThree,
281
+ 'Some depended issues are already closed, removed from dependency field:\n- url1',
282
+ ],
283
+ ],
284
+ },
285
+ {
286
+ name: 'should not call clearProjectField and createComment when dependedIssue is not found and the issue list came from the incremental cache',
287
+ input: {
288
+ project: basicProject,
289
+ issues: [
290
+ basicIssueOne,
291
+ {
292
+ ...basicIssueTwo,
293
+ dependedIssueUrls: ['url4'],
294
+ },
295
+ ],
296
+ cacheUsed: true,
297
+ },
298
+ expectedIssueRepositoryClearProjectFieldCalls: [],
299
+ expectedIssueRepositoryUpdateTextFieldCalls: [],
300
+ expectedIssueRepositoryCreateCommentCalls: [],
301
+ },
302
+ {
303
+ name: 'should keep a dependedIssue that is absent from the incremental cache and remove only the closed one',
304
+ input: {
305
+ project: basicProject,
306
+ issues: [
307
+ basicIssueOne,
308
+ {
309
+ ...basicIssueThree,
310
+ dependedIssueUrls: ['url1', 'url4'],
311
+ },
312
+ ],
313
+ cacheUsed: true,
314
+ },
315
+ expectedIssueRepositoryClearProjectFieldCalls: [],
316
+ expectedIssueRepositoryUpdateTextFieldCalls: [
317
+ [
318
+ basicProject,
319
+ 'fieldId',
320
+ { ...basicIssueThree, dependedIssueUrls: ['url1', 'url4'] },
321
+ 'url4',
322
+ ],
323
+ ],
324
+ expectedIssueRepositoryCreateCommentCalls: [
325
+ [
326
+ { ...basicIssueThree, dependedIssueUrls: ['url1', 'url4'] },
327
+ 'Some depended issues are already closed, removed from dependency field:\n- url1',
328
+ ],
329
+ ],
330
+ },
331
+ {
332
+ name: 'should not call clearProjectField and createComment for a circular dependency when the issue list came from the incremental cache',
333
+ input: {
334
+ project: basicProject,
335
+ issues: [
336
+ {
337
+ ...basicIssueTwo,
338
+ dependedIssueUrls: ['url3'],
339
+ },
340
+ {
341
+ ...basicIssueThree,
342
+ dependedIssueUrls: ['url2'],
343
+ },
344
+ ],
345
+ cacheUsed: true,
346
+ },
268
347
  expectedIssueRepositoryClearProjectFieldCalls: [],
269
348
  expectedIssueRepositoryUpdateTextFieldCalls: [],
270
349
  expectedIssueRepositoryCreateCommentCalls: [],
@@ -17,38 +17,17 @@ export class ClearDependedIssueURLUseCase {
17
17
  }): Promise<void> => {
18
18
  const dependedIssueUrlSeparatedByComma =
19
19
  input.project.dependedIssueUrlSeparatedByComma;
20
- if (!dependedIssueUrlSeparatedByComma || input.cacheUsed) {
20
+ if (!dependedIssueUrlSeparatedByComma) {
21
21
  return;
22
22
  }
23
+ const absentDependedIssueIsResolvable = !input.cacheUsed;
23
24
  for (const issue of input.issues) {
24
25
  if (issue.dependedIssueUrls.length <= 0 || issue.isClosed) {
25
26
  continue;
26
27
  }
27
- const circularDependedIssueUrls = issue.dependedIssueUrls.filter(
28
- (dependedIssueUrl) => {
29
- // get all depended issues circularly
30
- const circularDependedIssues = new Set<string>();
31
- const stack = [dependedIssueUrl];
32
- while (stack.length > 0) {
33
- const url = stack.pop();
34
- if (!url) {
35
- throw new Error('url is undefined');
36
- }
37
- if (circularDependedIssues.has(url)) {
38
- continue;
39
- }
40
- circularDependedIssues.add(url);
41
- const dependedIssue = input.issues.find(
42
- (issue) => issue.url === url,
43
- );
44
- if (!dependedIssue) {
45
- continue;
46
- }
47
- stack.push(...dependedIssue.dependedIssueUrls);
48
- }
49
- return circularDependedIssues.has(issue.url);
50
- },
51
- );
28
+ const circularDependedIssueUrls = absentDependedIssueIsResolvable
29
+ ? this.findCircularDependedIssueUrls(issue, input.issues)
30
+ : [];
52
31
  if (circularDependedIssueUrls.length > 0) {
53
32
  await this.issueRepository.clearProjectField(
54
33
  input.project,
@@ -62,11 +41,15 @@ ${circularDependedIssueUrls.map((url) => `- ${url}`).join('\n')}`,
62
41
  );
63
42
  continue;
64
43
  }
65
- const notFoundDependedIssueUrls = issue.dependedIssueUrls.filter(
66
- (dependedIssueUrl) =>
67
- !input.issues.some((depIssue) => depIssue.url === dependedIssueUrl),
68
- );
69
- const remainingDependedIssueUrls = issue.dependedIssueUrls.filter(
44
+ const notFoundDependedIssueUrls = absentDependedIssueIsResolvable
45
+ ? issue.dependedIssueUrls.filter(
46
+ (dependedIssueUrl) =>
47
+ !input.issues.some(
48
+ (depIssue) => depIssue.url === dependedIssueUrl,
49
+ ),
50
+ )
51
+ : [];
52
+ const openDependedIssueUrls = issue.dependedIssueUrls.filter(
70
53
  (dependedIssueUrl) =>
71
54
  input.issues.some(
72
55
  (depIssue) =>
@@ -86,6 +69,12 @@ ${circularDependedIssueUrls.map((url) => `- ${url}`).join('\n')}`,
86
69
  ) {
87
70
  continue;
88
71
  }
72
+ const remainingDependedIssueUrls = absentDependedIssueIsResolvable
73
+ ? openDependedIssueUrls
74
+ : issue.dependedIssueUrls.filter(
75
+ (dependedIssueUrl) =>
76
+ !closedDependedIssueUrls.includes(dependedIssueUrl),
77
+ );
89
78
  if (remainingDependedIssueUrls.length === 0) {
90
79
  await this.issueRepository.clearProjectField(
91
80
  input.project,
@@ -119,4 +108,29 @@ ${notFoundDependedIssueUrls.map((url) => `- ${url}`).join('\n')}`,
119
108
  }
120
109
  }
121
110
  };
111
+
112
+ private findCircularDependedIssueUrls = (
113
+ issue: Issue,
114
+ issues: Issue[],
115
+ ): string[] =>
116
+ issue.dependedIssueUrls.filter((dependedIssueUrl) => {
117
+ const reachableIssueUrls = new Set<string>();
118
+ const stack = [dependedIssueUrl];
119
+ while (stack.length > 0) {
120
+ const url = stack.pop();
121
+ if (!url) {
122
+ throw new Error('url is undefined');
123
+ }
124
+ if (reachableIssueUrls.has(url)) {
125
+ continue;
126
+ }
127
+ reachableIssueUrls.add(url);
128
+ const dependedIssue = issues.find((candidate) => candidate.url === url);
129
+ if (!dependedIssue) {
130
+ continue;
131
+ }
132
+ stack.push(...dependedIssue.dependedIssueUrls);
133
+ }
134
+ return reachableIssueUrls.has(issue.url);
135
+ });
122
136
  }
@@ -9,5 +9,6 @@ export declare class ClearDependedIssueURLUseCase {
9
9
  issues: Issue[];
10
10
  cacheUsed: boolean;
11
11
  }) => Promise<void>;
12
+ private findCircularDependedIssueUrls;
12
13
  }
13
14
  //# sourceMappingURL=ClearDependedIssueURLUseCase.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ClearDependedIssueURLUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ClearDependedIssueURLUseCase.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;AAE9C,qBAAa,4BAA4B;IAErC,QAAQ,CAAC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,mBAAmB,GAAG,eAAe,GAAG,wBAAwB,CACjE;gBAHQ,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,mBAAmB,GAAG,eAAe,GAAG,wBAAwB,CACjE;IAGH,GAAG,GAAU,OAAO;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;KACpB,KAAG,OAAO,CAAC,IAAI,CAAC,CAwGf;CACH"}
1
+ {"version":3,"file":"ClearDependedIssueURLUseCase.d.ts","sourceRoot":"","sources":["../../../src/domain/usecases/ClearDependedIssueURLUseCase.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;AAE9C,qBAAa,4BAA4B;IAErC,QAAQ,CAAC,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,mBAAmB,GAAG,eAAe,GAAG,wBAAwB,CACjE;gBAHQ,eAAe,EAAE,IAAI,CAC5B,eAAe,EACf,mBAAmB,GAAG,eAAe,GAAG,wBAAwB,CACjE;IAGH,GAAG,GAAU,OAAO;QAClB,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;KACpB,KAAG,OAAO,CAAC,IAAI,CAAC,CA6Ff;IAEF,OAAO,CAAC,6BAA6B,CAuBhC;CACN"}