@devrev/ts-adaas 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/common/constants.d.ts +5 -0
  2. package/dist/common/constants.js +6 -1
  3. package/dist/common/control-protocol.js +1 -1
  4. package/dist/common/helpers.d.ts +3 -1
  5. package/dist/common/helpers.js +20 -0
  6. package/dist/{tests/from_devrev/loading.test.js → common/helpers.test.js} +1 -1
  7. package/dist/common/install-initial-domain-mapping.js +37 -56
  8. package/dist/common/install-initial-domain-mapping.test.js +215 -0
  9. package/dist/repo/repo.interfaces.d.ts +1 -0
  10. package/dist/state/state.js +21 -12
  11. package/dist/tests/mock-server.d.ts +20 -0
  12. package/dist/tests/mock-server.js +278 -0
  13. package/dist/tests/test-helpers.d.ts +1 -1
  14. package/dist/tests/test-helpers.interfaces.d.ts +3 -1
  15. package/dist/tests/test-helpers.js +27 -28
  16. package/dist/tests/timeout-handling/extraction.d.ts +3 -0
  17. package/dist/tests/timeout-handling/extraction.js +21 -0
  18. package/dist/tests/timeout-handling/timeout-1.d.ts +1 -0
  19. package/dist/tests/timeout-handling/timeout-1.js +14 -0
  20. package/dist/tests/timeout-handling/timeout-1.test.d.ts +1 -0
  21. package/dist/tests/timeout-handling/timeout-1.test.js +45 -0
  22. package/dist/tests/timeout-handling/timeout-2.d.ts +1 -0
  23. package/dist/tests/timeout-handling/timeout-2.js +42 -0
  24. package/dist/tests/timeout-handling/timeout-2.test.d.ts +1 -0
  25. package/dist/tests/timeout-handling/timeout-2.test.js +46 -0
  26. package/dist/tests/timeout-handling/timeout-3a.d.ts +1 -0
  27. package/dist/tests/timeout-handling/timeout-3a.js +37 -0
  28. package/dist/tests/timeout-handling/timeout-3a.test.d.ts +1 -0
  29. package/dist/tests/timeout-handling/timeout-3a.test.js +46 -0
  30. package/dist/tests/timeout-handling/timeout-3b.d.ts +1 -0
  31. package/dist/tests/timeout-handling/timeout-3b.js +38 -0
  32. package/dist/tests/timeout-handling/timeout-3b.test.d.ts +1 -0
  33. package/dist/tests/timeout-handling/timeout-3b.test.js +46 -0
  34. package/dist/types/workers.d.ts +1 -8
  35. package/dist/types/workers.js +0 -1
  36. package/dist/uploader/uploader.js +2 -1
  37. package/dist/uploader/uploader.test.js +3 -0
  38. package/dist/workers/process-task.js +0 -4
  39. package/dist/workers/spawn.d.ts +3 -1
  40. package/dist/workers/spawn.js +47 -39
  41. package/dist/workers/worker-adapter.js +5 -0
  42. package/package.json +3 -2
  43. package/dist/tests/test-worker.js +0 -14
  44. /package/dist/{tests/from_devrev/loading.test.d.ts → common/helpers.test.d.ts} +0 -0
  45. /package/dist/{tests/test-worker.d.ts → common/install-initial-domain-mapping.test.d.ts} +0 -0
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const index_1 = require("../../index");
4
+ (0, index_1.processTask)({
5
+ task: async ({ adapter }) => {
6
+ console.log('Starting intensive computation...');
7
+ let result = 0;
8
+ // Intensive loop - calibrated to exceed 10 seconds but show progress
9
+ for (let i = 1; i <= 30000; i++) {
10
+ for (let num = 2; num <= 2000; num++) {
11
+ let isPrime = true;
12
+ for (let divisor = 2; divisor <= Math.sqrt(num); divisor++) {
13
+ if (num % divisor === 0) {
14
+ isPrime = false;
15
+ break;
16
+ }
17
+ }
18
+ if (isPrime) {
19
+ for (let j = 0; j < 200; j++) {
20
+ result +=
21
+ Math.sin(num * j) * Math.cos(i * j) * Math.sqrt(num + i + j);
22
+ result = Math.abs(result) % 1000000;
23
+ }
24
+ }
25
+ }
26
+ // Log every 500 iterations to see progress before timeout
27
+ if (i % 500 === 0) {
28
+ await new Promise((resolve) => setTimeout(resolve, 50));
29
+ console.log(`timeout-3b iteration ${i}, result: ${result}`);
30
+ }
31
+ }
32
+ console.log(`Final computation result: ${result}`);
33
+ await adapter.emit(index_1.ExtractorEventType.ExtractionDataDone);
34
+ },
35
+ onTimeout: async ({ adapter }) => {
36
+ await adapter.emit(index_1.ExtractorEventType.ExtractionDataProgress);
37
+ },
38
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const extraction_1 = require("../../types/extraction");
7
+ const test_helpers_1 = require("../test-helpers");
8
+ const extraction_2 = __importDefault(require("./extraction"));
9
+ const mock_server_1 = require("../mock-server");
10
+ jest.setTimeout(15000);
11
+ describe('timeout-3b', () => {
12
+ let mockServer;
13
+ beforeAll(async () => {
14
+ mockServer = new mock_server_1.MockServer(3001);
15
+ await mockServer.start();
16
+ });
17
+ afterAll(async () => {
18
+ if (mockServer) {
19
+ await mockServer.stop();
20
+ }
21
+ });
22
+ beforeEach(() => {
23
+ jest.clearAllMocks();
24
+ mockServer.clearRequests();
25
+ });
26
+ it('should emit progress event when soft timeout is reached', async () => {
27
+ const baseUrl = mockServer.getBaseUrl();
28
+ const event = (0, test_helpers_1.createEvent)({
29
+ eventType: extraction_1.EventType.ExtractionDataStart,
30
+ eventContextOverrides: {
31
+ callback_url: `${baseUrl}/internal/airdrop.external-extractor.message`,
32
+ worker_data_url: `${baseUrl}/internal/airdrop.external-worker`,
33
+ },
34
+ executionMetadataOverrides: {
35
+ devrev_endpoint: `${baseUrl}`,
36
+ },
37
+ });
38
+ await (0, extraction_2.default)([event], __dirname + '/timeout-3b');
39
+ const requests = mockServer.getRequests();
40
+ const lastRequest = requests[requests.length - 1];
41
+ // Expect last request to be emission of progress event
42
+ expect(lastRequest.url).toContain('airdrop.external-extractor.message');
43
+ expect(lastRequest.method).toBe('POST');
44
+ expect(lastRequest.body.event_type).toBe('EXTRACTION_DATA_PROGRESS');
45
+ });
46
+ });
@@ -97,7 +97,6 @@ export declare enum WorkerEvent {
97
97
  */
98
98
  export declare enum WorkerMessageSubject {
99
99
  WorkerMessageEmitted = "emit",
100
- WorkerMessageDone = "done",
101
100
  WorkerMessageExit = "exit",
102
101
  WorkerMessageLog = "log"
103
102
  }
@@ -110,12 +109,6 @@ export interface WorkerMessageEmitted {
110
109
  eventType: ExtractorEventType | LoaderEventType;
111
110
  };
112
111
  }
113
- /**
114
- * WorkerMessageDone interface represents the structure of the done worker message.
115
- */
116
- export interface WorkerMessageDone {
117
- subject: WorkerMessageSubject.WorkerMessageDone;
118
- }
119
112
  /**
120
113
  * WorkerMessageExit interface represents the structure of the exit worker message.
121
114
  */
@@ -125,7 +118,7 @@ export interface WorkerMessageExit {
125
118
  /**
126
119
  * WorkerMessage represents the structure of the worker message.
127
120
  */
128
- export type WorkerMessage = WorkerMessageDone | WorkerMessageEmitted | WorkerMessageExit;
121
+ export type WorkerMessage = WorkerMessageEmitted | WorkerMessageExit;
129
122
  /**
130
123
  * WorkerData represents the structure of the worker data object.
131
124
  */
@@ -17,7 +17,6 @@ var WorkerEvent;
17
17
  var WorkerMessageSubject;
18
18
  (function (WorkerMessageSubject) {
19
19
  WorkerMessageSubject["WorkerMessageEmitted"] = "emit";
20
- WorkerMessageSubject["WorkerMessageDone"] = "done";
21
20
  WorkerMessageSubject["WorkerMessageExit"] = "exit";
22
21
  WorkerMessageSubject["WorkerMessageLog"] = "log";
23
22
  })(WorkerMessageSubject || (exports.WorkerMessageSubject = WorkerMessageSubject = {}));
@@ -43,6 +43,7 @@ const zlib_1 = __importDefault(require("zlib"));
43
43
  const js_jsonl_1 = require("js-jsonl");
44
44
  const form_data_1 = __importDefault(require("form-data"));
45
45
  const constants_1 = require("../common/constants");
46
+ const helpers_1 = require("../common/helpers");
46
47
  const logger_1 = require("../logger/logger");
47
48
  class Uploader {
48
49
  constructor({ event, options }) {
@@ -114,7 +115,7 @@ class Uploader {
114
115
  params: {
115
116
  request_id: this.requestId,
116
117
  file_type: fileType,
117
- file_name: filename,
118
+ file_name: (0, helpers_1.truncateFilename)(filename),
118
119
  },
119
120
  });
120
121
  return response.data;
@@ -30,11 +30,14 @@ const getArtifactUploadUrlMockResponse = {
30
30
  describe('Uploader Class Tests', () => {
31
31
  const mockEvent = (0, test_helpers_1.createEvent)({ eventType: types_1.EventType.ExtractionDataStart });
32
32
  let uploader;
33
+ let consoleWarnSpy;
33
34
  beforeEach(() => {
34
35
  uploader = new uploader_1.Uploader({ event: mockEvent });
36
+ consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
35
37
  });
36
38
  afterEach(() => {
37
39
  jest.clearAllMocks();
40
+ consoleWarnSpy.mockRestore();
38
41
  });
39
42
  it('should upload the file to the DevRev platform and return the artifact information', async () => {
40
43
  // Mock successful response from getArtifactUploadUrl
@@ -34,10 +34,6 @@ function processTask({ task, onTimeout, }) {
34
34
  }
35
35
  });
36
36
  await task({ adapter });
37
- const message = {
38
- subject: workers_1.WorkerMessageSubject.WorkerMessageDone,
39
- };
40
- node_worker_threads_1.parentPort.postMessage(message);
41
37
  process.exit(0);
42
38
  }
43
39
  })();
@@ -16,9 +16,11 @@ export declare class Spawn {
16
16
  private alreadyEmitted;
17
17
  private defaultLambdaTimeout;
18
18
  private lambdaTimeout;
19
- private timer;
19
+ private softTimeoutTimer;
20
+ private hardTimeoutTimer;
20
21
  private logger;
21
22
  private resolve;
22
23
  constructor({ event, worker, options, resolve }: SpawnInterface);
24
+ private clearTimeouts;
23
25
  private exitFromMainThread;
24
26
  }
@@ -14,6 +14,7 @@ const helpers_2 = require("../common/helpers");
14
14
  const logger_1 = require("../logger/logger");
15
15
  const workers_1 = require("../types/workers");
16
16
  const create_worker_1 = require("./create-worker");
17
+ const constants_1 = require("../common/constants");
17
18
  function getWorkerPath({ event, connectorWorkerPath, }) {
18
19
  if (connectorWorkerPath)
19
20
  return connectorWorkerPath;
@@ -134,7 +135,7 @@ async function spawn({ event, initialState, workerPath, initialDomainMapping, op
134
135
  }
135
136
  class Spawn {
136
137
  constructor({ event, worker, options, resolve }) {
137
- this.defaultLambdaTimeout = 10 * 60 * 1000; // 10 minutes in milliseconds
138
+ this.defaultLambdaTimeout = constants_1.DEFAULT_LAMBDA_TIMEOUT;
138
139
  this.alreadyEmitted = false;
139
140
  this.event = event;
140
141
  this.logger = new logger_1.Logger({ event, options });
@@ -142,78 +143,85 @@ class Spawn {
142
143
  ? Math.min(options.timeout, this.defaultLambdaTimeout)
143
144
  : this.defaultLambdaTimeout;
144
145
  this.resolve = resolve;
145
- // if lambda timeout is reached, then send a message to the worker to gracefully exit
146
- this.timer = setTimeout(async () => {
147
- this.logger.log('Lambda timeout reached. Sending a message to the worker to gracefully exit.');
146
+ // If soft timeout is reached, send a message to the worker to gracefully exit.
147
+ this.softTimeoutTimer = setTimeout(async () => {
148
+ this.logger.log('SOFT TIMEOUT: Sending a message to the worker to gracefully exit.');
148
149
  if (worker) {
149
150
  worker.postMessage({
150
151
  subject: workers_1.WorkerMessageSubject.WorkerMessageExit,
151
152
  });
152
153
  }
153
154
  else {
154
- console.log("Worker doesn't exist. Exiting from main thread.");
155
+ console.log('Worker does not exist. Exiting from main thread.');
155
156
  await this.exitFromMainThread();
156
157
  }
157
158
  }, this.lambdaTimeout);
158
- // if worker exits with process.exit(code) then we need to clear the timer and exit from main thread
159
+ // If hard timeout is reached, that means the worker did not exit in time. Terminate the worker.
160
+ this.hardTimeoutTimer = setTimeout(async () => {
161
+ this.logger.log('HARD TIMEOUT: Worker did not exit in time. Terminating the worker.');
162
+ if (worker) {
163
+ worker.terminate();
164
+ }
165
+ else {
166
+ console.log('Worker does not exist. Exiting from main thread.');
167
+ await this.exitFromMainThread();
168
+ }
169
+ }, this.lambdaTimeout * constants_1.HARD_TIMEOUT_MULTIPLIER);
170
+ // If worker exits with process.exit(code), clear the timeouts and exit from main thread.
159
171
  worker.on(workers_1.WorkerEvent.WorkerExit, async (code) => {
160
172
  this.logger.info('Worker exited with exit code: ' + code + '.');
161
- if (this.timer) {
162
- clearTimeout(this.timer);
163
- }
173
+ this.clearTimeouts();
164
174
  await this.exitFromMainThread();
165
175
  });
166
176
  worker.on(workers_1.WorkerEvent.WorkerMessage, async (message) => {
167
177
  var _a, _b;
168
- // if worker send a log message, then log it from the main thread with logger
178
+ // Since it is not possible to log from the worker thread, we need to log
179
+ // from the main thread.
169
180
  if ((message === null || message === void 0 ? void 0 : message.subject) === workers_1.WorkerMessageSubject.WorkerMessageLog) {
170
181
  const args = (_a = message.payload) === null || _a === void 0 ? void 0 : _a.args;
171
182
  const level = (_b = message.payload) === null || _b === void 0 ? void 0 : _b.level;
172
183
  this.logger.logFn(args, level);
173
184
  }
174
- // if worker sends a message that it has completed work, then clear the timer and exit from main thread
175
- if ((message === null || message === void 0 ? void 0 : message.subject) === workers_1.WorkerMessageSubject.WorkerMessageDone) {
176
- this.logger.info('Worker has completed with executing the task.');
177
- if (this.timer) {
178
- clearTimeout(this.timer);
179
- }
180
- await this.exitFromMainThread();
181
- }
182
- // if worker sends a message that it has emitted an event, then set alreadyEmitted to true
185
+ // If worker sends a message that it has emitted an event, then set alreadyEmitted to true.
183
186
  if ((message === null || message === void 0 ? void 0 : message.subject) === workers_1.WorkerMessageSubject.WorkerMessageEmitted) {
184
187
  this.logger.info('Worker has emitted message to ADaaS.');
185
188
  this.alreadyEmitted = true;
186
189
  }
187
190
  });
188
191
  }
192
+ clearTimeouts() {
193
+ if (this.softTimeoutTimer) {
194
+ clearTimeout(this.softTimeoutTimer);
195
+ }
196
+ if (this.hardTimeoutTimer) {
197
+ clearTimeout(this.hardTimeoutTimer);
198
+ }
199
+ }
189
200
  async exitFromMainThread() {
190
201
  if (this.alreadyEmitted) {
191
202
  this.resolve();
192
203
  return;
193
204
  }
194
205
  this.alreadyEmitted = true;
195
- const timeoutEventType = (0, helpers_2.getTimeoutErrorEventType)(this.event.payload.event_type);
196
- if (timeoutEventType) {
197
- const { eventType } = timeoutEventType;
198
- try {
199
- await (0, control_protocol_1.emit)({
200
- eventType,
201
- event: this.event,
202
- data: {
203
- error: {
204
- message: 'Worker has not emitted anything. Exited.',
205
- },
206
+ const { eventType } = (0, helpers_2.getTimeoutErrorEventType)(this.event.payload.event_type);
207
+ try {
208
+ await (0, control_protocol_1.emit)({
209
+ eventType,
210
+ event: this.event,
211
+ data: {
212
+ error: {
213
+ message: 'Worker has not emitted anything. Exited.',
206
214
  },
207
- });
208
- this.resolve();
215
+ },
216
+ });
217
+ this.resolve();
218
+ }
219
+ catch (error) {
220
+ if (axios_1.default.isAxiosError(error)) {
221
+ console.error('Error while emitting event', (0, logger_1.serializeAxiosError)(error));
209
222
  }
210
- catch (error) {
211
- if (axios_1.default.isAxiosError(error)) {
212
- console.error('Error while emitting event', (0, logger_1.serializeAxiosError)(error));
213
- }
214
- else {
215
- console.error('Error while emitting event', error);
216
- }
223
+ else {
224
+ console.error('Error while emitting event', error);
217
225
  }
218
226
  }
219
227
  }
@@ -15,6 +15,7 @@ const mappers_1 = require("../mappers/mappers");
15
15
  const uploader_1 = require("../uploader/uploader");
16
16
  const logger_1 = require("../logger/logger");
17
17
  const mappers_interface_1 = require("../mappers/mappers.interface");
18
+ const helpers_2 = require("../common/helpers");
18
19
  function createWorkerAdapter({ event, adapterState, options, }) {
19
20
  return new WorkerAdapter({
20
21
  event,
@@ -91,6 +92,10 @@ class WorkerAdapter {
91
92
  }
92
93
  // Loop through the batches of attachments
93
94
  for (let i = lastProcessedBatchIndex; i < reducedAttachments.length; i++) {
95
+ // Check if we hit timeout
96
+ if (adapter.isTimeout) {
97
+ await (0, helpers_2.sleep)(constants_1.DEFAULT_SLEEP_DELAY_MS);
98
+ }
94
99
  const attachmentsBatch = reducedAttachments[i];
95
100
  // Create a list of promises for parallel processing
96
101
  const promises = [];
package/package.json CHANGED
@@ -1,16 +1,17 @@
1
1
  {
2
2
  "name": "@devrev/ts-adaas",
3
- "version": "1.5.0",
3
+ "version": "1.6.0",
4
4
  "description": "Typescript library containing the ADaaS(AirDrop as a Service) control protocol.",
5
5
  "type": "commonjs",
6
6
  "main": "./dist/index.js",
7
7
  "typings": "./dist/index.d.ts",
8
8
  "scripts": {
9
9
  "build": "tsc -p ./tsconfig.json",
10
+ "prepare": "npm run build",
10
11
  "start": "ts-node src/index.ts",
11
12
  "lint": "eslint .",
12
13
  "lint:fix": "eslint . --fix",
13
- "test": "jest --forceExit --coverage"
14
+ "test": "jest --forceExit --coverage --runInBand"
14
15
  },
15
16
  "repository": {
16
17
  "type": "git",
@@ -1,14 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const process_task_1 = require("../workers/process-task");
4
- const extraction_1 = require("../types/extraction");
5
- (0, process_task_1.processTask)({
6
- task: async ({ adapter }) => {
7
- await adapter.emit(extraction_1.ExtractorEventType.ExtractionExternalSyncUnitsDone, {});
8
- },
9
- onTimeout: async ({ adapter }) => {
10
- await adapter.emit(extraction_1.ExtractorEventType.ExtractionExternalSyncUnitsError, {
11
- error: { message: 'External sync unit failed.' },
12
- });
13
- },
14
- });