@splitsoftware/splitio-commons 3.1.1-rc.1 → 3.1.1-rc.11

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 (72) hide show
  1. package/CHANGES.txt +12 -1
  2. package/cjs/evaluator/index.js +11 -6
  3. package/cjs/presets/serverSide.js +3 -3
  4. package/cjs/sdkClient/client.js +3 -5
  5. package/cjs/sdkClient/sdkLifecycle.js +2 -0
  6. package/cjs/services/authProvider.js +23 -10
  7. package/cjs/services/secureSplitHttpClient.js +10 -13
  8. package/cjs/services/serviceApi.js +23 -21
  9. package/cjs/services/splitApi.js +10 -9
  10. package/cjs/storages/inRedis/RedisAdapter.js +8 -5
  11. package/cjs/sync/polling/pollingManagerCS.js +84 -79
  12. package/cjs/sync/polling/pollingManagerSS.js +43 -35
  13. package/cjs/sync/polling/updaters/definitionChangesUpdater.js +2 -2
  14. package/cjs/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  15. package/cjs/sync/streaming/AuthClient/index.js +13 -14
  16. package/cjs/sync/streaming/SSEHandler/index.js +1 -0
  17. package/cjs/sync/streaming/constants.js +2 -1
  18. package/cjs/sync/streaming/pushManager.js +5 -10
  19. package/cjs/sync/submitters/impressionsSubmitter.js +1 -1
  20. package/cjs/sync/syncManagerOnline.js +2 -7
  21. package/cjs/utils/Backoff.js +16 -4
  22. package/esm/evaluator/index.js +11 -6
  23. package/esm/presets/serverSide.js +3 -3
  24. package/esm/sdkClient/client.js +3 -5
  25. package/esm/sdkClient/sdkLifecycle.js +2 -0
  26. package/esm/services/authProvider.js +24 -11
  27. package/esm/services/secureSplitHttpClient.js +10 -13
  28. package/esm/services/serviceApi.js +24 -22
  29. package/esm/services/splitApi.js +10 -9
  30. package/esm/storages/inRedis/RedisAdapter.js +8 -5
  31. package/esm/sync/polling/pollingManagerCS.js +84 -79
  32. package/esm/sync/polling/pollingManagerSS.js +43 -35
  33. package/esm/sync/polling/updaters/definitionChangesUpdater.js +3 -3
  34. package/esm/sync/polling/updaters/segmentChangesUpdater.js +1 -1
  35. package/esm/sync/streaming/AuthClient/index.js +11 -11
  36. package/esm/sync/streaming/SSEHandler/index.js +2 -1
  37. package/esm/sync/streaming/constants.js +1 -0
  38. package/esm/sync/streaming/pushManager.js +6 -11
  39. package/esm/sync/submitters/impressionsSubmitter.js +1 -1
  40. package/esm/sync/syncManagerOnline.js +2 -7
  41. package/esm/utils/Backoff.js +16 -4
  42. package/package.json +6 -7
  43. package/src/dtos/types.ts +1 -1
  44. package/src/evaluator/index.ts +11 -5
  45. package/src/evaluator/types.ts +1 -0
  46. package/src/presets/serverSide.ts +3 -3
  47. package/src/sdkClient/client.ts +3 -4
  48. package/src/sdkClient/sdkLifecycle.ts +3 -0
  49. package/src/services/authProvider.ts +41 -19
  50. package/src/services/secureSplitHttpClient.ts +10 -18
  51. package/src/services/serviceApi.ts +27 -24
  52. package/src/services/splitApi.ts +10 -9
  53. package/src/services/types.ts +5 -4
  54. package/src/storages/inRedis/RedisAdapter.ts +8 -5
  55. package/src/sync/polling/pollingManagerCS.ts +80 -78
  56. package/src/sync/polling/pollingManagerSS.ts +49 -45
  57. package/src/sync/polling/updaters/definitionChangesUpdater.ts +3 -3
  58. package/src/sync/polling/updaters/segmentChangesUpdater.ts +1 -1
  59. package/src/sync/streaming/AuthClient/index.ts +14 -15
  60. package/src/sync/streaming/AuthClient/types.ts +9 -13
  61. package/src/sync/streaming/SSEClient/index.ts +2 -2
  62. package/src/sync/streaming/SSEClient/types.ts +2 -2
  63. package/src/sync/streaming/SSEHandler/index.ts +2 -1
  64. package/src/sync/streaming/SSEHandler/types.ts +2 -2
  65. package/src/sync/streaming/constants.ts +1 -0
  66. package/src/sync/streaming/pushManager.ts +32 -42
  67. package/src/sync/streaming/types.ts +3 -2
  68. package/src/sync/submitters/impressionsSubmitter.ts +3 -3
  69. package/src/sync/submitters/types.ts +2 -2
  70. package/src/sync/syncManagerOnline.ts +2 -8
  71. package/src/utils/Backoff.ts +16 -4
  72. package/types/splitio.d.ts +5 -4
@@ -1,12 +1,12 @@
1
1
  // Dynamically require ioredis to prevent strict TypeScript binding
2
- // and handle module export differences between v4 and v5.
2
+ // and handle module export differences between v4 and later major versions.
3
3
  let RedisConstructor: any;
4
4
  try {
5
5
  const ioredisLib = require('ioredis');
6
6
  RedisConstructor = ioredisLib.default || ioredisLib;
7
7
  } catch (e) {
8
8
  // If we reach here, the peer dependency is missing
9
- throw new Error('ioredis is missing. Please install ioredis v4 or v5.');
9
+ throw new Error('ioredis is missing. Please install ioredis v4, v5 or v6.');
10
10
  }
11
11
 
12
12
  import { ILogger } from '../../logger/types';
@@ -31,9 +31,12 @@ const DEFAULT_LIBRARY_OPTIONS = {
31
31
  enableOfflineQueue: false,
32
32
  connectTimeout: DEFAULT_OPTIONS.connectionTimeout,
33
33
  lazyConnect: false,
34
- // CRITICAL: v5 defaults this to 0 (disabled), which breaks dynamic clusters.
35
- // v4 defaulted to 5000. We explicitly set it here to ensure v5 works like v4.
34
+ // CRITICAL: v5 and v6 default this to 0 (disabled), which breaks dynamic clusters.
35
+ // v4 defaulted to 5000. We explicitly set it here to ensure v5 and v6 work like v4.
36
36
  slotsRefreshInterval: 5000,
37
+ // v6 defaults to RESP3 (`protocol: 3`). The SDK doesn't use any RESP3 feature,
38
+ // so we stick to RESP2 to keep the v4 and v5 behavior. Ignored by v4 and v5.
39
+ protocol: 2,
37
40
  };
38
41
 
39
42
  interface IRedisCommand {
@@ -45,7 +48,7 @@ interface IRedisCommand {
45
48
 
46
49
  /**
47
50
  * Redis adapter on top of the library of choice (written with ioredis) for some extra control.
48
- * Refactored to use Composition instead of Inheritance to support both v4 and v5.
51
+ * Refactored to use Composition instead of Inheritance to support v4, v5 and v6.
49
52
  */
50
53
  export class RedisAdapter {
51
54
  // eslint-disable-next-line no-undef -- Index signature to allow proxying dynamic ioredis methods without TS errors
@@ -10,110 +10,112 @@ import { POLLING_SMART_PAUSING, POLLING_START, POLLING_STOP } from '../../logger
10
10
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
11
11
  import { usesSegmentsSync } from '../../storages/AbstractDefinitionsCacheSync';
12
12
  import { SdkUpdateMetadata } from '../../../types/splitio';
13
- import { IDefinitionChangesFetcher } from './fetchers/types';
13
+ import { splitChangesFetcherFactory } from './fetchers/splitChangesFetcher';
14
14
 
15
15
  /**
16
16
  * Expose start / stop mechanism for polling data from services.
17
17
  * For client-side API with multiple clients.
18
18
  */
19
19
  export function pollingManagerCSFactory(
20
- params: ISdkFactoryContextSync,
21
- definitionChangesFetcher: IDefinitionChangesFetcher
22
- ): IPollingManagerCS {
20
+ definitionFetcherFactory = splitChangesFetcherFactory,
21
+ ): (params: ISdkFactoryContextSync) => IPollingManagerCS {
23
22
 
24
- const { serviceApi, storage, readiness, settings } = params;
25
- const log = settings.log;
23
+ return function (params: ISdkFactoryContextSync): IPollingManagerCS {
24
+ const { serviceApi, storage, readiness, settings } = params;
25
+ const log = settings.log;
26
26
 
27
- const definitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings);
27
+ const definitionChangesFetcher = definitionFetcherFactory(params);
28
+ const definitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings);
28
29
 
29
- // Map of matching keys to their corresponding MySegmentsSyncTask.
30
- const mySegmentsSyncTasks: Record<string, IMySegmentsSyncTask> = {};
30
+ // Map of matching keys to their corresponding MySegmentsSyncTask.
31
+ const mySegmentsSyncTasks: Record<string, IMySegmentsSyncTask> = {};
31
32
 
32
- const matchingKey = getMatching(settings.core.key);
33
- const mySegmentsSyncTask = add(matchingKey, readiness, storage);
33
+ const matchingKey = getMatching(settings.core.key);
34
+ const mySegmentsSyncTask = add(matchingKey, readiness, storage);
34
35
 
35
- function startMySegmentsSyncTasks() {
36
- forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
37
- mySegmentsSyncTask.start();
38
- });
39
- }
36
+ function startMySegmentsSyncTasks() {
37
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
38
+ mySegmentsSyncTask.start();
39
+ });
40
+ }
40
41
 
41
- function stopMySegmentsSyncTasks() {
42
- forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
43
- if (mySegmentsSyncTask.isRunning()) mySegmentsSyncTask.stop();
44
- });
45
- }
46
-
47
- // smart pausing
48
- readiness.definitions.on(SDK_DEFINITIONS_ARRIVED, () => {
49
- if (!definitionsSyncTask.isRunning()) return; // noop if not doing polling
50
- const usingSegments = usesSegmentsSync(storage);
51
- if (usingSegments !== mySegmentsSyncTask.isRunning()) {
52
- log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
53
- if (usingSegments) {
54
- startMySegmentsSyncTasks();
55
- } else {
56
- stopMySegmentsSyncTasks();
57
- }
42
+ function stopMySegmentsSyncTasks() {
43
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
44
+ if (mySegmentsSyncTask.isRunning()) mySegmentsSyncTask.stop();
45
+ });
58
46
  }
59
- });
60
47
 
61
- function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
62
- const mySegmentsSyncTask = mySegmentsSyncTaskFactory(serviceApi.fetchMemberships, storage, readiness, settings, matchingKey);
48
+ // smart pausing
49
+ readiness.definitions.on(SDK_DEFINITIONS_ARRIVED, () => {
50
+ if (!definitionsSyncTask.isRunning()) return; // noop if not doing polling
51
+ const usingSegments = usesSegmentsSync(storage);
52
+ if (usingSegments !== mySegmentsSyncTask.isRunning()) {
53
+ log.info(POLLING_SMART_PAUSING, [usingSegments ? 'ON' : 'OFF']);
54
+ if (usingSegments) {
55
+ startMySegmentsSyncTasks();
56
+ } else {
57
+ stopMySegmentsSyncTasks();
58
+ }
59
+ }
60
+ });
61
+
62
+ function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
63
+ const mySegmentsSyncTask = mySegmentsSyncTaskFactory(serviceApi.fetchMemberships, storage, readiness, settings, matchingKey);
63
64
 
64
- // smart ready
65
- function smartReady(metadata: SdkUpdateMetadata) {
66
- if (!readiness.isReady() && !usesSegmentsSync(storage)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED, metadata);
65
+ // smart ready
66
+ function smartReady(metadata: SdkUpdateMetadata) {
67
+ if (!readiness.isReady() && !usesSegmentsSync(storage)) readiness.segments.emit(SDK_SEGMENTS_ARRIVED, metadata);
68
+ }
69
+ if (!usesSegmentsSync(storage)) setTimeout(smartReady, 0);
70
+ else readiness.definitions.once(SDK_DEFINITIONS_ARRIVED, smartReady);
71
+
72
+ mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
73
+ return mySegmentsSyncTask;
67
74
  }
68
- if (!usesSegmentsSync(storage)) setTimeout(smartReady, 0);
69
- else readiness.definitions.once(SDK_DEFINITIONS_ARRIVED, smartReady);
70
75
 
71
- mySegmentsSyncTasks[matchingKey] = mySegmentsSyncTask;
72
- return mySegmentsSyncTask;
73
- }
76
+ return {
77
+ definitionsSyncTask,
78
+ segmentsSyncTask: mySegmentsSyncTask,
74
79
 
75
- return {
76
- definitionsSyncTask,
77
- segmentsSyncTask: mySegmentsSyncTask,
80
+ // Start periodic fetching (polling)
81
+ start() {
82
+ log.info(POLLING_START);
78
83
 
79
- // Start periodic fetching (polling)
80
- start() {
81
- log.info(POLLING_START);
84
+ definitionsSyncTask.start();
85
+ if (usesSegmentsSync(storage)) startMySegmentsSyncTasks();
86
+ },
82
87
 
83
- definitionsSyncTask.start();
84
- if (usesSegmentsSync(storage)) startMySegmentsSyncTasks();
85
- },
88
+ // Stop periodic fetching (polling)
89
+ stop() {
90
+ log.info(POLLING_STOP);
86
91
 
87
- // Stop periodic fetching (polling)
88
- stop() {
89
- log.info(POLLING_STOP);
92
+ if (definitionsSyncTask.isRunning()) definitionsSyncTask.stop();
93
+ stopMySegmentsSyncTasks();
94
+ },
90
95
 
91
- if (definitionsSyncTask.isRunning()) definitionsSyncTask.stop();
92
- stopMySegmentsSyncTasks();
93
- },
96
+ // Used by SyncManager to know if running in polling mode.
97
+ isRunning: definitionsSyncTask.isRunning,
94
98
 
95
- // Used by SyncManager to know if running in polling mode.
96
- isRunning: definitionsSyncTask.isRunning,
99
+ // fetch definitions and segments
100
+ syncAll() {
101
+ const promises = [definitionsSyncTask.execute()];
102
+ forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
103
+ promises.push(mySegmentsSyncTask.execute());
104
+ });
105
+ return Promise.all(promises);
106
+ },
97
107
 
98
- // fetch definitions and segments
99
- syncAll() {
100
- const promises = [definitionsSyncTask.execute()];
101
- forOwn(mySegmentsSyncTasks, (mySegmentsSyncTask) => {
102
- promises.push(mySegmentsSyncTask.execute());
103
- });
104
- return Promise.all(promises);
105
- },
108
+ // Support for handling mySegments sync of multiple clients
109
+ add,
106
110
 
107
- // Support for handling mySegments sync of multiple clients
108
- add,
111
+ remove(matchingKey: string) {
112
+ delete mySegmentsSyncTasks[matchingKey];
113
+ },
109
114
 
110
- remove(matchingKey: string) {
111
- delete mySegmentsSyncTasks[matchingKey];
112
- },
115
+ get(matchingKey: string) {
116
+ return mySegmentsSyncTasks[matchingKey];
117
+ }
118
+ };
113
119
 
114
- get(matchingKey: string) {
115
- return mySegmentsSyncTasks[matchingKey];
116
- }
117
120
  };
118
-
119
121
  }
@@ -3,57 +3,61 @@ import { segmentsSyncTaskFactory } from './syncTasks/segmentsSyncTask';
3
3
  import { IPollingManager, ISegmentsSyncTask, IDefinitionsSyncTask } from './types';
4
4
  import { POLLING_START, POLLING_STOP, LOG_PREFIX_SYNC_POLLING } from '../../logger/constants';
5
5
  import { ISdkFactoryContextSync } from '../../sdkFactory/types';
6
- import { IDefinitionChangesFetcher, ISegmentChangesFetcher } from './fetchers/types';
6
+ import { segmentChangesFetcherFactory } from './fetchers/segmentChangesFetcher';
7
+ import { splitChangesFetcherFactory } from './fetchers/splitChangesFetcher';
7
8
 
8
9
  /**
9
10
  * Expose start / stop mechanism for pulling data from services.
10
11
  */
11
12
  export function pollingManagerSSFactory(
12
- params: ISdkFactoryContextSync,
13
- definitionChangesFetcher: IDefinitionChangesFetcher,
14
- segmentChangesFetcher: ISegmentChangesFetcher
15
- ): IPollingManager {
16
-
17
- const { storage, readiness, settings } = params;
18
- const log = settings.log;
19
-
20
- const segmentsSyncTask: ISegmentsSyncTask = segmentsSyncTaskFactory(segmentChangesFetcher, storage, readiness, settings);
21
- const definitionsSyncTask: IDefinitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings, segmentsSyncTask);
22
-
23
- return {
24
- definitionsSyncTask,
25
- segmentsSyncTask,
26
-
27
- // Start periodic fetching (polling)
28
- start() {
29
- log.info(POLLING_START);
30
- log.debug(LOG_PREFIX_SYNC_POLLING + `${definitionChangesFetcher.type} will be refreshed each ${settings.scheduler.featuresRefreshRate} millis`);
31
- log.debug(LOG_PREFIX_SYNC_POLLING + `segments will be refreshed each ${settings.scheduler.segmentsRefreshRate} millis`);
32
-
33
- const startingUp = definitionsSyncTask.start();
34
- if (startingUp) {
35
- startingUp.then(() => {
36
- if (definitionsSyncTask.isRunning()) segmentsSyncTask.start();
13
+ definitionFetcherFactory = splitChangesFetcherFactory,
14
+ segmentFetcherFactory = (params: ISdkFactoryContextSync) => segmentChangesFetcherFactory(params.serviceApi.fetchSegmentChanges)
15
+ ): (params: ISdkFactoryContextSync) => IPollingManager {
16
+
17
+ return function (params: ISdkFactoryContextSync): IPollingManager {
18
+ const { storage, readiness, settings } = params;
19
+ const log = settings.log;
20
+
21
+ const definitionChangesFetcher = definitionFetcherFactory(params);
22
+ const segmentChangesFetcher = segmentFetcherFactory(params);
23
+ const segmentsSyncTask: ISegmentsSyncTask = segmentsSyncTaskFactory(segmentChangesFetcher, storage, readiness, settings);
24
+ const definitionsSyncTask: IDefinitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings, segmentsSyncTask);
25
+
26
+ return {
27
+ definitionsSyncTask,
28
+ segmentsSyncTask,
29
+
30
+ // Start periodic fetching (polling)
31
+ start() {
32
+ log.info(POLLING_START);
33
+ log.debug(LOG_PREFIX_SYNC_POLLING + `${definitionChangesFetcher.type} will be refreshed each ${settings.scheduler.featuresRefreshRate} millis`);
34
+ log.debug(LOG_PREFIX_SYNC_POLLING + `segments will be refreshed each ${settings.scheduler.segmentsRefreshRate} millis`);
35
+
36
+ const startingUp = definitionsSyncTask.start();
37
+ if (startingUp) {
38
+ startingUp.then(() => {
39
+ if (definitionsSyncTask.isRunning()) segmentsSyncTask.start();
40
+ });
41
+ }
42
+ },
43
+
44
+ // Stop periodic fetching (polling)
45
+ stop() {
46
+ log.info(POLLING_STOP);
47
+
48
+ if (definitionsSyncTask.isRunning()) definitionsSyncTask.stop();
49
+ if (segmentsSyncTask.isRunning()) segmentsSyncTask.stop();
50
+ },
51
+
52
+ // Used by SyncManager to know if running in polling mode.
53
+ isRunning: definitionsSyncTask.isRunning,
54
+
55
+ syncAll() {
56
+ // fetch definitions and segments. There is no need to catch this promise (`DefinitionChangesUpdater` is always resolved with a boolean value)
57
+ return definitionsSyncTask.execute().then(() => {
58
+ return segmentsSyncTask.execute();
37
59
  });
38
60
  }
39
- },
40
-
41
- // Stop periodic fetching (polling)
42
- stop() {
43
- log.info(POLLING_STOP);
44
-
45
- if (definitionsSyncTask.isRunning()) definitionsSyncTask.stop();
46
- if (segmentsSyncTask.isRunning()) segmentsSyncTask.stop();
47
- },
48
-
49
- // Used by SyncManager to know if running in polling mode.
50
- isRunning: definitionsSyncTask.isRunning,
51
-
52
- syncAll() {
53
- // fetch definitions and segments. There is no need to catch this promise (`DefinitionChangesUpdater` is always resolved with a boolean value)
54
- return definitionsSyncTask.execute().then(() => {
55
- return segmentsSyncTask.execute();
56
- });
57
- }
61
+ };
58
62
  };
59
63
  }
@@ -9,7 +9,7 @@ import { SYNC_FETCH, SYNC_UPDATE, SYNC_FETCH_FAILS, SYNC_FETCH_RETRY } from '../
9
9
  import { startsWith } from '../../../utils/lang';
10
10
  import { IN_RULE_BASED_SEGMENT, IN_SEGMENT, RULE_BASED_SEGMENT, STANDARD_SEGMENT } from '../../../utils/constants';
11
11
  import { setToArray } from '../../../utils/lang/sets';
12
- import { SPLIT_UPDATE } from '../../streaming/constants';
12
+ import { RB_SEGMENT_UPDATE } from '../../streaming/constants';
13
13
  import { SdkUpdateMetadata } from '../../../../types/splitio';
14
14
  import { ISplit } from '../fetchers/splitChangesFetcher';
15
15
  import { ISegmentsSyncTask } from '../types';
@@ -154,14 +154,14 @@ export function definitionChangesUpdaterFactory(
154
154
 
155
155
  return Promise.resolve(
156
156
  instantUpdate ?
157
- instantUpdate.type === SPLIT_UPDATE ?
157
+ instantUpdate.type === RB_SEGMENT_UPDATE ?
158
+ { rbs: convertInstantUpdateToDefinitionChanges(instantUpdate) as IDefinitionChangesResponse['rbs'] } :
158
159
  // IFFU edge case: a change to definition that adds an IN_RULE_BASED_SEGMENT matcher that is not present yet
159
160
  Promise.resolve(rbSegments.contains(parseSegments(instantUpdate.payload, IN_RULE_BASED_SEGMENT))).then((contains) => {
160
161
  return contains ?
161
162
  { d: convertInstantUpdateToDefinitionChanges(instantUpdate) as IDefinitionChangesResponse['d'] } :
162
163
  definitionChangesFetcher(since, noCache, till, rbSince, _promiseDecorator);
163
164
  }) :
164
- { rbs: convertInstantUpdateToDefinitionChanges(instantUpdate) as IDefinitionChangesResponse['rbs'] } :
165
165
  definitionChangesFetcher(since, noCache, till, rbSince, _promiseDecorator)
166
166
  )
167
167
  .then((definitionChanges: IDefinitionChangesResponse) => {
@@ -47,7 +47,7 @@ export function segmentChangesUpdaterFactory(
47
47
  false :
48
48
  segmentChangesFetcher(since || -1, segmentName, noCache, till, _promiseDecorator).then((changes) => {
49
49
  return Promise.all(changes.map(x => {
50
- log.debug(`${LOG_PREFIX_SYNC}Processing ${segmentName} with till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
50
+ log.debug(`${LOG_PREFIX_SYNC}Processing ${segmentName} with since = ${x.since} and till = ${x.till}. Added: ${x.added.length}. Removed: ${x.removed.length}`);
51
51
  return segments.update(segmentName, x.added, x.removed, x.till);
52
52
  })).then((updates) => {
53
53
  return updates.some(update => update);
@@ -1,22 +1,20 @@
1
- import { IFetchAuth } from '../../../services/types';
2
- import { IAuthenticate, IJwtCredentialV2 } from './types';
1
+ import { IJwtCredential, IFetchAuth } from './types';
3
2
  import { objectAssign } from '../../../utils/lang/objectAssign';
4
- import { encodeToBase64 } from '../../../utils/base64';
5
3
  import { decodeJWTtoken } from '../../../utils/jwt';
6
- import { hash } from '../../../utils/murmur3/murmur3';
4
+ import { IResponse } from '../../../services/types';
7
5
 
8
6
  /**
9
7
  * Factory of authentication function.
10
8
  *
11
- * @param fetchAuth - `ServiceApi.fetchAuth` endpoint
9
+ * @param fetchAuth - /auth endpoint
12
10
  */
13
- export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
11
+ export function fetchAuthFactory(fetchAuth: (userKeys?: string[]) => Promise<IResponse>): IFetchAuth {
14
12
 
15
13
  /**
16
14
  * Run authentication requests to Auth Server, and returns a promise that resolves with the decoded JTW token.
17
15
  * @param userKeys - set of user Keys to track membership updates. It is undefined for server-side API.
18
16
  */
19
- return function authenticate(userKeys?: string[]): Promise<IJwtCredentialV2> {
17
+ return function authenticate(userKeys?: string[]): Promise<IJwtCredential> {
20
18
  return fetchAuth(userKeys)
21
19
  .then(resp => resp.json())
22
20
  .then(json => {
@@ -24,19 +22,20 @@ export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
24
22
  const decodedToken = decodeJWTtoken(json.token);
25
23
  if (typeof decodedToken.iat !== 'number' || typeof decodedToken.exp !== 'number') throw new Error('token properties "issuedAt" (iat) or "expiration" (exp) are missing or invalid');
26
24
  const channels = JSON.parse(decodedToken['x-ably-capability']);
27
- return objectAssign({
25
+ const credential = objectAssign({
28
26
  decodedToken,
29
27
  channels
30
28
  }, json);
29
+ // The `/api/v3/auth` endpoint nests the streaming settings under `config.streaming`. Normalize
30
+ // them into the flat `pushEnabled`/`connDelay` properties used by the PushManager.
31
+ const streaming = credential.config && credential.config.streaming;
32
+ if (streaming) {
33
+ credential.pushEnabled = streaming.enabled;
34
+ credential.connDelay = streaming.delay;
35
+ }
36
+ return credential;
31
37
  }
32
38
  return json;
33
39
  });
34
40
  };
35
41
  }
36
-
37
- /**
38
- * Returns the hash of a given user key
39
- */
40
- export function hashUserKey(userKey: string): string {
41
- return encodeToBase64(hash(userKey, 0).toString());
42
- }
@@ -1,23 +1,19 @@
1
1
  import { IDecodedJWTToken } from '../../../utils/jwt/types';
2
2
 
3
- export type IJwtCredentialV2 = {
4
- pushEnabled: boolean
5
- token: string // empty string ("") when `"pushEnabled": false`
6
- decodedToken: IDecodedJWTToken
7
- channels: { [channel: string]: string[] }
8
- connDelay?: number
9
- }
10
-
11
- export type IJwtCredentialV3 = {
12
- token: string
3
+ export type IJwtCredential = {
4
+ token: string; // empty string ("") when `"pushEnabled": false`
13
5
  decodedToken: IDecodedJWTToken
14
6
  channels: { [channel: string]: string[] }
7
+ // /api/v2/auth fields
8
+ pushEnabled?: boolean | null;
9
+ connDelay?: number | null;
10
+ // /api/v3/auth fields
15
11
  config?: {
16
12
  streaming?: {
17
- delay?: number
18
- enabled?: boolean
13
+ delay?: number | null;
14
+ enabled?: boolean | null;
19
15
  } | null;
20
16
  } | null;
21
17
  }
22
18
 
23
- export type IAuthenticate = (userKeys?: string[]) => Promise<IJwtCredentialV2>
19
+ export type IFetchAuth = (userKeys?: string[]) => Promise<IJwtCredential>
@@ -5,7 +5,7 @@ import { ISettings } from '../../../types';
5
5
  import { checkIfServerSide } from '../../../utils/key';
6
6
  import { isString } from '../../../utils/lang';
7
7
  import { objectAssign } from '../../../utils/lang/objectAssign';
8
- import { IJwtCredentialV2 } from '../AuthClient/types';
8
+ import { IJwtCredential } from '../AuthClient/types';
9
9
  import { ISSEClient, ISseEventHandler } from './types';
10
10
 
11
11
  const ABLY_API_VERSION = '1.1';
@@ -66,7 +66,7 @@ export class SSEClient implements ISSEClient {
66
66
  /**
67
67
  * Open the connection with a given authToken
68
68
  */
69
- open(authToken: IJwtCredentialV2) {
69
+ open(authToken: IJwtCredential) {
70
70
  this.close(); // it closes connection if previously opened
71
71
 
72
72
  const channelsQueryParam = Object.keys(authToken.channels).map((channel) => {
@@ -1,4 +1,4 @@
1
- import { IJwtCredentialV2 } from '../AuthClient/types';
1
+ import { IJwtCredential } from '../AuthClient/types';
2
2
 
3
3
  export interface ISseEventHandler {
4
4
  handleError: (ev: Event) => any;
@@ -7,7 +7,7 @@ export interface ISseEventHandler {
7
7
  }
8
8
 
9
9
  export interface ISSEClient {
10
- open(authToken: IJwtCredentialV2): void,
10
+ open(authToken: IJwtCredential): void,
11
11
  close(): void,
12
12
  setEventHandler(handler: ISseEventHandler): void
13
13
  }
@@ -1,6 +1,6 @@
1
1
  import { errorParser, messageParser } from './NotificationParser';
2
2
  import { notificationKeeperFactory } from './NotificationKeeper';
3
- import { PUSH_RETRYABLE_ERROR, PUSH_NON_RETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
3
+ import { PUSH_RETRYABLE_ERROR, PUSH_NON_RETRYABLE_ERROR, OCCUPANCY, CONTROL, SEGMENT_UPDATE, SPLIT_KILL, SPLIT_UPDATE, CONFIG_UPDATE, MEMBERSHIPS_MS_UPDATE, MEMBERSHIPS_LS_UPDATE, RB_SEGMENT_UPDATE } from '../constants';
4
4
  import { IPushEventEmitter } from '../types';
5
5
  import { ISseEventHandler } from '../SSEClient/types';
6
6
  import { INotificationError, INotificationMessage } from './types';
@@ -80,6 +80,7 @@ export function SSEHandlerFactory(log: ILogger, pushEmitter: IPushEventEmitter,
80
80
  switch (parsedData.type) {
81
81
  /* update events */
82
82
  case SPLIT_UPDATE:
83
+ case CONFIG_UPDATE:
83
84
  case SEGMENT_UPDATE:
84
85
  case MEMBERSHIPS_MS_UPDATE:
85
86
  case MEMBERSHIPS_LS_UPDATE:
@@ -1,5 +1,5 @@
1
1
  import { ControlType } from '../constants';
2
- import { SEGMENT_UPDATE, SPLIT_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE, RB_SEGMENT_UPDATE } from '../types';
2
+ import { SEGMENT_UPDATE, SPLIT_UPDATE, CONFIG_UPDATE, SPLIT_KILL, CONTROL, OCCUPANCY, MEMBERSHIPS_LS_UPDATE, MEMBERSHIPS_MS_UPDATE, RB_SEGMENT_UPDATE } from '../types';
3
3
 
4
4
  export enum Compression {
5
5
  None = 0,
@@ -42,7 +42,7 @@ export interface ISegmentUpdateData {
42
42
  }
43
43
 
44
44
  export interface ISplitUpdateData {
45
- type: SPLIT_UPDATE | RB_SEGMENT_UPDATE,
45
+ type: SPLIT_UPDATE | CONFIG_UPDATE | RB_SEGMENT_UPDATE,
46
46
  changeNumber: number,
47
47
  pcn?: number,
48
48
  d?: string,
@@ -30,6 +30,7 @@ export const MEMBERSHIPS_LS_UPDATE = 'MEMBERSHIPS_LS_UPDATE';
30
30
  export const SEGMENT_UPDATE = 'SEGMENT_UPDATE';
31
31
  export const SPLIT_KILL = 'SPLIT_KILL';
32
32
  export const SPLIT_UPDATE = 'SPLIT_UPDATE';
33
+ export const CONFIG_UPDATE = 'CONFIG_UPDATE';
33
34
  export const RB_SEGMENT_UPDATE = 'RB_SEGMENT_UPDATE';
34
35
 
35
36
  // Control-type push notifications, handled by NotificationKeeper