@splitsoftware/splitio-commons 3.1.1-rc.7 → 3.1.1-rc.9

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.
@@ -61,9 +61,10 @@ function sdkLifecycleFactory(params, isSharedClient) {
61
61
  }
62
62
  // Stop background jobs
63
63
  syncManager && syncManager.stop();
64
+ // Stop the service API (the auth provider backoff retries specifically) before flushing,
65
+ // so that a pending/never-resolved auth retry doesn't block the flush forever
66
+ serviceApi && serviceApi.stop();
64
67
  return __flush().then(function () {
65
- // Stop the service API (the auth provider backoff retries specifically) after the final flush, so it can still authenticate using the still-valid cached credential
66
- serviceApi && serviceApi.stop();
67
68
  // Cleanup storage
68
69
  return storage.destroy();
69
70
  });
@@ -45,7 +45,7 @@ function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
45
45
  }
46
46
  return {
47
47
  credential: function () {
48
- if (cachedCredential && !isExpired(cachedCredential)) {
48
+ if (stopped || (cachedCredential && !isExpired(cachedCredential))) {
49
49
  return Promise.resolve(cachedCredential);
50
50
  }
51
51
  if (cachedCredential)
@@ -57,9 +57,10 @@ function authProviderFactory(settings, splitHttpClient, telemetryTracker) {
57
57
  },
58
58
  stop: function () {
59
59
  stopped = true;
60
- cachedCredential = undefined;
61
60
  inFlightPromise = undefined;
62
- backoff.reset();
61
+ // Keep any already-cached credential usable (e.g. by a final flush on destroy).
62
+ // Pass it to reset() so a pending retry's promise settles with it immediately.
63
+ backoff.reset(cachedCredential);
63
64
  }
64
65
  };
65
66
  }
@@ -3,7 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Backoff = void 0;
4
4
  var Backoff = /** @class */ (function () {
5
5
  /**
6
- * Schedule function calls with exponential backoff
6
+ * Schedule function calls with exponential backoff.
7
+ * @param cb - The function to call. Must return a promise if used with `scheduleCallAsync`.
7
8
  */
8
9
  function Backoff(cb, baseMillis, maxMillis) {
9
10
  this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
@@ -20,11 +21,11 @@ var Backoff = /** @class */ (function () {
20
21
  var delayInMillis = Math.min(this.baseMillis * Math.pow(2, this.attempts), this.maxMillis);
21
22
  if (this.timeoutID)
22
23
  clearTimeout(this.timeoutID);
24
+ this.attempts++;
23
25
  this.timeoutID = setTimeout(function () {
24
26
  _this.timeoutID = undefined;
25
27
  _this.cb();
26
28
  }, delayInMillis);
27
- this.attempts++;
28
29
  return delayInMillis;
29
30
  };
30
31
  /**
@@ -38,21 +39,28 @@ var Backoff = /** @class */ (function () {
38
39
  clearTimeout(this.timeoutID);
39
40
  this.attempts++;
40
41
  return new Promise(function (resolve, reject) {
42
+ _this.pendingResolve = resolve;
41
43
  _this.timeoutID = setTimeout(function () {
42
44
  _this.timeoutID = undefined;
45
+ _this.pendingResolve = undefined;
43
46
  _this.cb().then(resolve, reject);
44
47
  }, delayInMillis);
45
48
  });
46
49
  };
47
50
  /**
48
- * Reset the backoff attempts
51
+ * Reset the backoff attempts, canceling any scheduled `cb` call. If a `scheduleCallAsync` call is pending,
52
+ * its promise is resolved immediately with provided `value`, instead of invoking `cb` again.
49
53
  */
50
- Backoff.prototype.reset = function () {
54
+ Backoff.prototype.reset = function (value) {
51
55
  this.attempts = 0;
52
56
  if (this.timeoutID) {
53
57
  clearTimeout(this.timeoutID);
54
58
  this.timeoutID = undefined;
55
59
  }
60
+ if (this.pendingResolve) {
61
+ this.pendingResolve(value);
62
+ this.pendingResolve = undefined;
63
+ }
56
64
  };
57
65
  Backoff.DEFAULT_BASE_MILLIS = 1000; // 1 second
58
66
  Backoff.DEFAULT_MAX_MILLIS = 1800000; // 30 minutes
@@ -58,9 +58,10 @@ export function sdkLifecycleFactory(params, isSharedClient) {
58
58
  }
59
59
  // Stop background jobs
60
60
  syncManager && syncManager.stop();
61
+ // Stop the service API (the auth provider backoff retries specifically) before flushing,
62
+ // so that a pending/never-resolved auth retry doesn't block the flush forever
63
+ serviceApi && serviceApi.stop();
61
64
  return __flush().then(function () {
62
- // Stop the service API (the auth provider backoff retries specifically) after the final flush, so it can still authenticate using the still-valid cached credential
63
- serviceApi && serviceApi.stop();
64
65
  // Cleanup storage
65
66
  return storage.destroy();
66
67
  });
@@ -42,7 +42,7 @@ export function authProviderFactory(settings, splitHttpClient, telemetryTracker)
42
42
  }
43
43
  return {
44
44
  credential: function () {
45
- if (cachedCredential && !isExpired(cachedCredential)) {
45
+ if (stopped || (cachedCredential && !isExpired(cachedCredential))) {
46
46
  return Promise.resolve(cachedCredential);
47
47
  }
48
48
  if (cachedCredential)
@@ -54,9 +54,10 @@ export function authProviderFactory(settings, splitHttpClient, telemetryTracker)
54
54
  },
55
55
  stop: function () {
56
56
  stopped = true;
57
- cachedCredential = undefined;
58
57
  inFlightPromise = undefined;
59
- backoff.reset();
58
+ // Keep any already-cached credential usable (e.g. by a final flush on destroy).
59
+ // Pass it to reset() so a pending retry's promise settles with it immediately.
60
+ backoff.reset(cachedCredential);
60
61
  }
61
62
  };
62
63
  }
@@ -1,6 +1,7 @@
1
1
  var Backoff = /** @class */ (function () {
2
2
  /**
3
- * Schedule function calls with exponential backoff
3
+ * Schedule function calls with exponential backoff.
4
+ * @param cb - The function to call. Must return a promise if used with `scheduleCallAsync`.
4
5
  */
5
6
  function Backoff(cb, baseMillis, maxMillis) {
6
7
  this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
@@ -17,11 +18,11 @@ var Backoff = /** @class */ (function () {
17
18
  var delayInMillis = Math.min(this.baseMillis * Math.pow(2, this.attempts), this.maxMillis);
18
19
  if (this.timeoutID)
19
20
  clearTimeout(this.timeoutID);
21
+ this.attempts++;
20
22
  this.timeoutID = setTimeout(function () {
21
23
  _this.timeoutID = undefined;
22
24
  _this.cb();
23
25
  }, delayInMillis);
24
- this.attempts++;
25
26
  return delayInMillis;
26
27
  };
27
28
  /**
@@ -35,21 +36,28 @@ var Backoff = /** @class */ (function () {
35
36
  clearTimeout(this.timeoutID);
36
37
  this.attempts++;
37
38
  return new Promise(function (resolve, reject) {
39
+ _this.pendingResolve = resolve;
38
40
  _this.timeoutID = setTimeout(function () {
39
41
  _this.timeoutID = undefined;
42
+ _this.pendingResolve = undefined;
40
43
  _this.cb().then(resolve, reject);
41
44
  }, delayInMillis);
42
45
  });
43
46
  };
44
47
  /**
45
- * Reset the backoff attempts
48
+ * Reset the backoff attempts, canceling any scheduled `cb` call. If a `scheduleCallAsync` call is pending,
49
+ * its promise is resolved immediately with provided `value`, instead of invoking `cb` again.
46
50
  */
47
- Backoff.prototype.reset = function () {
51
+ Backoff.prototype.reset = function (value) {
48
52
  this.attempts = 0;
49
53
  if (this.timeoutID) {
50
54
  clearTimeout(this.timeoutID);
51
55
  this.timeoutID = undefined;
52
56
  }
57
+ if (this.pendingResolve) {
58
+ this.pendingResolve(value);
59
+ this.pendingResolve = undefined;
60
+ }
53
61
  };
54
62
  Backoff.DEFAULT_BASE_MILLIS = 1000; // 1 second
55
63
  Backoff.DEFAULT_MAX_MILLIS = 1800000; // 30 minutes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "3.1.1-rc.7",
3
+ "version": "3.1.1-rc.9",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -20,8 +20,8 @@
20
20
  "check:lint": "eslint src types --ext .js,.ts",
21
21
  "check:types": "tsc --noEmit",
22
22
  "build": "npm run build:cjs && npm run build:esm",
23
- "build:esm": "rimraf esm && tsc -m es2015 --outDir esm",
24
- "build:cjs": "rimraf cjs && tsc -m CommonJS --outDir cjs",
23
+ "build:esm": "rimraf esm && tsc -p tsconfig.build.json -m es2015 --outDir esm",
24
+ "build:cjs": "rimraf cjs && tsc -p tsconfig.build.json -m CommonJS --outDir cjs",
25
25
  "test": "jest",
26
26
  "test:coverage": "jest --coverage",
27
27
  "all": "npm run check && npm run build && npm run test",
@@ -70,9 +70,11 @@ export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?:
70
70
  // Stop background jobs
71
71
  syncManager && syncManager.stop();
72
72
 
73
+ // Stop the service API (the auth provider backoff retries specifically) before flushing,
74
+ // so that a pending/never-resolved auth retry doesn't block the flush forever
75
+ serviceApi && serviceApi.stop();
76
+
73
77
  return __flush().then(() => {
74
- // Stop the service API (the auth provider backoff retries specifically) after the final flush, so it can still authenticate using the still-valid cached credential
75
- serviceApi && serviceApi.stop();
76
78
  // Cleanup storage
77
79
  return storage.destroy();
78
80
  });
@@ -69,8 +69,8 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
69
69
 
70
70
  return {
71
71
  credential(): Promise<IJwtCredential> {
72
- if (cachedCredential && !isExpired(cachedCredential)) {
73
- return Promise.resolve(cachedCredential);
72
+ if (stopped || (cachedCredential && !isExpired(cachedCredential))) {
73
+ return Promise.resolve(cachedCredential!);
74
74
  }
75
75
 
76
76
  if (cachedCredential) log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
@@ -84,9 +84,10 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
84
84
 
85
85
  stop() {
86
86
  stopped = true;
87
- cachedCredential = undefined;
88
87
  inFlightPromise = undefined;
89
- backoff.reset();
88
+ // Keep any already-cached credential usable (e.g. by a final flush on destroy).
89
+ // Pass it to reset() so a pending retry's promise settles with it immediately.
90
+ backoff.reset(cachedCredential);
90
91
  }
91
92
  };
92
93
  }
@@ -12,9 +12,11 @@ export class Backoff {
12
12
  attempts: number;
13
13
  cb: (...args: any[]) => any;
14
14
  timeoutID: ReturnType<typeof setTimeout> | undefined;
15
+ pendingResolve: ((value: any) => void) | undefined;
15
16
 
16
17
  /**
17
- * Schedule function calls with exponential backoff
18
+ * Schedule function calls with exponential backoff.
19
+ * @param cb - The function to call. Must return a promise if used with `scheduleCallAsync`.
18
20
  */
19
21
  constructor(cb: (...args: any[]) => any, baseMillis?: number, maxMillis?: number) {
20
22
  this.baseMillis = Backoff.__TEST__BASE_MILLIS || baseMillis || Backoff.DEFAULT_BASE_MILLIS;
@@ -31,11 +33,12 @@ export class Backoff {
31
33
  let delayInMillis = Math.min(this.baseMillis * Math.pow(2, this.attempts), this.maxMillis);
32
34
 
33
35
  if (this.timeoutID) clearTimeout(this.timeoutID);
36
+ this.attempts++;
37
+
34
38
  this.timeoutID = setTimeout(() => {
35
39
  this.timeoutID = undefined;
36
40
  this.cb();
37
41
  }, delayInMillis);
38
- this.attempts++;
39
42
 
40
43
  return delayInMillis;
41
44
  }
@@ -51,22 +54,29 @@ export class Backoff {
51
54
  this.attempts++;
52
55
 
53
56
  return new Promise<T>((resolve, reject) => {
57
+ this.pendingResolve = resolve;
54
58
  this.timeoutID = setTimeout(() => {
55
59
  this.timeoutID = undefined;
60
+ this.pendingResolve = undefined;
56
61
  this.cb().then(resolve, reject);
57
62
  }, delayInMillis);
58
63
  });
59
64
  }
60
65
 
61
66
  /**
62
- * Reset the backoff attempts
67
+ * Reset the backoff attempts, canceling any scheduled `cb` call. If a `scheduleCallAsync` call is pending,
68
+ * its promise is resolved immediately with provided `value`, instead of invoking `cb` again.
63
69
  */
64
- reset() {
70
+ reset<T>(value?: T) {
65
71
  this.attempts = 0;
66
72
  if (this.timeoutID) {
67
73
  clearTimeout(this.timeoutID);
68
74
  this.timeoutID = undefined;
69
75
  }
76
+ if (this.pendingResolve) {
77
+ this.pendingResolve(value);
78
+ this.pendingResolve = undefined;
79
+ }
70
80
  }
71
81
 
72
82
  }