github-issue-tower-defence-management 1.108.0 → 1.109.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +12 -0
  3. package/bin/adapter/entry-points/cli/index.js +4 -4
  4. package/bin/adapter/entry-points/cli/index.js.map +1 -1
  5. package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js +1 -1
  6. package/bin/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.js.map +1 -1
  7. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js +1 -1
  8. package/bin/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.js.map +1 -1
  9. package/bin/adapter/repositories/GraphqlProjectRepository.js +41 -2
  10. package/bin/adapter/repositories/GraphqlProjectRepository.js.map +1 -1
  11. package/bin/adapter/repositories/LocalStorageCacheRepository.js +5 -1
  12. package/bin/adapter/repositories/LocalStorageCacheRepository.js.map +1 -1
  13. package/bin/adapter/repositories/LocalStorageRepository.js +3 -0
  14. package/bin/adapter/repositories/LocalStorageRepository.js.map +1 -1
  15. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js +1 -1
  16. package/bin/adapter/repositories/issue/GraphqlProjectItemRepository.js.map +1 -1
  17. package/package.json +1 -1
  18. package/src/adapter/entry-points/cli/index.ts +4 -0
  19. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.test.ts +6 -1
  20. package/src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts +4 -1
  21. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.test.ts +6 -1
  22. package/src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts +1 -0
  23. package/src/adapter/repositories/GraphqlProjectRepository.diskCache.test.ts +254 -0
  24. package/src/adapter/repositories/GraphqlProjectRepository.ts +66 -0
  25. package/src/adapter/repositories/LocalStorageCacheRepository.test.ts +35 -2
  26. package/src/adapter/repositories/LocalStorageCacheRepository.ts +5 -4
  27. package/src/adapter/repositories/LocalStorageRepository.test.ts +12 -0
  28. package/src/adapter/repositories/LocalStorageRepository.ts +3 -0
  29. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.test.ts +3 -6
  30. package/src/adapter/repositories/issue/GraphqlProjectItemRepository.ts +1 -1
  31. package/types/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.d.ts.map +1 -1
  32. package/types/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.d.ts.map +1 -1
  33. package/types/adapter/repositories/GraphqlProjectRepository.d.ts +6 -0
  34. package/types/adapter/repositories/GraphqlProjectRepository.d.ts.map +1 -1
  35. package/types/adapter/repositories/LocalStorageCacheRepository.d.ts.map +1 -1
  36. package/types/adapter/repositories/LocalStorageRepository.d.ts +1 -0
  37. package/types/adapter/repositories/LocalStorageRepository.d.ts.map +1 -1
  38. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts +1 -1
  39. package/types/adapter/repositories/issue/GraphqlProjectItemRepository.d.ts.map +1 -1
@@ -53,7 +53,10 @@ export class GetStoryObjectMapUseCaseHandler {
53
53
  typeof BaseGitHubRepository
54
54
  > = [localStorageRepository, input.credentials.bot.github.token];
55
55
  const projectRepository = {
56
- ...new GraphqlProjectRepository(...githubRepositoryParams),
56
+ ...new GraphqlProjectRepository(
57
+ ...githubRepositoryParams,
58
+ localStorageCacheRepository,
59
+ ),
57
60
  };
58
61
  const apiV3IssueRepository = new ApiV3IssueRepository(
59
62
  ...githubRepositoryParams,
@@ -202,7 +202,6 @@ describe('HandleScheduledEventUseCaseHandler', () => {
202
202
  await handler.handle('config.yml', false);
203
203
 
204
204
  for (const MockedClass of [
205
- MockedGraphqlProjectRepository,
206
205
  MockedApiV3IssueRepository,
207
206
  MockedRestIssueRepository,
208
207
  MockedGraphqlProjectItemRepository,
@@ -210,6 +209,12 @@ describe('HandleScheduledEventUseCaseHandler', () => {
210
209
  expect(MockedClass).toHaveBeenCalledWith(expect.anything(), 'test-token');
211
210
  }
212
211
 
212
+ expect(MockedGraphqlProjectRepository).toHaveBeenCalledWith(
213
+ expect.anything(),
214
+ 'test-token',
215
+ expect.anything(),
216
+ );
217
+
213
218
  expect(MockedApiV3CheerioRestIssueRepository).toHaveBeenCalledWith(
214
219
  expect.anything(),
215
220
  expect.anything(),
@@ -246,6 +246,7 @@ export class HandleScheduledEventUseCaseHandler {
246
246
  > = [localStorageRepository, input.credentials.bot.github.token];
247
247
  const projectRepository = new GraphqlProjectRepository(
248
248
  ...githubRepositoryParams,
249
+ localStorageCacheRepository,
249
250
  );
250
251
  const apiV3IssueRepository = new ApiV3IssueRepository(
251
252
  ...githubRepositoryParams,
@@ -0,0 +1,254 @@
1
+ const mockPost = jest.fn();
2
+
3
+ jest.mock('ky', () => ({
4
+ default: {
5
+ post: mockPost,
6
+ get: jest.fn(),
7
+ put: jest.fn(),
8
+ patch: jest.fn(),
9
+ delete: jest.fn(),
10
+ extend: jest.fn(),
11
+ create: jest.fn(),
12
+ stop: jest.fn(),
13
+ },
14
+ __esModule: true,
15
+ }));
16
+
17
+ import { GraphqlProjectRepository } from './GraphqlProjectRepository';
18
+ import { LocalStorageCacheRepository } from './LocalStorageCacheRepository';
19
+ import { LocalStorageRepository } from './LocalStorageRepository';
20
+ import { Project } from '../../domain/entities/Project';
21
+
22
+ const mockJsonResponse = <T>(data: T) => ({
23
+ json: jest.fn().mockResolvedValue(data),
24
+ });
25
+
26
+ const projectId = 'PVT_project123';
27
+
28
+ const getProjectResponse = {
29
+ data: {
30
+ node: {
31
+ id: projectId,
32
+ databaseId: 1,
33
+ title: 'A project',
34
+ shortDescription: '',
35
+ public: true,
36
+ closed: false,
37
+ createdAt: '2024-01-01T00:00:00.000Z',
38
+ updatedAt: '2024-01-01T00:00:00.000Z',
39
+ number: 49,
40
+ url: 'https://github.com/users/owner/projects/49',
41
+ fields: {
42
+ nodes: [
43
+ {
44
+ id: 'PVTSSF_status',
45
+ databaseId: 10,
46
+ name: 'Status',
47
+ dataType: 'SINGLE_SELECT',
48
+ options: [
49
+ { id: 'st1', name: 'Todo', description: '', color: 'GRAY' },
50
+ ],
51
+ },
52
+ ],
53
+ },
54
+ },
55
+ },
56
+ };
57
+
58
+ const expectedProject: Project = {
59
+ id: projectId,
60
+ url: 'https://github.com/users/owner/projects/49',
61
+ databaseId: 1,
62
+ name: 'A project',
63
+ status: {
64
+ name: 'Status',
65
+ fieldId: 'PVTSSF_status',
66
+ statuses: [{ id: 'st1', name: 'Todo', color: 'GRAY', description: '' }],
67
+ },
68
+ nextActionDate: null,
69
+ nextActionHour: null,
70
+ story: null,
71
+ remainingEstimationMinutes: null,
72
+ dependedIssueUrlSeparatedByComma: null,
73
+ completionDate50PercentConfidence: null,
74
+ };
75
+
76
+ const fetchProjectIdResponse = {
77
+ data: {
78
+ organization: null,
79
+ user: { projectV2: { id: projectId, databaseId: 1 } },
80
+ },
81
+ };
82
+
83
+ type CacheEntry = { value: object; timestamp: Date } | null;
84
+
85
+ const buildCacheStub = (overrides?: {
86
+ getLatest?: jest.Mock;
87
+ set?: jest.Mock;
88
+ }): Pick<LocalStorageCacheRepository, 'getLatest' | 'set'> & {
89
+ getLatest: jest.Mock;
90
+ set: jest.Mock;
91
+ } => ({
92
+ getLatest: overrides?.getLatest ?? jest.fn().mockResolvedValue(null),
93
+ set: overrides?.set ?? jest.fn().mockResolvedValue(undefined),
94
+ });
95
+
96
+ describe('GraphqlProjectRepository disk cache', () => {
97
+ const localStorageRepository = new LocalStorageRepository();
98
+
99
+ beforeEach(() => {
100
+ jest.useFakeTimers();
101
+ mockPost.mockReset();
102
+ });
103
+
104
+ afterEach(() => {
105
+ jest.useRealTimers();
106
+ });
107
+
108
+ describe('getProject', () => {
109
+ it('always fetches the project fresh via GraphQL without reading or writing the disk cache', async () => {
110
+ const cache = buildCacheStub();
111
+ mockPost.mockReturnValueOnce(mockJsonResponse(getProjectResponse));
112
+ const repository = new GraphqlProjectRepository(
113
+ localStorageRepository,
114
+ 'dummy',
115
+ cache,
116
+ );
117
+
118
+ const project = await repository.getProject(projectId);
119
+
120
+ expect(project).toEqual(expectedProject);
121
+ expect(mockPost).toHaveBeenCalledTimes(1);
122
+ expect(cache.getLatest).not.toHaveBeenCalled();
123
+ expect(cache.set).not.toHaveBeenCalled();
124
+ });
125
+
126
+ it('does not serve a project from the disk cache even when one is present', async () => {
127
+ const cache = buildCacheStub({
128
+ getLatest: jest.fn().mockResolvedValue({
129
+ value: expectedProject,
130
+ timestamp: new Date(),
131
+ } satisfies CacheEntry),
132
+ });
133
+ mockPost.mockReturnValueOnce(mockJsonResponse(getProjectResponse));
134
+ const repository = new GraphqlProjectRepository(
135
+ localStorageRepository,
136
+ 'dummy',
137
+ cache,
138
+ );
139
+
140
+ const project = await repository.getProject(projectId);
141
+
142
+ expect(project).toEqual(expectedProject);
143
+ expect(mockPost).toHaveBeenCalledTimes(1);
144
+ expect(cache.set).not.toHaveBeenCalled();
145
+ });
146
+ });
147
+
148
+ describe('fetchProjectId', () => {
149
+ it('fetches via GraphQL and writes the cache on a cache miss', async () => {
150
+ const cache = buildCacheStub();
151
+ mockPost.mockReturnValueOnce(mockJsonResponse(fetchProjectIdResponse));
152
+ const repository = new GraphqlProjectRepository(
153
+ localStorageRepository,
154
+ 'dummy',
155
+ cache,
156
+ );
157
+
158
+ const result = await repository.fetchProjectId('owner', 49);
159
+
160
+ expect(result).toBe(projectId);
161
+ expect(mockPost).toHaveBeenCalledTimes(1);
162
+ expect(cache.set).toHaveBeenCalledWith('projectId-owner:49', {
163
+ projectId,
164
+ });
165
+ });
166
+
167
+ it('returns the disk-cached project ID without any GraphQL call', async () => {
168
+ const cache = buildCacheStub({
169
+ getLatest: jest.fn().mockResolvedValue({
170
+ value: { projectId },
171
+ timestamp: new Date(),
172
+ } satisfies CacheEntry),
173
+ });
174
+ const repository = new GraphqlProjectRepository(
175
+ localStorageRepository,
176
+ 'dummy',
177
+ cache,
178
+ );
179
+
180
+ const result = await repository.fetchProjectId('owner', 49);
181
+
182
+ expect(result).toBe(projectId);
183
+ expect(mockPost).not.toHaveBeenCalled();
184
+ expect(cache.set).not.toHaveBeenCalled();
185
+ });
186
+
187
+ it('does not apply a TTL to the project ID cache because the mapping is static', async () => {
188
+ const cache = buildCacheStub({
189
+ getLatest: jest.fn().mockResolvedValue({
190
+ value: { projectId },
191
+ timestamp: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000),
192
+ } satisfies CacheEntry),
193
+ });
194
+ const repository = new GraphqlProjectRepository(
195
+ localStorageRepository,
196
+ 'dummy',
197
+ cache,
198
+ );
199
+
200
+ const result = await repository.fetchProjectId('owner', 49);
201
+
202
+ expect(result).toBe(projectId);
203
+ expect(mockPost).not.toHaveBeenCalled();
204
+ });
205
+
206
+ it('falls back to a live GraphQL fetch when the project ID cache read throws', async () => {
207
+ const cache = buildCacheStub({
208
+ getLatest: jest.fn().mockRejectedValue(new Error('corrupted file')),
209
+ });
210
+ mockPost.mockReturnValueOnce(mockJsonResponse(fetchProjectIdResponse));
211
+ const repository = new GraphqlProjectRepository(
212
+ localStorageRepository,
213
+ 'dummy',
214
+ cache,
215
+ );
216
+
217
+ const result = await repository.fetchProjectId('owner', 49);
218
+
219
+ expect(result).toBe(projectId);
220
+ expect(mockPost).toHaveBeenCalledTimes(1);
221
+ });
222
+
223
+ it('serves the second call from the in-memory L1 cache without a disk read', async () => {
224
+ const cache = buildCacheStub();
225
+ mockPost.mockReturnValueOnce(mockJsonResponse(fetchProjectIdResponse));
226
+ const repository = new GraphqlProjectRepository(
227
+ localStorageRepository,
228
+ 'dummy',
229
+ cache,
230
+ );
231
+
232
+ await repository.fetchProjectId('owner', 49);
233
+ await repository.fetchProjectId('owner', 49);
234
+
235
+ expect(mockPost).toHaveBeenCalledTimes(1);
236
+ expect(cache.getLatest).toHaveBeenCalledTimes(1);
237
+ });
238
+ });
239
+
240
+ describe('without a cache repository', () => {
241
+ it('still works using the in-memory cache only', async () => {
242
+ mockPost.mockReturnValueOnce(mockJsonResponse(fetchProjectIdResponse));
243
+ const repository = new GraphqlProjectRepository(
244
+ localStorageRepository,
245
+ 'dummy',
246
+ );
247
+
248
+ const result = await repository.fetchProjectId('owner', 49);
249
+
250
+ expect(result).toBe(projectId);
251
+ expect(mockPost).toHaveBeenCalledTimes(1);
252
+ });
253
+ });
254
+ });
@@ -1,11 +1,15 @@
1
1
  import ky from 'ky';
2
2
  import { BaseGitHubRepository } from './BaseGitHubRepository';
3
+ import { LocalStorageCacheRepository } from './LocalStorageCacheRepository';
4
+ import { LocalStorageRepository } from './LocalStorageRepository';
3
5
  import { ProjectRepository } from '../../domain/usecases/adapter-interfaces/ProjectRepository';
4
6
  import { FieldOption, Project } from '../../domain/entities/Project';
5
7
  import { normalizeFieldName } from './utils';
6
8
 
7
9
  const ONE_HOUR_MS = 60 * 60 * 1000;
8
10
 
11
+ const PROJECT_ID_DISK_CACHE_KEY_PREFIX = 'projectId';
12
+
9
13
  export const convertToFieldOptionColor = (
10
14
  color: string,
11
15
  ): FieldOption['color'] => {
@@ -38,6 +42,62 @@ export class GraphqlProjectRepository
38
42
  {
39
43
  private readonly projectIdCache = new Map<string, string>();
40
44
  private readonly fetchProjectIdFailedAt = new Map<string, number>();
45
+ private readonly projectCache?: Pick<
46
+ LocalStorageCacheRepository,
47
+ 'getLatest' | 'set'
48
+ >;
49
+
50
+ constructor(
51
+ localStorageRepository: LocalStorageRepository,
52
+ ghToken: string = process.env.GH_TOKEN || 'dummy',
53
+ projectCache?: Pick<LocalStorageCacheRepository, 'getLatest' | 'set'>,
54
+ ) {
55
+ super(localStorageRepository, ghToken);
56
+ this.projectCache = projectCache;
57
+ }
58
+
59
+ private readProjectIdFromDiskCache = async (
60
+ cacheKey: string,
61
+ ): Promise<string | null> => {
62
+ if (!this.projectCache) {
63
+ return null;
64
+ }
65
+ let cache: { value: object; timestamp: Date } | null;
66
+ try {
67
+ cache = await this.projectCache.getLatest(
68
+ `${PROJECT_ID_DISK_CACHE_KEY_PREFIX}-${cacheKey}`,
69
+ );
70
+ } catch (error) {
71
+ return null;
72
+ }
73
+ if (!cache) {
74
+ return null;
75
+ }
76
+ if (
77
+ 'projectId' in cache.value &&
78
+ typeof cache.value.projectId === 'string'
79
+ ) {
80
+ return cache.value.projectId;
81
+ }
82
+ return null;
83
+ };
84
+
85
+ private writeProjectIdToDiskCache = async (
86
+ cacheKey: string,
87
+ projectId: string,
88
+ ): Promise<void> => {
89
+ if (!this.projectCache) {
90
+ return;
91
+ }
92
+ try {
93
+ await this.projectCache.set(
94
+ `${PROJECT_ID_DISK_CACHE_KEY_PREFIX}-${cacheKey}`,
95
+ { projectId },
96
+ );
97
+ } catch (error) {
98
+ return;
99
+ }
100
+ };
41
101
 
42
102
  extractProjectFromUrl = (
43
103
  projectUrl: string,
@@ -60,6 +120,11 @@ export class GraphqlProjectRepository
60
120
  if (cached) {
61
121
  return cached;
62
122
  }
123
+ const diskCached = await this.readProjectIdFromDiskCache(cacheKey);
124
+ if (diskCached) {
125
+ this.projectIdCache.set(cacheKey, diskCached);
126
+ return diskCached;
127
+ }
63
128
  const failedAt = this.fetchProjectIdFailedAt.get(cacheKey);
64
129
  if (failedAt !== undefined && Date.now() - failedAt < ONE_HOUR_MS) {
65
130
  throw new Error(
@@ -139,6 +204,7 @@ export class GraphqlProjectRepository
139
204
  );
140
205
  }
141
206
  this.projectIdCache.set(cacheKey, projectId);
207
+ await this.writeProjectIdToDiskCache(cacheKey, projectId);
142
208
  return projectId;
143
209
  };
144
210
  findProjectIdByUrl = async (
@@ -15,6 +15,7 @@ describe('LocalStorageCacheRepository', () => {
15
15
  listFiles: jest.fn(),
16
16
  read: jest.fn(),
17
17
  write: jest.fn(),
18
+ rename: jest.fn(),
18
19
  mkdir: jest.fn(),
19
20
  remove: jest.fn(),
20
21
  };
@@ -69,6 +70,19 @@ describe('LocalStorageCacheRepository', () => {
69
70
  timestamp: new Date('2024-01-01T00:00:00.000Z'),
70
71
  },
71
72
  },
73
+ {
74
+ name: 'ignores in-progress temp files and reads the latest committed file',
75
+ key: 'test-key',
76
+ files: [
77
+ '2024-01-01T00:00:00.000Z.json',
78
+ '2024-01-01T00:00:01.000Z.json.1234.abc.tmp',
79
+ ],
80
+ fileContent: '{"test": "value"}',
81
+ expected: {
82
+ value: { test: 'value' },
83
+ timestamp: new Date('2024-01-01T00:00:00.000Z'),
84
+ },
85
+ },
72
86
  ];
73
87
 
74
88
  test.each(testCases)(
@@ -137,11 +151,30 @@ describe('LocalStorageCacheRepository', () => {
137
151
  expect(localStorageRepository.mkdir).toHaveBeenCalledWith(
138
152
  expectedDirPath,
139
153
  );
140
- expect(localStorageRepository.write).toHaveBeenCalledWith(
154
+ const writeArgs = localStorageRepository.write.mock.calls[0];
155
+ const writtenPath = writeArgs[0];
156
+ expect(writtenPath.startsWith(`${expectedFilePath}.`)).toBe(true);
157
+ expect(writtenPath.endsWith('.tmp')).toBe(true);
158
+ expect(writeArgs[1]).toBe(expectedContent);
159
+ expect(localStorageRepository.rename).toHaveBeenCalledWith(
160
+ writtenPath,
141
161
  expectedFilePath,
142
- expectedContent,
143
162
  );
144
163
  },
145
164
  );
165
+
166
+ test('writes to the temp file before renaming it into place', async () => {
167
+ const callOrder: string[] = [];
168
+ localStorageRepository.write.mockImplementation(() => {
169
+ callOrder.push('write');
170
+ });
171
+ localStorageRepository.rename.mockImplementation(() => {
172
+ callOrder.push('rename');
173
+ });
174
+
175
+ await repository.set('ordering-key', { ordered: true });
176
+
177
+ expect(callOrder).toEqual(['write', 'rename']);
178
+ });
146
179
  });
147
180
  });
@@ -15,6 +15,7 @@ export class LocalStorageCacheRepository {
15
15
  const dirPath = `${this.cachePath}/${key}`;
16
16
  const latestFile = this.localStorageRepository
17
17
  .listFiles(dirPath)
18
+ .filter((fileName) => !fileName.endsWith('.tmp'))
18
19
  .sort((a, b) => a.localeCompare(b))
19
20
  .reverse()[0];
20
21
  if (!latestFile) {
@@ -45,9 +46,9 @@ export class LocalStorageCacheRepository {
45
46
  const dirPath = `${this.cachePath}/${key}`;
46
47
  this.localStorageRepository.mkdir(dirPath);
47
48
  const timestamp = new Date().toISOString();
48
- this.localStorageRepository.write(
49
- `${dirPath}/${timestamp}.json`,
50
- JSON.stringify(value),
51
- );
49
+ const finalPath = `${dirPath}/${timestamp}.json`;
50
+ const tmpPath = `${finalPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
51
+ this.localStorageRepository.write(tmpPath, JSON.stringify(value));
52
+ this.localStorageRepository.rename(tmpPath, finalPath);
52
53
  };
53
54
  }
@@ -15,6 +15,7 @@ describe('LocalStorageRepository', () => {
15
15
  let mockReaddirSync: jest.SpyInstance;
16
16
  let mockMkdirSync: jest.SpyInstance;
17
17
  let mockExistsSync: jest.SpyInstance;
18
+ let mockRenameSync: jest.SpyInstance;
18
19
 
19
20
  beforeEach(() => {
20
21
  repository = new LocalStorageRepository();
@@ -23,6 +24,7 @@ describe('LocalStorageRepository', () => {
23
24
  mockReaddirSync = jest.spyOn(fs, 'readdirSync').mockImplementation();
24
25
  mockMkdirSync = jest.spyOn(fs, 'mkdirSync').mockImplementation();
25
26
  mockExistsSync = jest.spyOn(fs, 'existsSync').mockImplementation();
27
+ mockRenameSync = jest.spyOn(fs, 'renameSync').mockImplementation();
26
28
  });
27
29
 
28
30
  afterEach(() => {
@@ -59,6 +61,16 @@ describe('LocalStorageRepository', () => {
59
61
  });
60
62
  });
61
63
 
64
+ describe('rename', () => {
65
+ test('renames a file from the old path to the new path', () => {
66
+ repository.rename('/path/to/file.txt.tmp', '/path/to/file.txt');
67
+ expect(mockRenameSync).toHaveBeenCalledWith(
68
+ '/path/to/file.txt.tmp',
69
+ '/path/to/file.txt',
70
+ );
71
+ });
72
+ });
73
+
62
74
  describe('read', () => {
63
75
  const testCases: Array<{
64
76
  name: string;
@@ -9,6 +9,9 @@ export class LocalStorageRepository {
9
9
  read = (path: string): string | null => {
10
10
  return fs.readFileSync(path, 'utf8');
11
11
  };
12
+ rename = (oldPath: string, newPath: string) => {
13
+ fs.renameSync(oldPath, newPath);
14
+ };
12
15
  listFiles = (dirPath: string): string[] => {
13
16
  if (!fs.existsSync(dirPath)) {
14
17
  return [];
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"GetStoryObjectMapUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,qBAAa,+BAA+B;IAC1C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,EACjB,oBAAoB,MAAM,KACzB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,cAAc,CAAC;KAChC,CAAC,CA2DA;CACH"}
1
+ {"version":3,"file":"GetStoryObjectMapUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/GetStoryObjectMapUseCaseHandler.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAG3D,OAAO,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAEzE,qBAAa,+BAA+B;IAC1C,MAAM,GACJ,gBAAgB,MAAM,EACtB,UAAU,OAAO,EACjB,oBAAoB,MAAM,KACzB,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,MAAM,EAAE,KAAK,EAAE,CAAC;QAChB,SAAS,EAAE,OAAO,CAAC;QACnB,cAAc,EAAE,cAAc,CAAC;KAChC,CAAC,CA8DA;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAgC3D,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,CAwcP;CACH"}
1
+ {"version":3,"file":"HandleScheduledEventUseCaseHandler.d.ts","sourceRoot":"","sources":["../../../../src/adapter/entry-points/handlers/HandleScheduledEventUseCaseHandler.ts"],"names":[],"mappings":"AA8BA,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAgC3D,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,CAycP;CACH"}
@@ -1,10 +1,16 @@
1
1
  import { BaseGitHubRepository } from './BaseGitHubRepository';
2
+ import { LocalStorageCacheRepository } from './LocalStorageCacheRepository';
3
+ import { LocalStorageRepository } from './LocalStorageRepository';
2
4
  import { ProjectRepository } from '../../domain/usecases/adapter-interfaces/ProjectRepository';
3
5
  import { FieldOption, Project } from '../../domain/entities/Project';
4
6
  export declare const convertToFieldOptionColor: (color: string) => FieldOption["color"];
5
7
  export declare class GraphqlProjectRepository extends BaseGitHubRepository implements Pick<ProjectRepository, 'getProject' | 'findProjectIdByUrl' | 'getByUrl' | 'updateStoryList' | 'updateStatusList'> {
6
8
  private readonly projectIdCache;
7
9
  private readonly fetchProjectIdFailedAt;
10
+ private readonly projectCache?;
11
+ constructor(localStorageRepository: LocalStorageRepository, ghToken?: string, projectCache?: Pick<LocalStorageCacheRepository, 'getLatest' | 'set'>);
12
+ private readProjectIdFromDiskCache;
13
+ private writeProjectIdToDiskCache;
8
14
  extractProjectFromUrl: (projectUrl: string) => {
9
15
  owner: string;
10
16
  projectNumber: number;
@@ -1 +1 @@
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,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAKrE,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;IAEpE,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,CAsFhB;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"}
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"}
@@ -1 +1 @@
1
- {"version":3,"file":"LocalStorageCacheRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalStorageCacheRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,qBAAa,2BAA2B;IAEpC,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB;IACvD,QAAQ,CAAC,SAAS;gBADT,sBAAsB,EAAE,sBAAsB,EAC9C,SAAS,SAAgB;IAGpC,SAAS,GACP,KAAK,MAAM,KACV,OAAO,CAAC;QACT,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,IAAI,CAAC;KACjB,GAAG,IAAI,CAAC,CA6BP;IACF,GAAG,GAAU,CAAC,EAAE,KAAK,MAAM,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC,CAQnD;CACH"}
1
+ {"version":3,"file":"LocalStorageCacheRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalStorageCacheRepository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,qBAAa,2BAA2B;IAEpC,QAAQ,CAAC,sBAAsB,EAAE,sBAAsB;IACvD,QAAQ,CAAC,SAAS;gBADT,sBAAsB,EAAE,sBAAsB,EAC9C,SAAS,SAAgB;IAGpC,SAAS,GACP,KAAK,MAAM,KACV,OAAO,CAAC;QACT,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,IAAI,CAAC;KACjB,GAAG,IAAI,CAAC,CA8BP;IACF,GAAG,GAAU,CAAC,EAAE,KAAK,MAAM,EAAE,OAAO,CAAC,KAAG,OAAO,CAAC,IAAI,CAAC,CAQnD;CACH"}
@@ -1,6 +1,7 @@
1
1
  export declare class LocalStorageRepository {
2
2
  write: (path: string, value: string) => void;
3
3
  read: (path: string) => string | null;
4
+ rename: (oldPath: string, newPath: string) => void;
4
5
  listFiles: (dirPath: string) => string[];
5
6
  mkdir: (dirPath: string) => void;
6
7
  remove: (path: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"LocalStorageRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalStorageRepository.ts"],"names":[],"mappings":"AAEA,qBAAa,sBAAsB;IACjC,KAAK,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,UAIlC;IACF,IAAI,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,IAAI,CAElC;IACF,SAAS,GAAI,SAAS,MAAM,KAAG,MAAM,EAAE,CAKrC;IACF,KAAK,GAAI,SAAS,MAAM,UAEtB;IACF,MAAM,GAAI,MAAM,MAAM,UAKpB;CACH"}
1
+ {"version":3,"file":"LocalStorageRepository.d.ts","sourceRoot":"","sources":["../../../src/adapter/repositories/LocalStorageRepository.ts"],"names":[],"mappings":"AAEA,qBAAa,sBAAsB;IACjC,KAAK,GAAI,MAAM,MAAM,EAAE,OAAO,MAAM,UAIlC;IACF,IAAI,GAAI,MAAM,MAAM,KAAG,MAAM,GAAG,IAAI,CAElC;IACF,MAAM,GAAI,SAAS,MAAM,EAAE,SAAS,MAAM,UAExC;IACF,SAAS,GAAI,SAAS,MAAM,KAAG,MAAM,EAAE,CAKrC;IACF,KAAK,GAAI,SAAS,MAAM,UAEtB;IACF,MAAM,GAAI,MAAM,MAAM,UAKpB;CACH"}
@@ -19,7 +19,7 @@ export type ProjectItem = {
19
19
  value: string | null;
20
20
  }[];
21
21
  };
22
- export declare const PAGINATION_DELAY_MS = 500;
22
+ export declare const PAGINATION_DELAY_MS = 5000;
23
23
  export declare const FETCH_PROJECT_ITEMS_INITIAL_PAGE_SIZE = 100;
24
24
  export declare const FETCH_PROJECT_ITEMS_GRAPHQL_ERROR_PAYLOAD_MAX_LENGTH = 4000;
25
25
  export declare const RATE_LIMIT_MAX_RETRIES = 6;
@@ -1 +1 @@
1
- {"version":3,"file":"GraphqlProjectItemRepository.d.ts","sourceRoot":"","sources":["../../../../src/adapter/repositories/issue/GraphqlProjectItemRepository.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,kCAAkC,CAAC;AAC3D,OAAO,EAAE,KAAK,EAAE,MAAM,gCAAgC,CAAC;AACvD,MAAM,MAAM,WAAW,GAAG;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACpC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,yBAAyB,EAAE,MAAM,EAAE,CAAC;IACpC,YAAY,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;KACtB,EAAE,CAAC;CACL,CAAC;AACF,eAAO,MAAM,mBAAmB,MAAM,CAAC;AACvC,eAAO,MAAM,qCAAqC,MAAM,CAAC;AACzD,eAAO,MAAM,oDAAoD,OAAO,CAAC;AACzE,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAC9C,eAAO,MAAM,6BAA6B,QAAQ,CAAC;AACnD,eAAO,MAAM,yBAAyB,SAAS,CAAC;AAsDhD,eAAO,MAAM,sBAAsB,GAAU,CAAC,EAC5C,SAAS,MAAM,OAAO,CAAC,CAAC,CAAC,KACxB,OAAO,CAAC,CAAC,CAyBX,CAAC;AAcF,qBAAa,4BAA6B,SAAQ,oBAAoB;IACpE,WAAW,GACT,WAAW,MAAM,EACjB,OAAO,MAAM,EACb,gBAAgB,MAAM,EACtB,aAAa,MAAM,KAClB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAoE5B;IACF,iBAAiB,GAAU,WAAW,MAAM,KAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAgZnE;IACF,gCAAgC,GAC9B,UAAU,MAAM,KACf,OAAO,CACR;QACE,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB,EAAE,CACJ,CAGC;IAEF,oBAAoB,GAClB,OAAO,MAAM,EACb,gBAAgB,MAAM,EACtB,aAAa,MAAM,KAClB,OAAO,CACR;QACE,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;KACpB,EAAE,CACJ,CAqJC;IACF,qBAAqB,GACnB,UAAU,MAAM,EAChB,YAAY,MAAM,KACjB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAkR5B;IACF,iBAAiB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAQ/D;IAEF,kBAAkB,GAChB,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,QAAQ,MAAM,EACd,OACI;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,GAClB;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB;QAAE,oBAAoB,EAAE,MAAM,CAAA;KAAE,KACnC,OAAO,CAAC,IAAI,CAAC,CAgCd;IAEF,iBAAiB,GACf,WAAW,MAAM,EACjB,SAAS,MAAM,EACf,QAAQ,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CA+Bd;IACF,sBAAsB,GACpB,SAAS,OAAO,CAAC,IAAI,CAAC,EACtB,SAAS,MAAM,EACf,OAAO,KAAK,CAAC,QAAQ,CAAC,EACtB,MAAM,MAAM,KACX,OAAO,CAAC,IAAI,CAAC,CAEd;IAEF,qBAAqB,GACnB,WAAW,MAAM,EACjB,QAAQ,MAAM,KACb,OAAO,CAAC,IAAI,CAAC,CAgCd;IAEF,+BAA+B,GAC7B,UAAU,MAAM,EAChB,WAAW,MAAM,KAChB,OAAO,CAAC,IAAI,CAAC,CASd;IAEF,iBAAiB,GACf,WAAW,MAAM,EACjB,UAAU,MAAM,KACf,OAAO,CAAC,MAAM,CAAC,CAqEhB;CACH"}
1
+ {"version":3,"file":"GraphqlProjectItemRepository.d.ts","sourceRoot":"","sources":["../../../../src/adapter/repositories/issue/GraphqlProjectItemRepository.ts"],"names":[],"mappings":"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"}