@devrev/airsync-sdk 2.0.0-beta.1 → 2.0.0-beta.3

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 (191) hide show
  1. package/README.md +0 -4
  2. package/dist/airsync-sdk.d.ts +2059 -0
  3. package/dist/attachments-streaming/attachments-streaming-pool.d.ts +2 -1
  4. package/dist/attachments-streaming/attachments-streaming-pool.d.ts.map +1 -1
  5. package/dist/attachments-streaming/attachments-streaming-pool.interfaces.d.ts +8 -2
  6. package/dist/attachments-streaming/attachments-streaming-pool.interfaces.d.ts.map +1 -1
  7. package/dist/attachments-streaming/attachments-streaming-pool.js +27 -16
  8. package/dist/attachments-streaming/attachments-streaming-pool.test.js +246 -27
  9. package/dist/common/constants.d.ts +16 -5
  10. package/dist/common/constants.d.ts.map +1 -1
  11. package/dist/common/constants.js +20 -6
  12. package/dist/common/control-protocol.d.ts +2 -2
  13. package/dist/common/control-protocol.d.ts.map +1 -1
  14. package/dist/common/control-protocol.js +1 -5
  15. package/dist/common/helpers.d.ts +8 -0
  16. package/dist/common/helpers.d.ts.map +1 -1
  17. package/dist/common/helpers.js +15 -0
  18. package/dist/common/install-initial-domain-mapping.d.ts +2 -2
  19. package/dist/common/install-initial-domain-mapping.test.js +5 -2
  20. package/dist/common/test-utils.d.ts +19 -0
  21. package/dist/common/test-utils.d.ts.map +1 -0
  22. package/dist/common/test-utils.js +101 -0
  23. package/dist/common/time-value-resolver.d.ts +47 -0
  24. package/dist/common/time-value-resolver.d.ts.map +1 -0
  25. package/dist/common/time-value-resolver.js +141 -0
  26. package/dist/common/time-value-resolver.test.d.ts +2 -0
  27. package/dist/common/time-value-resolver.test.d.ts.map +1 -0
  28. package/dist/common/time-value-resolver.test.js +268 -0
  29. package/dist/http/axios-client-internal.d.ts.map +1 -1
  30. package/dist/http/axios-client-internal.js +7 -0
  31. package/dist/http/axios-client-internal.test.js +21 -0
  32. package/dist/http/axios-client.d.ts.map +1 -1
  33. package/dist/http/axios-client.js +6 -3
  34. package/dist/http/index.d.ts +0 -1
  35. package/dist/http/index.d.ts.map +1 -1
  36. package/dist/http/index.js +0 -1
  37. package/dist/index.d.ts +12 -10
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +18 -13
  40. package/dist/logger/logger.d.ts +1 -17
  41. package/dist/logger/logger.d.ts.map +1 -1
  42. package/dist/logger/logger.interfaces.d.ts +2 -2
  43. package/dist/logger/logger.js +35 -27
  44. package/dist/logger/logger.test.js +66 -19
  45. package/dist/mappers/mappers.interface.d.ts +5 -5
  46. package/dist/mappers/mappers.interface.js +3 -3
  47. package/dist/mappers/mappers.test.js +8 -10
  48. package/dist/mock-server/mock-server.d.ts +47 -0
  49. package/dist/mock-server/mock-server.d.ts.map +1 -0
  50. package/dist/mock-server/mock-server.interfaces.d.ts +73 -0
  51. package/dist/mock-server/mock-server.interfaces.d.ts.map +1 -0
  52. package/dist/mock-server/mock-server.interfaces.js +4 -0
  53. package/dist/mock-server/mock-server.js +278 -0
  54. package/dist/multithreading/adapters/base-adapter.d.ts +56 -0
  55. package/dist/multithreading/adapters/base-adapter.d.ts.map +1 -0
  56. package/dist/multithreading/adapters/base-adapter.emit.test.d.ts +2 -0
  57. package/dist/multithreading/adapters/base-adapter.emit.test.d.ts.map +1 -0
  58. package/dist/multithreading/adapters/base-adapter.emit.test.js +403 -0
  59. package/dist/multithreading/adapters/base-adapter.js +105 -0
  60. package/dist/multithreading/adapters/extraction-adapter.artifacts.test.d.ts +2 -0
  61. package/dist/multithreading/adapters/extraction-adapter.artifacts.test.d.ts.map +1 -0
  62. package/dist/multithreading/{worker-adapter/worker-adapter.artifacts.test.js → adapters/extraction-adapter.artifacts.test.js} +53 -5
  63. package/dist/multithreading/adapters/extraction-adapter.d.ts +58 -0
  64. package/dist/multithreading/adapters/extraction-adapter.d.ts.map +1 -0
  65. package/dist/multithreading/adapters/extraction-adapter.js +325 -0
  66. package/dist/multithreading/adapters/extraction-adapter.test.d.ts +2 -0
  67. package/dist/multithreading/adapters/extraction-adapter.test.d.ts.map +1 -0
  68. package/dist/multithreading/adapters/extraction-adapter.test.js +789 -0
  69. package/dist/multithreading/adapters/loading-adapter.d.ts +44 -0
  70. package/dist/multithreading/adapters/loading-adapter.d.ts.map +1 -0
  71. package/dist/multithreading/{worker-adapter/worker-adapter.helpers.d.ts → adapters/loading-adapter.helpers.d.ts} +1 -1
  72. package/dist/multithreading/adapters/loading-adapter.helpers.d.ts.map +1 -0
  73. package/dist/multithreading/adapters/loading-adapter.helpers.test.d.ts +2 -0
  74. package/dist/multithreading/adapters/loading-adapter.helpers.test.d.ts.map +1 -0
  75. package/dist/multithreading/{worker-adapter/worker-adapter.helpers.test.js → adapters/loading-adapter.helpers.test.js} +26 -26
  76. package/dist/multithreading/adapters/loading-adapter.js +461 -0
  77. package/dist/multithreading/adapters/loading-adapter.test.d.ts +2 -0
  78. package/dist/multithreading/adapters/loading-adapter.test.d.ts.map +1 -0
  79. package/dist/multithreading/adapters/loading-adapter.test.js +589 -0
  80. package/dist/multithreading/adapters/serialization.test.d.ts +2 -0
  81. package/dist/multithreading/adapters/serialization.test.d.ts.map +1 -0
  82. package/dist/multithreading/adapters/serialization.test.js +71 -0
  83. package/dist/multithreading/create-worker.test.js +36 -17
  84. package/dist/multithreading/process-task.d.ts +16 -1
  85. package/dist/multithreading/process-task.d.ts.map +1 -1
  86. package/dist/multithreading/process-task.js +97 -82
  87. package/dist/multithreading/process-task.test.d.ts +2 -0
  88. package/dist/multithreading/process-task.test.d.ts.map +1 -0
  89. package/dist/multithreading/process-task.test.js +146 -0
  90. package/dist/multithreading/spawn/spawn.d.ts +3 -3
  91. package/dist/multithreading/spawn/spawn.d.ts.map +1 -1
  92. package/dist/multithreading/spawn/spawn.helpers.d.ts.map +1 -1
  93. package/dist/multithreading/spawn/spawn.helpers.js +6 -14
  94. package/dist/multithreading/spawn/spawn.helpers.test.js +8 -8
  95. package/dist/multithreading/spawn/spawn.js +20 -25
  96. package/dist/multithreading/spawn/spawn.test.d.ts +2 -0
  97. package/dist/multithreading/spawn/spawn.test.d.ts.map +1 -0
  98. package/dist/multithreading/spawn/spawn.test.js +232 -0
  99. package/dist/repo/repo.d.ts.map +1 -1
  100. package/dist/repo/repo.interfaces.d.ts +4 -2
  101. package/dist/repo/repo.interfaces.d.ts.map +1 -1
  102. package/dist/repo/repo.js +4 -3
  103. package/dist/repo/repo.test.js +60 -7
  104. package/dist/state/base-state.d.ts +75 -0
  105. package/dist/state/base-state.d.ts.map +1 -0
  106. package/dist/state/base-state.js +261 -0
  107. package/dist/state/extraction-state.d.ts +29 -0
  108. package/dist/state/extraction-state.d.ts.map +1 -0
  109. package/dist/state/extraction-state.js +122 -0
  110. package/dist/state/loading-state.d.ts +19 -0
  111. package/dist/state/loading-state.d.ts.map +1 -0
  112. package/dist/state/loading-state.js +40 -0
  113. package/dist/state/state.extract-window.test.d.ts +2 -0
  114. package/dist/state/state.extract-window.test.d.ts.map +1 -0
  115. package/dist/state/state.extract-window.test.js +163 -0
  116. package/dist/state/state.interfaces.d.ts +44 -27
  117. package/dist/state/state.interfaces.d.ts.map +1 -1
  118. package/dist/state/state.interfaces.js +18 -5
  119. package/dist/state/state.pending-boundaries.test.d.ts +2 -0
  120. package/dist/state/state.pending-boundaries.test.d.ts.map +1 -0
  121. package/dist/state/state.pending-boundaries.test.js +189 -0
  122. package/dist/state/state.post-state.test.d.ts +2 -0
  123. package/dist/state/state.post-state.test.d.ts.map +1 -0
  124. package/dist/state/state.post-state.test.js +78 -0
  125. package/dist/state/state.test.js +135 -88
  126. package/dist/state/state.time-value-resolution.test.d.ts +2 -0
  127. package/dist/state/state.time-value-resolution.test.d.ts.map +1 -0
  128. package/dist/state/state.time-value-resolution.test.js +175 -0
  129. package/dist/tsdoc-metadata.json +11 -0
  130. package/dist/types/common.d.ts +0 -25
  131. package/dist/types/common.d.ts.map +1 -1
  132. package/dist/types/common.js +1 -11
  133. package/dist/types/external-domain-metadata.d.ts +20 -1
  134. package/dist/types/external-domain-metadata.d.ts.map +1 -1
  135. package/dist/types/extraction.d.ts +92 -221
  136. package/dist/types/extraction.d.ts.map +1 -1
  137. package/dist/types/extraction.js +56 -124
  138. package/dist/types/extraction.test.js +69 -43
  139. package/dist/types/index.d.ts +3 -3
  140. package/dist/types/index.d.ts.map +1 -1
  141. package/dist/types/index.js +6 -3
  142. package/dist/types/loading.d.ts +4 -24
  143. package/dist/types/loading.d.ts.map +1 -1
  144. package/dist/types/loading.js +1 -22
  145. package/dist/types/workers.d.ts +47 -29
  146. package/dist/types/workers.d.ts.map +1 -1
  147. package/dist/types/workers.js +1 -0
  148. package/dist/uploader/uploader.d.ts +1 -0
  149. package/dist/uploader/uploader.d.ts.map +1 -1
  150. package/dist/uploader/uploader.helpers.test.js +0 -11
  151. package/dist/uploader/uploader.interfaces.d.ts +3 -3
  152. package/dist/uploader/uploader.js +53 -20
  153. package/dist/uploader/uploader.test.js +22 -10
  154. package/package.json +10 -13
  155. package/dist/common/event-type-translation.d.ts +0 -24
  156. package/dist/common/event-type-translation.d.ts.map +0 -1
  157. package/dist/common/event-type-translation.js +0 -117
  158. package/dist/deprecated/adapter/index.d.ts +0 -62
  159. package/dist/deprecated/adapter/index.d.ts.map +0 -1
  160. package/dist/deprecated/adapter/index.js +0 -151
  161. package/dist/deprecated/common/helpers.d.ts +0 -7
  162. package/dist/deprecated/common/helpers.d.ts.map +0 -1
  163. package/dist/deprecated/common/helpers.js +0 -47
  164. package/dist/deprecated/demo-extractor/external_domain_metadata.json +0 -38
  165. package/dist/deprecated/demo-extractor/index.d.ts +0 -18
  166. package/dist/deprecated/demo-extractor/index.d.ts.map +0 -1
  167. package/dist/deprecated/demo-extractor/index.js +0 -161
  168. package/dist/deprecated/http/client.d.ts +0 -22
  169. package/dist/deprecated/http/client.d.ts.map +0 -1
  170. package/dist/deprecated/http/client.js +0 -161
  171. package/dist/deprecated/uploader/index.d.ts +0 -35
  172. package/dist/deprecated/uploader/index.d.ts.map +0 -1
  173. package/dist/deprecated/uploader/index.js +0 -161
  174. package/dist/http/types.d.ts +0 -17
  175. package/dist/http/types.d.ts.map +0 -1
  176. package/dist/http/types.js +0 -2
  177. package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.d.ts +0 -2
  178. package/dist/multithreading/worker-adapter/worker-adapter.artifacts.test.d.ts.map +0 -1
  179. package/dist/multithreading/worker-adapter/worker-adapter.d.ts +0 -91
  180. package/dist/multithreading/worker-adapter/worker-adapter.d.ts.map +0 -1
  181. package/dist/multithreading/worker-adapter/worker-adapter.helpers.d.ts.map +0 -1
  182. package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.d.ts +0 -2
  183. package/dist/multithreading/worker-adapter/worker-adapter.helpers.test.d.ts.map +0 -1
  184. package/dist/multithreading/worker-adapter/worker-adapter.js +0 -745
  185. package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts +0 -2
  186. package/dist/multithreading/worker-adapter/worker-adapter.test.d.ts.map +0 -1
  187. package/dist/multithreading/worker-adapter/worker-adapter.test.js +0 -483
  188. package/dist/state/state.d.ts +0 -30
  189. package/dist/state/state.d.ts.map +0 -1
  190. package/dist/state/state.js +0 -166
  191. /package/dist/multithreading/{worker-adapter/worker-adapter.helpers.js → adapters/loading-adapter.helpers.js} +0 -0
@@ -0,0 +1,56 @@
1
+ import { BaseState } from '../../state/base-state';
2
+ import { BaseSdkState } from '../../state/state.interfaces';
3
+ import { AirSyncEvent, EventData, ExtractorEventType } from '../../types/extraction';
4
+ import { LoaderEventType } from '../../types/loading';
5
+ import { WorkerAdapterOptions } from '../../types/workers';
6
+ import { Uploader } from '../../uploader/uploader';
7
+ /**
8
+ * BaseAdapter holds the state and behavior shared by both sync modes and owns
9
+ * the `emit` control-protocol flow as a template method. Mode-specific adapters
10
+ * (`ExtractionAdapter`, `LoadingAdapter`) implement the abstract hooks to inject
11
+ * their own pre-emit work and event payload shaping.
12
+ *
13
+ * @typeParam ConnectorState - the connector-owned state shape
14
+ * @typeParam StateClass - the mode's `BaseState` subclass
15
+ */
16
+ export declare abstract class BaseAdapter<ConnectorState, StateClass extends BaseState<ConnectorState, BaseSdkState>> {
17
+ readonly event: AirSyncEvent;
18
+ readonly options?: WorkerAdapterOptions;
19
+ isTimeout: boolean;
20
+ hasWorkerEmitted: boolean;
21
+ protected adapterState: StateClass;
22
+ protected uploader: Uploader;
23
+ constructor({ event, adapterState, options, }: {
24
+ event: AirSyncEvent;
25
+ adapterState: StateClass;
26
+ options?: WorkerAdapterOptions;
27
+ });
28
+ get state(): ConnectorState;
29
+ set state(value: ConnectorState);
30
+ postState(): Promise<void>;
31
+ /**
32
+ * Pre-emit hook run before any state is persisted or the event is sent.
33
+ * Extraction uploads pending repos and updates extraction boundaries here;
34
+ * loading has nothing to do. Throwing aborts the emit (the caller signals
35
+ * worker exit).
36
+ */
37
+ protected abstract beforeEmit(newEventType: ExtractorEventType | LoaderEventType): Promise<void>;
38
+ /**
39
+ * Builds the mode-specific extras merged into the emitted event payload
40
+ * (extraction: artifacts; loading: reports + processed files).
41
+ */
42
+ protected abstract buildEmitPayload(newEventType: ExtractorEventType | LoaderEventType): EventData;
43
+ /**
44
+ * Post-emit hook run after the event has been sent successfully. Extraction
45
+ * clears its accumulated artifacts here; loading has nothing to do.
46
+ */
47
+ protected abstract afterEmit(newEventType: ExtractorEventType | LoaderEventType): void;
48
+ /**
49
+ * Emits an event to the platform.
50
+ *
51
+ * @param newEventType - The event type to be emitted
52
+ * @param data - The data to be sent with the event
53
+ */
54
+ emit(newEventType: ExtractorEventType | LoaderEventType, data?: EventData): Promise<void>;
55
+ }
56
+ //# sourceMappingURL=base-adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/base-adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAC5D,OAAO,EACL,YAAY,EACZ,SAAS,EACT,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EACL,oBAAoB,EAGrB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD;;;;;;;;GAQG;AACH,8BAAsB,WAAW,CAC/B,cAAc,EACd,UAAU,SAAS,SAAS,CAAC,cAAc,EAAE,YAAY,CAAC;IAE1D,QAAQ,CAAC,KAAK,EAAE,YAAY,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,oBAAoB,CAAC;IACxC,SAAS,EAAE,OAAO,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAE1B,SAAS,CAAC,YAAY,EAAE,UAAU,CAAC;IACnC,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAC;gBAEjB,EACV,KAAK,EACL,YAAY,EACZ,OAAO,GACR,EAAE;QACD,KAAK,EAAE,YAAY,CAAC;QACpB,YAAY,EAAE,UAAU,CAAC;QACzB,OAAO,CAAC,EAAE,oBAAoB,CAAC;KAChC;IAYD,IAAI,KAAK,IAAI,cAAc,CAE1B;IAED,IAAI,KAAK,CAAC,KAAK,EAAE,cAAc,EAE9B;IAEK,SAAS;IAMf;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,CAAC,UAAU,CAC3B,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,OAAO,CAAC,IAAI,CAAC;IAEhB;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,gBAAgB,CACjC,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,SAAS;IAEZ;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,SAAS,CAC1B,YAAY,EAAE,kBAAkB,GAAG,eAAe,GACjD,IAAI;IAEP;;;;;OAKG;IACG,IAAI,CACR,YAAY,EAAE,kBAAkB,GAAG,eAAe,EAClD,IAAI,CAAC,EAAE,SAAS,GACf,OAAO,CAAC,IAAI,CAAC;CAkEjB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=base-adapter.emit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-adapter.emit.test.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/base-adapter.emit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,403 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const constants_1 = require("../../common/constants");
4
+ const extraction_state_1 = require("../../state/extraction-state");
5
+ const loading_state_1 = require("../../state/loading-state");
6
+ const jest_setup_1 = require("../../tests/jest.setup");
7
+ const test_utils_1 = require("../../common/test-utils");
8
+ const types_1 = require("../../types");
9
+ const loading_1 = require("../../types/loading");
10
+ const extraction_adapter_1 = require("./extraction-adapter");
11
+ const loading_adapter_1 = require("./loading-adapter");
12
+ /* eslint-disable @typescript-eslint/no-require-imports */
13
+ jest.mock('../../common/control-protocol', () => ({
14
+ emit: jest.fn().mockResolvedValue({}),
15
+ }));
16
+ jest.mock('../../mappers/mappers');
17
+ jest.mock('../../uploader/uploader');
18
+ jest.mock('../../repo/repo');
19
+ jest.mock('node:worker_threads', () => ({
20
+ parentPort: { postMessage: jest.fn() },
21
+ }));
22
+ jest.mock('../../attachments-streaming/attachments-streaming-pool', () => ({
23
+ AttachmentsStreamingPool: jest.fn().mockImplementation(() => ({
24
+ streamAll: jest.fn().mockResolvedValue(undefined),
25
+ })),
26
+ }));
27
+ function makeAdapter(eventType = types_1.EventType.StartExtractingData) {
28
+ const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
29
+ payload: { event_type: eventType },
30
+ });
31
+ const initialState = {
32
+ attachments: { completed: false },
33
+ };
34
+ const adapterState = new extraction_state_1.ExtractionState({ event, initialState });
35
+ adapterState.sdkState.snapInVersionId = '';
36
+ adapterState.sdkState.toDevRev = {
37
+ attachmentsMetadata: {
38
+ artifactIds: [],
39
+ lastProcessed: 0,
40
+ lastProcessedAttachmentsIdsList: [],
41
+ },
42
+ };
43
+ const adapter = new extraction_adapter_1.ExtractionAdapter({ event, adapterState });
44
+ return { adapter, event, adapterState };
45
+ }
46
+ function makeLoadingAdapter(eventType = types_1.EventType.StartLoadingData) {
47
+ const event = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
48
+ payload: { event_type: eventType },
49
+ });
50
+ const initialState = {
51
+ attachments: { completed: false },
52
+ };
53
+ const adapterState = new loading_state_1.LoadingState({ event, initialState });
54
+ adapterState.sdkState.snapInVersionId = '';
55
+ const adapter = new loading_adapter_1.LoadingAdapter({ event, adapterState });
56
+ return { adapter, event, adapterState };
57
+ }
58
+ describe(`${extraction_adapter_1.ExtractionAdapter.name}.emit`, () => {
59
+ let adapter;
60
+ let mockPostMessage;
61
+ beforeEach(() => {
62
+ jest.clearAllMocks();
63
+ ({ adapter } = makeAdapter());
64
+ const workerThreads = require('node:worker_threads');
65
+ mockPostMessage = jest.fn();
66
+ if (workerThreads.parentPort) {
67
+ jest
68
+ .spyOn(workerThreads.parentPort, 'postMessage')
69
+ .mockImplementation(mockPostMessage);
70
+ }
71
+ else {
72
+ workerThreads.parentPort = { postMessage: mockPostMessage };
73
+ }
74
+ });
75
+ afterEach(() => {
76
+ jest.restoreAllMocks();
77
+ });
78
+ it('should emit only one event when multiple events of same type are sent', async () => {
79
+ // Arrange
80
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
81
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
82
+ // Act
83
+ await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
84
+ reports: [],
85
+ processed_files: [],
86
+ });
87
+ await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
88
+ reports: [],
89
+ processed_files: [],
90
+ });
91
+ // Assert
92
+ expect(mockPostMessage).toHaveBeenCalledTimes(1);
93
+ });
94
+ it('should emit only once even when a different event type follows', async () => {
95
+ // Arrange
96
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
97
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
98
+ // Act
99
+ await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
100
+ reports: [],
101
+ processed_files: [],
102
+ });
103
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionError, {
104
+ reports: [],
105
+ processed_files: [],
106
+ });
107
+ await adapter.emit(types_1.ExtractorEventType.AttachmentExtractionError, {
108
+ reports: [],
109
+ processed_files: [],
110
+ });
111
+ // Assert
112
+ expect(mockPostMessage).toHaveBeenCalledTimes(1);
113
+ });
114
+ it('should correctly emit one event even if postState errors', async () => {
115
+ // Arrange
116
+ adapter['adapterState'].postState = jest
117
+ .fn()
118
+ .mockRejectedValue(new Error('postState error'));
119
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
120
+ // Act
121
+ await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
122
+ reports: [],
123
+ processed_files: [],
124
+ });
125
+ // Assert
126
+ expect(mockPostMessage).toHaveBeenCalledTimes(1);
127
+ });
128
+ it('should correctly emit one event even if uploadAllRepos errors', async () => {
129
+ // Arrange
130
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
131
+ adapter.uploadAllRepos = jest
132
+ .fn()
133
+ .mockRejectedValue(new Error('uploadAllRepos error'));
134
+ // Act
135
+ await adapter.emit(types_1.ExtractorEventType.MetadataExtractionError, {
136
+ reports: [],
137
+ processed_files: [],
138
+ });
139
+ // Assert
140
+ expect(mockPostMessage).toHaveBeenCalledTimes(1);
141
+ });
142
+ it('should include artifacts in data for extraction events', async () => {
143
+ // Arrange
144
+ const { emit: mockEmit } = require('../../common/control-protocol');
145
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
146
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
147
+ adapter['_artifacts'] = [
148
+ { id: 'art-1', item_count: 10, item_type: 'issues' },
149
+ ];
150
+ // Act
151
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionDone);
152
+ // Assert
153
+ expect(mockEmit).toHaveBeenCalledWith(expect.objectContaining({
154
+ data: expect.objectContaining({
155
+ artifacts: expect.arrayContaining([
156
+ expect.objectContaining({ id: 'art-1' }),
157
+ ]),
158
+ }),
159
+ }));
160
+ const callData = mockEmit.mock.calls[0][0].data;
161
+ expect(callData).not.toHaveProperty('reports');
162
+ expect(callData).not.toHaveProperty('processed_files');
163
+ });
164
+ it('should include reports and processed_files in data for loader events', async () => {
165
+ // Arrange
166
+ const { emit: mockEmit } = require('../../common/control-protocol');
167
+ const { adapter: loadingAdapter } = makeLoadingAdapter();
168
+ loadingAdapter['adapterState'].postState = jest
169
+ .fn()
170
+ .mockResolvedValue(undefined);
171
+ loadingAdapter['loaderReports'] = [
172
+ { item_type: 'tasks', [loading_1.ActionType.CREATED]: 5 },
173
+ ];
174
+ loadingAdapter['_processedFiles'] = ['file-1', 'file-2'];
175
+ // Act
176
+ await loadingAdapter.emit(types_1.LoaderEventType.DataLoadingDone);
177
+ // Assert
178
+ expect(mockEmit).toHaveBeenCalledWith(expect.objectContaining({
179
+ data: expect.objectContaining({
180
+ reports: expect.arrayContaining([
181
+ expect.objectContaining({ item_type: 'tasks' }),
182
+ ]),
183
+ processed_files: ['file-1', 'file-2'],
184
+ }),
185
+ }));
186
+ const callData = mockEmit.mock.calls[0][0].data;
187
+ expect(callData).not.toHaveProperty('artifacts');
188
+ });
189
+ it('should not include artifacts, reports, or processed_files for unknown event types', async () => {
190
+ // Arrange
191
+ const { emit: mockEmit } = require('../../common/control-protocol');
192
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
193
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
194
+ adapter['_artifacts'] = [
195
+ { id: 'art-1', item_count: 10, item_type: 'issues' },
196
+ ];
197
+ // Act
198
+ await adapter.emit('SOME_UNKNOWN_EVENT');
199
+ // Assert
200
+ const callData = mockEmit.mock.calls[0][0].data;
201
+ expect(callData).not.toHaveProperty('artifacts');
202
+ expect(callData).not.toHaveProperty('reports');
203
+ expect(callData).not.toHaveProperty('processed_files');
204
+ });
205
+ it('should include artifacts for all ExtractorEventType values', async () => {
206
+ var _a, _b;
207
+ // Arrange
208
+ const { emit: mockEmit } = require('../../common/control-protocol');
209
+ const extractorEvents = [
210
+ types_1.ExtractorEventType.DataExtractionDone,
211
+ types_1.ExtractorEventType.DataExtractionProgress,
212
+ types_1.ExtractorEventType.DataExtractionError,
213
+ types_1.ExtractorEventType.AttachmentExtractionDone,
214
+ types_1.ExtractorEventType.AttachmentExtractionProgress,
215
+ ];
216
+ for (const eventType of extractorEvents) {
217
+ jest.clearAllMocks();
218
+ adapter.hasWorkerEmitted = false;
219
+ adapter['adapterState'].postState = jest
220
+ .fn()
221
+ .mockResolvedValue(undefined);
222
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
223
+ // Act
224
+ await adapter.emit(eventType);
225
+ // Assert
226
+ const callData = (_b = (_a = mockEmit.mock.calls[0]) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.data;
227
+ expect(callData).toHaveProperty('artifacts');
228
+ expect(callData).not.toHaveProperty('reports');
229
+ }
230
+ });
231
+ it('should include reports and processed_files for all LoaderEventType values', async () => {
232
+ var _a, _b;
233
+ // Arrange
234
+ const { emit: mockEmit } = require('../../common/control-protocol');
235
+ const loaderEvents = [
236
+ types_1.LoaderEventType.DataLoadingDone,
237
+ types_1.LoaderEventType.DataLoadingProgress,
238
+ types_1.LoaderEventType.DataLoadingError,
239
+ types_1.LoaderEventType.AttachmentLoadingDone,
240
+ types_1.LoaderEventType.AttachmentLoadingProgress,
241
+ ];
242
+ const { adapter: loadingAdapter } = makeLoadingAdapter();
243
+ loadingAdapter['adapterState'].postState = jest
244
+ .fn()
245
+ .mockResolvedValue(undefined);
246
+ for (const eventType of loaderEvents) {
247
+ jest.clearAllMocks();
248
+ loadingAdapter.hasWorkerEmitted = false;
249
+ // Act
250
+ await loadingAdapter.emit(eventType);
251
+ // Assert
252
+ const callData = (_b = (_a = mockEmit.mock.calls[0]) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.data;
253
+ expect(callData).toHaveProperty('reports');
254
+ expect(callData).toHaveProperty('processed_files');
255
+ expect(callData).not.toHaveProperty('artifacts');
256
+ }
257
+ });
258
+ it('should truncate a long error message, preserving the original prefix', async () => {
259
+ var _a, _b;
260
+ // Arrange
261
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
262
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
263
+ const longMessage = 'E'.repeat(20000);
264
+ // Act
265
+ await adapter.emit(types_1.ExtractorEventType.DataExtractionError, {
266
+ error: { message: longMessage },
267
+ });
268
+ // Assert
269
+ const { emit: mockEmit } = require('../../common/control-protocol');
270
+ const emittedMessage = (_b = (_a = mockEmit.mock.calls[0][0].data) === null || _a === void 0 ? void 0 : _a.error) === null || _b === void 0 ? void 0 : _b.message;
271
+ expect(emittedMessage.length).toBeLessThan(longMessage.length);
272
+ expect(emittedMessage.startsWith('E'.repeat(100))).toBe(true);
273
+ });
274
+ });
275
+ describe('WorkerAdapter — workersOldest / workersNewest boundary updates', () => {
276
+ let adapter;
277
+ let adapterState;
278
+ let mockPostMessage;
279
+ beforeEach(() => {
280
+ jest.clearAllMocks();
281
+ ({ adapter, adapterState } = makeAdapter());
282
+ const workerThreads = require('node:worker_threads');
283
+ mockPostMessage = jest.fn();
284
+ if (workerThreads.parentPort) {
285
+ jest
286
+ .spyOn(workerThreads.parentPort, 'postMessage')
287
+ .mockImplementation(mockPostMessage);
288
+ }
289
+ else {
290
+ workerThreads.parentPort = { postMessage: mockPostMessage };
291
+ }
292
+ adapter['adapterState'].postState = jest.fn().mockResolvedValue(undefined);
293
+ adapter.uploadAllRepos = jest.fn().mockResolvedValue(undefined);
294
+ });
295
+ afterEach(() => {
296
+ jest.restoreAllMocks();
297
+ });
298
+ async function emitDone(adapterInstance, extractionStart, extractionEnd) {
299
+ adapterInstance.event.payload.event_context.extract_from = extractionStart;
300
+ adapterInstance.event.payload.event_context.extract_to = extractionEnd;
301
+ // Reset the emit guard so we can emit multiple times within one test.
302
+ adapterInstance['hasWorkerEmitted'] = false;
303
+ await adapterInstance.emit(types_1.ExtractorEventType.AttachmentExtractionDone, {
304
+ reports: [],
305
+ processed_files: [],
306
+ });
307
+ }
308
+ describe('initial import with UNBOUNDED start', () => {
309
+ it('should set workersOldest to UNBOUNDED_DATE_TIME_VALUE and workersNewest to extraction end', async () => {
310
+ await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
311
+ expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
312
+ expect(adapterState.sdkState.workersNewest).toBe('2025-06-01T00:00:00.000Z');
313
+ });
314
+ });
315
+ describe('reconciliation after UNBOUNDED initial import', () => {
316
+ it('should NOT overwrite workersOldest when reconciliation start is later than sentinel', async () => {
317
+ await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
318
+ await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
319
+ expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
320
+ expect(adapterState.sdkState.workersNewest).toBe('2025-06-01T00:00:00.000Z');
321
+ });
322
+ it('should NOT overwrite workersOldest even when reconciliation start is very early', async () => {
323
+ await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
324
+ await emitDone(adapter, '1980-01-01T00:00:00.000Z', '1990-01-01T00:00:00.000Z');
325
+ expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
326
+ expect(adapterState.sdkState.workersNewest).toBe('2025-06-01T00:00:00.000Z');
327
+ });
328
+ });
329
+ describe('forward sync after UNBOUNDED initial import', () => {
330
+ it('should expand workersNewest forward while preserving workersOldest', async () => {
331
+ await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
332
+ await emitDone(adapter, '2025-06-01T00:00:00.000Z', '2025-07-01T00:00:00.000Z');
333
+ expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
334
+ expect(adapterState.sdkState.workersNewest).toBe('2025-07-01T00:00:00.000Z');
335
+ });
336
+ });
337
+ describe('reconciliation with end beyond current newest', () => {
338
+ it('should expand workersNewest when reconciliation end is later', async () => {
339
+ await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
340
+ await emitDone(adapter, '2024-01-01T00:00:00.000Z', '2025-08-01T00:00:00.000Z');
341
+ expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
342
+ expect(adapterState.sdkState.workersNewest).toBe('2025-08-01T00:00:00.000Z');
343
+ });
344
+ });
345
+ describe('first sync with absolute dates (no UNBOUNDED)', () => {
346
+ it('should set both boundaries from the extraction range', async () => {
347
+ await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
348
+ expect(adapterState.sdkState.workersOldest).toBe('2025-01-01T00:00:00.000Z');
349
+ expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
350
+ });
351
+ });
352
+ describe('reconciliation after absolute initial sync', () => {
353
+ it('should expand workersOldest backward when reconciliation start is earlier', async () => {
354
+ await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
355
+ await emitDone(adapter, '2024-06-01T00:00:00.000Z', '2025-02-01T00:00:00.000Z');
356
+ expect(adapterState.sdkState.workersOldest).toBe('2024-06-01T00:00:00.000Z');
357
+ expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
358
+ });
359
+ it('should NOT change boundaries when reconciliation is within existing range', async () => {
360
+ await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
361
+ await emitDone(adapter, '2025-01-15T00:00:00.000Z', '2025-02-15T00:00:00.000Z');
362
+ expect(adapterState.sdkState.workersOldest).toBe('2025-01-01T00:00:00.000Z');
363
+ expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
364
+ });
365
+ it('should expand both boundaries when reconciliation exceeds both', async () => {
366
+ await emitDone(adapter, '2025-01-01T00:00:00.000Z', '2025-03-01T00:00:00.000Z');
367
+ await emitDone(adapter, '2024-06-01T00:00:00.000Z', '2025-09-01T00:00:00.000Z');
368
+ expect(adapterState.sdkState.workersOldest).toBe('2024-06-01T00:00:00.000Z');
369
+ expect(adapterState.sdkState.workersNewest).toBe('2025-09-01T00:00:00.000Z');
370
+ });
371
+ });
372
+ describe('multiple forward syncs', () => {
373
+ it('should progressively expand workersNewest while preserving workersOldest', async () => {
374
+ await emitDone(adapter, constants_1.UNBOUNDED_DATE_TIME_VALUE, '2025-06-01T00:00:00.000Z');
375
+ await emitDone(adapter, '2025-06-01T00:00:00.000Z', '2025-07-01T00:00:00.000Z');
376
+ expect(adapterState.sdkState.workersNewest).toBe('2025-07-01T00:00:00.000Z');
377
+ await emitDone(adapter, '2025-07-01T00:00:00.000Z', '2025-08-01T00:00:00.000Z');
378
+ expect(adapterState.sdkState.workersNewest).toBe('2025-08-01T00:00:00.000Z');
379
+ expect(adapterState.sdkState.workersOldest).toBe(constants_1.UNBOUNDED_DATE_TIME_VALUE);
380
+ });
381
+ });
382
+ describe('non-AttachmentExtractionDone events should NOT update boundaries', () => {
383
+ it.each([
384
+ types_1.ExtractorEventType.DataExtractionDone,
385
+ types_1.ExtractorEventType.DataExtractionProgress,
386
+ types_1.ExtractorEventType.MetadataExtractionError,
387
+ types_1.ExtractorEventType.AttachmentExtractionError,
388
+ ])('should not update boundaries on %s', async (eventType) => {
389
+ adapterState.sdkState.workersOldest = '2025-01-01T00:00:00.000Z';
390
+ adapterState.sdkState.workersNewest = '2025-03-01T00:00:00.000Z';
391
+ adapter.event.payload.event_context.extract_from =
392
+ '2024-01-01T00:00:00.000Z';
393
+ adapter.event.payload.event_context.extract_to =
394
+ '2025-12-01T00:00:00.000Z';
395
+ await adapter.emit(eventType, {
396
+ reports: [],
397
+ processed_files: [],
398
+ });
399
+ expect(adapterState.sdkState.workersOldest).toBe('2025-01-01T00:00:00.000Z');
400
+ expect(adapterState.sdkState.workersNewest).toBe('2025-03-01T00:00:00.000Z');
401
+ });
402
+ });
403
+ });
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BaseAdapter = void 0;
4
+ const node_worker_threads_1 = require("node:worker_threads");
5
+ const constants_1 = require("../../common/constants");
6
+ const control_protocol_1 = require("../../common/control-protocol");
7
+ const helpers_1 = require("../../common/helpers");
8
+ const logger_1 = require("../../logger/logger");
9
+ const logger_context_1 = require("../../logger/logger.context");
10
+ const workers_1 = require("../../types/workers");
11
+ const uploader_1 = require("../../uploader/uploader");
12
+ /**
13
+ * BaseAdapter holds the state and behavior shared by both sync modes and owns
14
+ * the `emit` control-protocol flow as a template method. Mode-specific adapters
15
+ * (`ExtractionAdapter`, `LoadingAdapter`) implement the abstract hooks to inject
16
+ * their own pre-emit work and event payload shaping.
17
+ *
18
+ * @typeParam ConnectorState - the connector-owned state shape
19
+ * @typeParam StateClass - the mode's `BaseState` subclass
20
+ */
21
+ class BaseAdapter {
22
+ constructor({ event, adapterState, options, }) {
23
+ this.event = event;
24
+ this.options = options;
25
+ this.adapterState = adapterState;
26
+ this.hasWorkerEmitted = false;
27
+ this.isTimeout = false;
28
+ this.uploader = new uploader_1.Uploader({
29
+ event,
30
+ options,
31
+ });
32
+ }
33
+ get state() {
34
+ return this.adapterState.state;
35
+ }
36
+ set state(value) {
37
+ this.adapterState.state = value;
38
+ }
39
+ async postState() {
40
+ return (0, logger_context_1.runWithSdkLogContext)(async () => {
41
+ await this.adapterState.postState();
42
+ });
43
+ }
44
+ /**
45
+ * Emits an event to the platform.
46
+ *
47
+ * @param newEventType - The event type to be emitted
48
+ * @param data - The data to be sent with the event
49
+ */
50
+ async emit(newEventType, data) {
51
+ return (0, logger_context_1.runWithSdkLogContext)(async () => {
52
+ var _a;
53
+ if (this.hasWorkerEmitted) {
54
+ console.warn(`Trying to emit event with event type: ${newEventType}. Ignoring emit request because it has already been emitted.`);
55
+ return;
56
+ }
57
+ try {
58
+ await this.beforeEmit(newEventType);
59
+ }
60
+ catch (error) {
61
+ console.error('Error while preparing to emit event', error);
62
+ node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
63
+ this.hasWorkerEmitted = true;
64
+ return;
65
+ }
66
+ // We want to save the state every time we emit an event, except for the start and delete events
67
+ if (!constants_1.STATELESS_EVENT_TYPES.includes(this.event.payload.event_type)) {
68
+ console.log(`Saving state before emitting event with event type: ${newEventType}.`);
69
+ try {
70
+ await this.adapterState.postState();
71
+ }
72
+ catch (error) {
73
+ console.error('Error while posting state', error);
74
+ node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
75
+ this.hasWorkerEmitted = true;
76
+ return;
77
+ }
78
+ }
79
+ try {
80
+ // Always prune error messages to make them shorter before emit
81
+ if ((_a = data === null || data === void 0 ? void 0 : data.error) === null || _a === void 0 ? void 0 : _a.message) {
82
+ data.error.message = (0, helpers_1.truncateMessage)(data.error.message);
83
+ }
84
+ await (0, control_protocol_1.emit)({
85
+ eventType: newEventType,
86
+ event: this.event,
87
+ data: Object.assign(Object.assign({}, data), this.buildEmitPayload(newEventType)),
88
+ });
89
+ const message = {
90
+ subject: workers_1.WorkerMessageSubject.WorkerMessageEmitted,
91
+ payload: { eventType: newEventType },
92
+ };
93
+ this.afterEmit(newEventType);
94
+ node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(message);
95
+ this.hasWorkerEmitted = true;
96
+ }
97
+ catch (error) {
98
+ console.error(`Error while emitting event with event type: ${newEventType}.`, (0, logger_1.serializeError)(error));
99
+ node_worker_threads_1.parentPort === null || node_worker_threads_1.parentPort === void 0 ? void 0 : node_worker_threads_1.parentPort.postMessage(workers_1.WorkerMessageSubject.WorkerMessageExit);
100
+ this.hasWorkerEmitted = true;
101
+ }
102
+ });
103
+ }
104
+ }
105
+ exports.BaseAdapter = BaseAdapter;
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=extraction-adapter.artifacts.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extraction-adapter.artifacts.test.d.ts","sourceRoot":"","sources":["../../../src/multithreading/adapters/extraction-adapter.artifacts.test.ts"],"names":[],"mappings":""}
@@ -1,9 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const state_1 = require("../../state/state");
3
+ const constants_1 = require("../../common/constants");
4
+ const extraction_state_1 = require("../../state/extraction-state");
5
+ const jest_setup_1 = require("../../tests/jest.setup");
4
6
  const test_helpers_1 = require("../../tests/test-helpers");
7
+ const test_utils_1 = require("../../common/test-utils");
5
8
  const types_1 = require("../../types");
6
- const worker_adapter_1 = require("./worker-adapter");
9
+ const extraction_adapter_1 = require("./extraction-adapter");
7
10
  // 1. Create a mock function for the method you want to override.
8
11
  const mockUpload = (itemType, objects) => {
9
12
  return {
@@ -49,12 +52,14 @@ describe('Artifact ordering when artifacts overflow batch sizes in repositories'
49
52
  let testAdapter;
50
53
  beforeEach(() => {
51
54
  // Create a fresh adapter instance for this test to avoid mocking conflicts
52
- const mockEvent = (0, test_helpers_1.createEvent)({ eventType: types_1.EventType.ExtractionDataStart });
53
- const mockAdapterState = new state_1.State({
55
+ const mockEvent = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
56
+ payload: { event_type: types_1.EventType.StartExtractingData },
57
+ });
58
+ const mockAdapterState = new extraction_state_1.ExtractionState({
54
59
  event: mockEvent,
55
60
  initialState: { attachments: { completed: false } },
56
61
  });
57
- testAdapter = new worker_adapter_1.WorkerAdapter({
62
+ testAdapter = new extraction_adapter_1.ExtractionAdapter({
58
63
  event: mockEvent,
59
64
  adapterState: mockAdapterState,
60
65
  options: {
@@ -125,3 +130,46 @@ describe('Artifact ordering when artifacts overflow batch sizes in repositories'
125
130
  expect(checkArtifactOrder(artifacts, repos)).toBe(true);
126
131
  });
127
132
  });
133
+ describe('External sync units splitting into artifacts', () => {
134
+ let testAdapter;
135
+ beforeEach(() => {
136
+ const mockEvent = (0, test_utils_1.createMockEvent)(jest_setup_1.mockServer.baseUrl, {
137
+ payload: { event_type: types_1.EventType.StartExtractingExternalSyncUnits },
138
+ });
139
+ const mockAdapterState = new extraction_state_1.ExtractionState({
140
+ event: mockEvent,
141
+ initialState: {},
142
+ });
143
+ testAdapter = new extraction_adapter_1.ExtractionAdapter({
144
+ event: mockEvent,
145
+ adapterState: mockAdapterState,
146
+ });
147
+ });
148
+ it('should split 125k external sync units into 5 artifacts', async () => {
149
+ const BATCH_SIZE = 25000;
150
+ const TOTAL_UNITS = 125000;
151
+ const externalSyncUnits = Array.from({ length: TOTAL_UNITS }, (_, i) => ({
152
+ id: String(i),
153
+ name: `Unit ${i}`,
154
+ description: `Description ${i}`,
155
+ }));
156
+ testAdapter.initializeRepos([
157
+ {
158
+ itemType: constants_1.AirSyncDefaultItemTypes.EXTERNAL_SYNC_UNITS,
159
+ overridenOptions: {
160
+ batchSize: BATCH_SIZE,
161
+ skipConfirmation: true,
162
+ },
163
+ },
164
+ ]);
165
+ const repo = testAdapter.getRepo(constants_1.AirSyncDefaultItemTypes.EXTERNAL_SYNC_UNITS);
166
+ const chunkSize = 10000;
167
+ for (let i = 0; i < TOTAL_UNITS; i += chunkSize) {
168
+ await (repo === null || repo === void 0 ? void 0 : repo.push(externalSyncUnits.slice(i, i + chunkSize)));
169
+ }
170
+ await testAdapter.uploadAllRepos();
171
+ expect(testAdapter.artifacts.length).toBe(TOTAL_UNITS / BATCH_SIZE // 5
172
+ );
173
+ expect(testAdapter.artifacts.every((a) => a.item_type === constants_1.AirSyncDefaultItemTypes.EXTERNAL_SYNC_UNITS)).toBe(true);
174
+ });
175
+ });