@devrev/ts-adaas 1.19.10 → 1.19.11-beta.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 (43) hide show
  1. package/dist/common/control-protocol.d.ts +3 -2
  2. package/dist/common/control-protocol.d.ts.map +1 -1
  3. package/dist/common/control-protocol.js +2 -4
  4. package/dist/common/control-protocol.test.d.ts +2 -0
  5. package/dist/common/control-protocol.test.d.ts.map +1 -0
  6. package/dist/common/control-protocol.test.js +126 -0
  7. package/dist/multithreading/worker-adapter/worker-adapter.d.ts +1 -0
  8. package/dist/multithreading/worker-adapter/worker-adapter.d.ts.map +1 -1
  9. package/dist/multithreading/worker-adapter/worker-adapter.emit.test.js +94 -0
  10. package/dist/multithreading/worker-adapter/worker-adapter.helpers.d.ts +1 -0
  11. package/dist/multithreading/worker-adapter/worker-adapter.helpers.d.ts.map +1 -1
  12. package/dist/multithreading/worker-adapter/worker-adapter.helpers.js +7 -0
  13. package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.js +28 -0
  14. package/dist/multithreading/worker-adapter/worker-adapter.interfaces.d.ts +14 -0
  15. package/dist/multithreading/worker-adapter/worker-adapter.interfaces.d.ts.map +1 -0
  16. package/dist/multithreading/worker-adapter/worker-adapter.interfaces.js +2 -0
  17. package/dist/multithreading/worker-adapter/worker-adapter.js +21 -2
  18. package/dist/multithreading/worker-adapter/worker-adapter.loading.test.js +4 -3
  19. package/dist/repo/repo.d.ts +10 -0
  20. package/dist/repo/repo.d.ts.map +1 -1
  21. package/dist/repo/repo.helpers.d.ts +6 -0
  22. package/dist/repo/repo.helpers.d.ts.map +1 -0
  23. package/dist/repo/repo.helpers.js +16 -0
  24. package/dist/repo/repo.helpers.test.d.ts +2 -0
  25. package/dist/repo/repo.helpers.test.d.ts.map +1 -0
  26. package/dist/repo/repo.helpers.test.js +71 -0
  27. package/dist/repo/repo.interfaces.d.ts +2 -0
  28. package/dist/repo/repo.interfaces.d.ts.map +1 -1
  29. package/dist/repo/repo.js +21 -0
  30. package/dist/repo/repo.test.js +162 -0
  31. package/dist/types/extraction.d.ts +8 -1
  32. package/dist/types/extraction.d.ts.map +1 -1
  33. package/dist/uploader/uploader.d.ts.map +1 -1
  34. package/dist/uploader/uploader.helpers.d.ts +6 -1
  35. package/dist/uploader/uploader.helpers.d.ts.map +1 -1
  36. package/dist/uploader/uploader.helpers.js +65 -0
  37. package/dist/uploader/uploader.helpers.test.js +148 -7
  38. package/dist/uploader/uploader.interfaces.d.ts +11 -0
  39. package/dist/uploader/uploader.interfaces.d.ts.map +1 -1
  40. package/dist/uploader/uploader.interfaces.js +11 -0
  41. package/dist/uploader/uploader.js +7 -11
  42. package/dist/uploader/uploader.test.js +78 -3
  43. package/package.json +3 -3
@@ -1,10 +1,11 @@
1
1
  import { AxiosResponse } from 'axios';
2
- import { AirdropEvent, EventData, ExtractorEventType } from '../types/extraction';
2
+ import { AirdropEvent, EventData, ExtractorEventType, WorkerMetadata } from '../types/extraction';
3
3
  import { LoaderEventType } from '../types/loading';
4
4
  export interface EmitInterface {
5
5
  event: AirdropEvent;
6
6
  eventType: ExtractorEventType | LoaderEventType;
7
7
  data?: EventData;
8
+ worker_metadata?: WorkerMetadata;
8
9
  }
9
- export declare const emit: ({ event, eventType, data, }: EmitInterface) => Promise<AxiosResponse>;
10
+ export declare const emit: ({ event, eventType, data, worker_metadata, }: EmitInterface) => Promise<AxiosResponse>;
10
11
  //# sourceMappingURL=control-protocol.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"control-protocol.d.ts","sourceRoot":"","sources":["../../src/common/control-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EACL,YAAY,EACZ,SAAS,EAET,kBAAkB,EAEnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAInD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAChD,IAAI,CAAC,EAAE,SAAS,CAAC;CAClB;AAED,eAAO,MAAM,IAAI,GAAU,6BAIxB,aAAa,KAAG,OAAO,CAAC,aAAa,CAgCvC,CAAC"}
1
+ {"version":3,"file":"control-protocol.d.ts","sourceRoot":"","sources":["../../src/common/control-protocol.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,EACL,YAAY,EACZ,SAAS,EAET,kBAAkB,EAElB,cAAc,EACf,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAInD,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,SAAS,EAAE,kBAAkB,GAAG,eAAe,CAAC;IAChD,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,eAAe,CAAC,EAAE,cAAc,CAAC;CAClC;AAED,eAAO,MAAM,IAAI,GAAU,8CAKxB,aAAa,KAAG,OAAO,CAAC,aAAa,CAmCvC,CAAC"}
@@ -4,7 +4,7 @@ exports.emit = void 0;
4
4
  const axios_client_internal_1 = require("../http/axios-client-internal");
5
5
  const constants_1 = require("./constants");
6
6
  const event_type_translation_1 = require("./event-type-translation");
7
- const emit = async ({ event, eventType, data, }) => {
7
+ const emit = async ({ event, eventType, data, worker_metadata, }) => {
8
8
  // Translate outgoing event type to ensure we always send new event types
9
9
  // TODO: Remove when the old types are completely phased out
10
10
  const translatedEventType = (0, event_type_translation_1.translateOutgoingEventType)(eventType);
@@ -12,9 +12,7 @@ const emit = async ({ event, eventType, data, }) => {
12
12
  event_type: translatedEventType,
13
13
  event_context: event.payload.event_context,
14
14
  event_data: Object.assign({}, data),
15
- worker_metadata: {
16
- adaas_library_version: constants_1.LIBRARY_VERSION,
17
- },
15
+ worker_metadata: Object.assign(Object.assign({}, worker_metadata), { newest_state_date: event.payload.event_context.extract_to, oldest_state_date: event.payload.event_context.extract_from, adaas_library_version: constants_1.LIBRARY_VERSION }),
18
16
  };
19
17
  console.info('Emitting event', newEvent);
20
18
  return axios_client_internal_1.axiosClient.post(event.payload.event_context.callback_url, Object.assign({}, newEvent), {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=control-protocol.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"control-protocol.test.d.ts","sourceRoot":"","sources":["../../src/common/control-protocol.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const axios_client_internal_1 = require("../http/axios-client-internal");
4
+ const test_helpers_1 = require("../tests/test-helpers");
5
+ const test_utils_1 = require("./test-utils");
6
+ const control_protocol_1 = require("./control-protocol");
7
+ const extraction_1 = require("../types/extraction");
8
+ const loading_1 = require("../types/loading");
9
+ jest.mock('../http/axios-client-internal');
10
+ const mockedAxiosClient = jest.mocked(axios_client_internal_1.axiosClient);
11
+ describe('control-protocol.emit', () => {
12
+ beforeEach(() => {
13
+ jest.clearAllMocks();
14
+ mockedAxiosClient.post.mockResolvedValue((0, test_helpers_1.createAxiosResponse)());
15
+ });
16
+ it.each([
17
+ {
18
+ title: 'extractor events',
19
+ inputEventType: extraction_1.EventType.StartExtractingData,
20
+ outputEventType: extraction_1.ExtractorEventType.DataExtractionProgress,
21
+ },
22
+ {
23
+ title: 'loader events',
24
+ inputEventType: extraction_1.EventType.StartLoadingData,
25
+ outputEventType: loading_1.LoaderEventType.DataLoadingProgress,
26
+ },
27
+ {
28
+ title: 'unknown events',
29
+ inputEventType: extraction_1.EventType.StartExtractingData,
30
+ outputEventType: 'SOME_UNKNOWN_EVENT',
31
+ },
32
+ ])('sets state dates from event context for $title', async ({ inputEventType, outputEventType }) => {
33
+ const event = (0, test_utils_1.createMockEvent)(undefined, {
34
+ payload: {
35
+ event_type: inputEventType,
36
+ event_context: {
37
+ extract_from: '2024-01-01T00:00:00.000Z',
38
+ extract_to: '2024-06-01T00:00:00.000Z',
39
+ },
40
+ },
41
+ });
42
+ await (0, control_protocol_1.emit)({
43
+ event,
44
+ eventType: outputEventType,
45
+ });
46
+ expect(mockedAxiosClient.post).toHaveBeenCalledTimes(1);
47
+ const [, body, config] = mockedAxiosClient.post.mock.calls[0];
48
+ expect(body).toMatchObject({
49
+ event_type: outputEventType,
50
+ event_context: expect.objectContaining({
51
+ extract_from: '2024-01-01T00:00:00.000Z',
52
+ extract_to: '2024-06-01T00:00:00.000Z',
53
+ }),
54
+ worker_metadata: expect.objectContaining({
55
+ oldest_state_date: '2024-01-01T00:00:00.000Z',
56
+ newest_state_date: '2024-06-01T00:00:00.000Z',
57
+ }),
58
+ });
59
+ expect(config).toEqual(expect.objectContaining({
60
+ headers: expect.objectContaining({
61
+ 'X-DevRev-Client-Version': expect.any(String),
62
+ }),
63
+ }));
64
+ });
65
+ it.each([
66
+ {
67
+ title: 'only extract_from is set',
68
+ extractFrom: '2024-01-01T00:00:00.000Z',
69
+ extractTo: undefined,
70
+ },
71
+ {
72
+ title: 'only extract_to is set',
73
+ extractFrom: undefined,
74
+ extractTo: '2024-06-01T00:00:00.000Z',
75
+ },
76
+ {
77
+ title: 'neither extract_from nor extract_to is set',
78
+ extractFrom: undefined,
79
+ extractTo: undefined,
80
+ },
81
+ ])('handles state-date absence when $title', async ({ extractFrom, extractTo }) => {
82
+ const event = (0, test_utils_1.createMockEvent)(undefined, {
83
+ payload: {
84
+ event_type: extraction_1.EventType.StartExtractingData,
85
+ event_context: {
86
+ extract_from: extractFrom,
87
+ extract_to: extractTo,
88
+ },
89
+ },
90
+ });
91
+ await (0, control_protocol_1.emit)({
92
+ event,
93
+ eventType: extraction_1.ExtractorEventType.DataExtractionProgress,
94
+ });
95
+ const [, body] = mockedAxiosClient.post.mock.calls[0];
96
+ const workerMetadata = body.worker_metadata;
97
+ expect(workerMetadata.oldest_state_date).toBe(extractFrom);
98
+ expect(workerMetadata.newest_state_date).toBe(extractTo);
99
+ });
100
+ it('overrides caller-provided worker_metadata state dates', async () => {
101
+ const event = (0, test_utils_1.createMockEvent)(undefined, {
102
+ payload: {
103
+ event_type: extraction_1.EventType.StartExtractingData,
104
+ event_context: {
105
+ extract_from: '2024-01-01T00:00:00.000Z',
106
+ extract_to: '2024-06-01T00:00:00.000Z',
107
+ },
108
+ },
109
+ });
110
+ await (0, control_protocol_1.emit)({
111
+ event,
112
+ eventType: extraction_1.ExtractorEventType.DataExtractionProgress,
113
+ worker_metadata: {
114
+ item_type: 'tasks',
115
+ oldest_state_date: 'should-be-overwritten',
116
+ newest_state_date: 'should-be-overwritten',
117
+ },
118
+ });
119
+ const [, body] = mockedAxiosClient.post.mock.calls[0];
120
+ expect(body.worker_metadata).toEqual(expect.objectContaining({
121
+ item_type: 'tasks',
122
+ oldest_state_date: '2024-01-01T00:00:00.000Z',
123
+ newest_state_date: '2024-06-01T00:00:00.000Z',
124
+ }));
125
+ });
126
+ });
@@ -32,6 +32,7 @@ export declare class WorkerAdapter<ConnectorState> {
32
32
  private adapterState;
33
33
  private _artifacts;
34
34
  private repos;
35
+ private lastExtractedItemType?;
35
36
  private currentEventDataLength;
36
37
  private loaderReports;
37
38
  private _processedFiles;
@@ -1 +1 @@
1
- {"version":3,"file":"worker-adapter.d.ts","sourceRoot":"","sources":["../../../src/multithreading/worker-adapter/worker-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"worker-adapter.d.ts","sourceRoot":"","sources":["../../../src/multithreading/worker-adapter/worker-adapter.ts"],"names":[],"mappings":"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"}
@@ -43,6 +43,7 @@ function makeAdapter(eventType = types_1.EventType.StartExtractingData) {
43
43
  const adapter = new worker_adapter_1.WorkerAdapter({ event, adapterState });
44
44
  return { adapter, event, adapterState };
45
45
  }
46
+ const iso = (ms) => new Date(ms).toISOString();
46
47
  describe(`${worker_adapter_1.WorkerAdapter.name}.emit`, () => {
47
48
  let adapter;
48
49
  let mockPostMessage;
@@ -262,6 +263,99 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.emit`, () => {
262
263
  expect(emittedMessage.startsWith('E'.repeat(100))).toBe(true);
263
264
  });
264
265
  });
266
+ describe(`${worker_adapter_1.WorkerAdapter.name}.emit — worker_metadata`, () => {
267
+ let adapter;
268
+ beforeEach(() => {
269
+ jest.clearAllMocks();
270
+ ({ adapter } = makeAdapter());
271
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
272
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
273
+ });
274
+ function setupRepos(adapterInstance, lastExtractedItemType) {
275
+ adapterInstance['repos'] = [
276
+ {
277
+ itemType: 'issues',
278
+ dateRanges: {
279
+ creationDate: { oldest: 100, newest: 200 },
280
+ modifiedDate: { oldest: 150, newest: 250 },
281
+ },
282
+ },
283
+ {
284
+ itemType: 'tasks',
285
+ dateRanges: {
286
+ creationDate: { oldest: 300, newest: 400 },
287
+ modifiedDate: { oldest: 350, newest: 450 },
288
+ },
289
+ },
290
+ ];
291
+ adapterInstance['lastExtractedItemType'] = lastExtractedItemType;
292
+ }
293
+ it('should emit flat worker_metadata for the latest extracted item type only', async () => {
294
+ setupRepos(adapter, 'tasks');
295
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionProgress);
296
+ const { emit: mockEmit } = require('../../common/control-protocol');
297
+ expect(mockEmit.mock.calls[0][0].worker_metadata).toEqual({
298
+ item_type: 'tasks',
299
+ oldest_created_date: iso(300),
300
+ newest_created_date: iso(400),
301
+ oldest_modified_date: iso(350),
302
+ newest_modified_date: iso(450),
303
+ });
304
+ });
305
+ it('should omit unset RFC3339 bounds from worker_metadata', async () => {
306
+ adapter['repos'] = [
307
+ {
308
+ itemType: 'tasks',
309
+ dateRanges: {
310
+ creationDate: {},
311
+ modifiedDate: {},
312
+ },
313
+ },
314
+ ];
315
+ adapter['lastExtractedItemType'] = 'tasks';
316
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionProgress);
317
+ const { emit: mockEmit } = require('../../common/control-protocol');
318
+ expect(mockEmit.mock.calls[0][0].worker_metadata).toEqual({
319
+ item_type: 'tasks',
320
+ });
321
+ });
322
+ it('should include Unix-epoch bounds rather than treating them as unset', async () => {
323
+ adapter['repos'] = [
324
+ {
325
+ itemType: 'tasks',
326
+ dateRanges: {
327
+ creationDate: { oldest: 0, newest: 0 },
328
+ modifiedDate: {},
329
+ },
330
+ },
331
+ ];
332
+ adapter['lastExtractedItemType'] = 'tasks';
333
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionProgress);
334
+ const { emit: mockEmit } = require('../../common/control-protocol');
335
+ expect(mockEmit.mock.calls[0][0].worker_metadata).toEqual({
336
+ item_type: 'tasks',
337
+ oldest_created_date: iso(0),
338
+ newest_created_date: iso(0),
339
+ });
340
+ });
341
+ it('should send empty worker_metadata when no item type has been extracted', async () => {
342
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionProgress);
343
+ const { emit: mockEmit } = require('../../common/control-protocol');
344
+ expect(mockEmit.mock.calls[0][0].worker_metadata).toEqual({});
345
+ });
346
+ it('should send empty worker_metadata for loader events', async () => {
347
+ setupRepos(adapter, 'tasks');
348
+ await adapter.emit(types_1.LoaderEventType.DataLoadingProgress);
349
+ const { emit: mockEmit } = require('../../common/control-protocol');
350
+ expect(mockEmit.mock.calls[0][0].worker_metadata).toEqual({});
351
+ });
352
+ it('should send empty worker_metadata for non-progress extraction events', async () => {
353
+ setupRepos(adapter, 'tasks');
354
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionError);
355
+ const { emit: mockEmit } = require('../../common/control-protocol');
356
+ expect(mockEmit.mock.calls[0][0].worker_metadata).toEqual({});
357
+ });
358
+ });
265
359
  describe(`${worker_adapter_1.WorkerAdapter.name}.emit — ExternalSyncUnitExtractionDone legacy path`, () => {
266
360
  it('should upload ESUs via a repo and strip external_sync_units from the emitted payload', async () => {
267
361
  // Arrange
@@ -19,4 +19,5 @@ export declare function addReportToLoaderReport({ loaderReports, report, }: {
19
19
  loaderReports: LoaderReport[];
20
20
  report: LoaderReport;
21
21
  }): LoaderReport[];
22
+ export declare function toRfc3339Timestamp(ms?: number): string | undefined;
22
23
  //# sourceMappingURL=worker-adapter.helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-adapter.helpers.d.ts","sourceRoot":"","sources":["../../../src/multithreading/worker-adapter/worker-adapter.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,YAAY,EACZ,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAC7B,kBAAkB,EAClB,SAAS,GACV,EAAE;IACD,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B,GAAG,UAAU,EAAE,CA8Bf;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,aAAa,EACb,MAAM,GACP,EAAE;IACD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,MAAM,EAAE,YAAY,CAAC;CACtB,GAAG,YAAY,EAAE,CA4BjB"}
1
+ {"version":3,"file":"worker-adapter.helpers.d.ts","sourceRoot":"","sources":["../../../src/multithreading/worker-adapter/worker-adapter.helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EACV,YAAY,EACZ,eAAe,EAChB,MAAM,qBAAqB,CAAC;AAE7B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAC7B,kBAAkB,EAClB,SAAS,GACV,EAAE;IACD,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;CAC9B,GAAG,UAAU,EAAE,CA8Bf;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,aAAa,EACb,MAAM,GACP,EAAE;IACD,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,MAAM,EAAE,YAAY,CAAC;CACtB,GAAG,YAAY,EAAE,CA4BjB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAMlE"}
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getFilesToLoad = getFilesToLoad;
4
4
  exports.addReportToLoaderReport = addReportToLoaderReport;
5
+ exports.toRfc3339Timestamp = toRfc3339Timestamp;
5
6
  const loading_1 = require("../../types/loading");
6
7
  /**
7
8
  * Gets the files to load for the loader.
@@ -62,3 +63,9 @@ function addReportToLoaderReport({ loaderReports, report, }) {
62
63
  }
63
64
  return loaderReports;
64
65
  }
66
+ function toRfc3339Timestamp(ms) {
67
+ if (ms === undefined || !Number.isFinite(ms)) {
68
+ return undefined;
69
+ }
70
+ return new Date(ms).toISOString();
71
+ }
@@ -512,3 +512,31 @@ describe(worker_adapter_helpers_1.addReportToLoaderReport.name, () => {
512
512
  expect(result[0][loading_1.ActionType.CREATED]).toBe(2);
513
513
  });
514
514
  });
515
+ describe(worker_adapter_helpers_1.toRfc3339Timestamp.name, () => {
516
+ it('should convert milliseconds to an RFC3339 string', () => {
517
+ const ms = new Date('2024-06-01T00:00:00.000Z').getTime();
518
+ const result = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(ms);
519
+ expect(result).toBe('2024-06-01T00:00:00.000Z');
520
+ });
521
+ it('[edge] should convert the Unix epoch (0) instead of treating it as unset', () => {
522
+ const result = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(0);
523
+ expect(result).toBe('1970-01-01T00:00:00.000Z');
524
+ });
525
+ it('[edge] should return undefined when ms is undefined', () => {
526
+ const result = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(undefined);
527
+ expect(result).toBeUndefined();
528
+ });
529
+ it('[edge] should return undefined for NaN', () => {
530
+ const result = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(NaN);
531
+ expect(result).toBeUndefined();
532
+ });
533
+ it('[edge] should return undefined for Infinity', () => {
534
+ const result = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(Infinity);
535
+ expect(result).toBeUndefined();
536
+ });
537
+ it('[edge] should convert negative milliseconds (pre-1970 dates)', () => {
538
+ const ms = new Date('1969-12-31T00:00:00.000Z').getTime();
539
+ const result = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(ms);
540
+ expect(result).toBe('1969-12-31T00:00:00.000Z');
541
+ });
542
+ });
@@ -0,0 +1,14 @@
1
+ /**
2
+ * ProgressData represents the progress information sent with extraction events.
3
+ * Includes statistics about the last extracted item type and calculated time ranges.
4
+ */
5
+ export interface ProgressData {
6
+ item_type?: string;
7
+ oldest_created_date?: string;
8
+ newest_created_date?: string;
9
+ oldest_modified_date?: string;
10
+ newest_modified_date?: string;
11
+ oldest_state_date?: string;
12
+ newest_state_date?: string;
13
+ }
14
+ //# sourceMappingURL=worker-adapter.interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-adapter.interfaces.d.ts","sourceRoot":"","sources":["../../../src/multithreading/worker-adapter/worker-adapter.interfaces.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,YAAY;IAE3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAG9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -114,6 +114,7 @@ class WorkerAdapter {
114
114
  repo.itemType !== constants_1.SSOR_ATTACHMENT;
115
115
  return new repo_1.Repo(Object.assign(Object.assign({ event: this.event, itemType: repo.itemType }, (shouldNormalize && { normalize: repo.normalize })), { onUpload: (artifact) => {
116
116
  var _a;
117
+ this.lastExtractedItemType = repo.itemType;
117
118
  // We need to store artifacts ids in state for later use when streaming attachments
118
119
  if (repo.itemType === constants_1.AirSyncDefaultItemTypes.ATTACHMENTS) {
119
120
  (_a = this.state.toDevRev) === null || _a === void 0 ? void 0 : _a.attachmentsMetadata.artifactIds.push(artifact.id);
@@ -239,12 +240,30 @@ class WorkerAdapter {
239
240
  }
240
241
  const isExtractionEvent = Object.values(extraction_1.ExtractorEventType).includes(newEventType);
241
242
  const isLoaderEvent = Object.values(loading_1.LoaderEventType).includes(newEventType);
243
+ const progressData = {};
244
+ if (isExtractionEvent &&
245
+ (newEventType == extraction_1.ExtractorEventType.DataExtractionDone ||
246
+ newEventType == extraction_1.ExtractorEventType.DataExtractionProgress ||
247
+ newEventType == extraction_1.ExtractorEventType.AttachmentExtractionDone ||
248
+ newEventType == extraction_1.ExtractorEventType.AttachmentExtractionProgress)) {
249
+ const repo = this.lastExtractedItemType
250
+ ? this.repos.find((r) => r.itemType === this.lastExtractedItemType)
251
+ : undefined;
252
+ if (repo) {
253
+ progressData.item_type = repo.itemType;
254
+ progressData.newest_created_date = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(repo.dateRanges.creationDate.newest);
255
+ progressData.oldest_created_date = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(repo.dateRanges.creationDate.oldest);
256
+ progressData.newest_modified_date = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(repo.dateRanges.modifiedDate.newest);
257
+ progressData.oldest_modified_date = (0, worker_adapter_helpers_1.toRfc3339Timestamp)(repo.dateRanges.modifiedDate.oldest);
258
+ }
259
+ }
242
260
  await (0, control_protocol_1.emit)({
243
261
  eventType: newEventType,
244
262
  event: this.event,
245
263
  data: Object.assign(Object.assign(Object.assign({}, data), (isExtractionEvent ? { artifacts: this.artifacts } : {})), (isLoaderEvent
246
264
  ? { reports: this.reports, processed_files: this.processedFiles }
247
265
  : {})),
266
+ worker_metadata: Object.assign({}, progressData),
248
267
  });
249
268
  const message = {
250
269
  subject: workers_1.WorkerMessageSubject.WorkerMessageEmitted,
@@ -766,7 +785,7 @@ class WorkerAdapter {
766
785
  }
767
786
  return {
768
787
  report: {
769
- item_type: 'attachment',
788
+ item_type: 'attachments',
770
789
  [loading_1.ActionType.CREATED]: 1,
771
790
  },
772
791
  };
@@ -775,7 +794,7 @@ class WorkerAdapter {
775
794
  console.warn('Failed to create attachment in external system', error);
776
795
  return {
777
796
  report: {
778
- item_type: 'attachment',
797
+ item_type: 'attachments',
779
798
  [loading_1.ActionType.FAILED]: 1,
780
799
  },
781
800
  };
@@ -553,7 +553,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.loadAttachment`, () => {
553
553
  create,
554
554
  });
555
555
  // Assert
556
- expect((_a = result.report) === null || _a === void 0 ? void 0 : _a.item_type).toBe('attachment');
556
+ expect((_a = result.report) === null || _a === void 0 ? void 0 : _a.item_type).toBe('attachments');
557
557
  expect((_b = result.report) === null || _b === void 0 ? void 0 : _b[loading_1.ActionType.CREATED]).toBe(1);
558
558
  });
559
559
  it('should still return CREATED even when mapper create fails — attachment loading is resilient', async () => {
@@ -584,7 +584,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.loadAttachment`, () => {
584
584
  expect((_a = result.rateLimit) === null || _a === void 0 ? void 0 : _a.delay).toBe(30);
585
585
  });
586
586
  it('should return a FAILED report when create returns neither id nor delay', async () => {
587
- var _a;
587
+ var _a, _b;
588
588
  // Arrange
589
589
  const create = jest.fn().mockResolvedValue({ id: null, delay: null });
590
590
  // Act
@@ -593,6 +593,7 @@ describe(`${worker_adapter_1.WorkerAdapter.name}.loadAttachment`, () => {
593
593
  create,
594
594
  });
595
595
  // Assert
596
- expect((_a = result.report) === null || _a === void 0 ? void 0 : _a[loading_1.ActionType.FAILED]).toBe(1);
596
+ expect((_a = result.report) === null || _a === void 0 ? void 0 : _a.item_type).toBe('attachments');
597
+ expect((_b = result.report) === null || _b === void 0 ? void 0 : _b[loading_1.ActionType.FAILED]).toBe(1);
597
598
  });
598
599
  });
@@ -10,6 +10,16 @@ export declare class Repo {
10
10
  private onUpload;
11
11
  private options?;
12
12
  uploadedArtifacts: Artifact[];
13
+ dateRanges: {
14
+ creationDate: {
15
+ oldest?: number;
16
+ newest?: number;
17
+ };
18
+ modifiedDate: {
19
+ oldest?: number;
20
+ newest?: number;
21
+ };
22
+ };
13
23
  constructor({ event, itemType, normalize, onUpload, options, }: RepoFactoryInterface);
14
24
  getItems(): (NormalizedItem | NormalizedAttachment | Item)[];
15
25
  upload(batch?: (NormalizedItem | NormalizedAttachment | Item)[]): Promise<void | ErrorRecord>;
@@ -1 +1 @@
1
- {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/repo/repo.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAI3D,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAE3B,qBAAa,IAAI;IACf,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAmD;IAChE,OAAO,CAAC,SAAS,CAAC,CAAwD;IAC1E,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAuB;IAChC,iBAAiB,EAAE,QAAQ,EAAE,CAAC;gBAEzB,EACV,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,OAAO,GACR,EAAE,oBAAoB;IAUvB,QAAQ,IAAI,CAAC,cAAc,GAAG,oBAAoB,GAAG,IAAI,CAAC,EAAE;IAItD,MAAM,CACV,KAAK,CAAC,EAAE,CAAC,cAAc,GAAG,oBAAoB,GAAG,IAAI,CAAC,EAAE,GACvD,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC;IAqCxB,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAyC5C"}
1
+ {"version":3,"file":"repo.d.ts","sourceRoot":"","sources":["../../src/repo/repo.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAE9C,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAI3D,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACrB,MAAM,mBAAmB,CAAC;AAG3B,qBAAa,IAAI;IACf,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAmD;IAChE,OAAO,CAAC,SAAS,CAAC,CAAwD;IAC1E,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAAC,CAAuB;IAChC,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC9B,UAAU,EAAE;QACjB,YAAY,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACnD,YAAY,EAAE;YAAE,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KACpD,CAGC;gBAEU,EACV,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,OAAO,GACR,EAAE,oBAAoB;IAUvB,QAAQ,IAAI,CAAC,cAAc,GAAG,oBAAoB,GAAG,IAAI,CAAC,EAAE;IAItD,MAAM,CACV,KAAK,CAAC,EAAE,CAAC,cAAc,GAAG,oBAAoB,GAAG,IAAI,CAAC,EAAE,GACvD,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC;IAsDxB,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;CAyC5C"}
@@ -0,0 +1,6 @@
1
+ export declare function updateRange(range: {
2
+ oldest?: number;
3
+ newest?: number;
4
+ }, ms: number): void;
5
+ export declare function toValidTimestamp(value: string): number | undefined;
6
+ //# sourceMappingURL=repo.helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.helpers.d.ts","sourceRoot":"","sources":["../../src/repo/repo.helpers.ts"],"names":[],"mappings":"AAAA,wBAAgB,WAAW,CACzB,KAAK,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,EAC3C,EAAE,EAAE,MAAM,GACT,IAAI,CAON;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGlE"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.updateRange = updateRange;
4
+ exports.toValidTimestamp = toValidTimestamp;
5
+ function updateRange(range, ms) {
6
+ if (range.oldest === undefined || ms < range.oldest) {
7
+ range.oldest = ms;
8
+ }
9
+ if (range.newest === undefined || ms > range.newest) {
10
+ range.newest = ms;
11
+ }
12
+ }
13
+ function toValidTimestamp(value) {
14
+ const ms = new Date(value).getTime();
15
+ return Number.isFinite(ms) ? ms : undefined;
16
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=repo.helpers.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repo.helpers.test.d.ts","sourceRoot":"","sources":["../../src/repo/repo.helpers.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const repo_helpers_1 = require("./repo.helpers");
4
+ describe(repo_helpers_1.updateRange.name, () => {
5
+ it('should set both oldest and newest from the first value', () => {
6
+ const range = {};
7
+ (0, repo_helpers_1.updateRange)(range, 1000);
8
+ expect(range).toEqual({ oldest: 1000, newest: 1000 });
9
+ });
10
+ it('should expand oldest when a smaller value is seen', () => {
11
+ const range = {
12
+ oldest: 1000,
13
+ newest: 1000,
14
+ };
15
+ (0, repo_helpers_1.updateRange)(range, 500);
16
+ expect(range).toEqual({ oldest: 500, newest: 1000 });
17
+ });
18
+ it('should expand newest when a larger value is seen', () => {
19
+ const range = {
20
+ oldest: 1000,
21
+ newest: 1000,
22
+ };
23
+ (0, repo_helpers_1.updateRange)(range, 1500);
24
+ expect(range).toEqual({ oldest: 1000, newest: 1500 });
25
+ });
26
+ it('should not change bounds when the value is within range', () => {
27
+ const range = {
28
+ oldest: 1000,
29
+ newest: 2000,
30
+ };
31
+ (0, repo_helpers_1.updateRange)(range, 1500);
32
+ expect(range).toEqual({ oldest: 1000, newest: 2000 });
33
+ });
34
+ it('[edge] should track a value of 0 (Unix epoch) instead of treating it as unset', () => {
35
+ const range = {};
36
+ (0, repo_helpers_1.updateRange)(range, 0);
37
+ expect(range).toEqual({ oldest: 0, newest: 0 });
38
+ });
39
+ it('[edge] should not let a later 0 value overwrite an already-set range incorrectly', () => {
40
+ const range = {
41
+ oldest: 1000,
42
+ newest: 2000,
43
+ };
44
+ (0, repo_helpers_1.updateRange)(range, 0);
45
+ expect(range).toEqual({ oldest: 0, newest: 2000 });
46
+ });
47
+ it('[edge] should handle negative timestamps (pre-1970 dates)', () => {
48
+ const range = {};
49
+ (0, repo_helpers_1.updateRange)(range, -1000);
50
+ (0, repo_helpers_1.updateRange)(range, 500);
51
+ expect(range).toEqual({ oldest: -1000, newest: 500 });
52
+ });
53
+ });
54
+ describe(repo_helpers_1.toValidTimestamp.name, () => {
55
+ it('should convert a valid RFC3339 string to milliseconds', () => {
56
+ const result = (0, repo_helpers_1.toValidTimestamp)('2023-06-15T12:00:00.000Z');
57
+ expect(result).toBe(new Date('2023-06-15T12:00:00.000Z').getTime());
58
+ });
59
+ it('[edge] should return 0 for the Unix epoch instead of undefined', () => {
60
+ const result = (0, repo_helpers_1.toValidTimestamp)('1970-01-01T00:00:00.000Z');
61
+ expect(result).toBe(0);
62
+ });
63
+ it('[edge] should return undefined for an invalid date string', () => {
64
+ const result = (0, repo_helpers_1.toValidTimestamp)('not-a-date');
65
+ expect(result).toBeUndefined();
66
+ });
67
+ it('[edge] should return undefined for an empty string', () => {
68
+ const result = (0, repo_helpers_1.toValidTimestamp)('');
69
+ expect(result).toBeUndefined();
70
+ });
71
+ });
@@ -39,6 +39,8 @@ export interface NormalizedAttachment {
39
39
  author_id?: string;
40
40
  inline?: boolean;
41
41
  content_type?: string;
42
+ created_date?: string;
43
+ modified_date?: string;
42
44
  grand_parent_id?: number | string;
43
45
  }
44
46
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"repo.interfaces.d.ts","sourceRoot":"","sources":["../../src/repo/repo.interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,GAAG,oBAAoB,CAAC;IACtE,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,GAAG,oBAAoB,CAAC;IACtE,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IAItB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED;;GAEG;AAEH,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC"}
1
+ {"version":3,"file":"repo.interfaces.d.ts","sourceRoot":"","sources":["../../src/repo/repo.interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAE3D,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAExD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,GAAG,oBAAoB,CAAC;IACtE,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,YAAY,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,cAAc,GAAG,oBAAoB,CAAC;IACtE,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,oBAAoB,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IAIvB,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACnC;AAED;;GAEG;AAEH,MAAM,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC"}