@splitsoftware/splitio-commons 1.17.0-rc.0 → 1.17.0-rc.2

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.
@@ -4,6 +4,7 @@ exports.decorateHeaders = void 0;
4
4
  var objectAssign_1 = require("../utils/lang/objectAssign");
5
5
  var sets_1 = require("../utils/lang/sets");
6
6
  var FORBIDDEN_HEADERS = new sets_1._Set([
7
+ 'splitsdkclientkey',
7
8
  'splitsdkversion',
8
9
  'splitsdkmachineip',
9
10
  'splitsdkmachinename',
@@ -17,20 +18,13 @@ var FORBIDDEN_HEADERS = new sets_1._Set([
17
18
  'keep-alive',
18
19
  'x-fastly-debug'
19
20
  ]);
20
- function convertKeysToLowerCase(obj) {
21
- return Object.keys(obj).reduce(function (acc, key) {
22
- acc[key.toLowerCase()] = obj[key];
23
- return acc;
24
- }, {});
25
- }
26
21
  function decorateHeaders(settings, headers) {
27
22
  var _a;
28
23
  if ((_a = settings.sync.requestOptions) === null || _a === void 0 ? void 0 : _a.getHeaderOverrides) {
29
- headers = convertKeysToLowerCase(headers);
30
24
  try {
31
- var headerOverrides_1 = convertKeysToLowerCase(settings.sync.requestOptions.getHeaderOverrides({ headers: (0, objectAssign_1.objectAssign)({}, headers) }));
25
+ var headerOverrides_1 = settings.sync.requestOptions.getHeaderOverrides({ headers: (0, objectAssign_1.objectAssign)({}, headers) });
32
26
  Object.keys(headerOverrides_1)
33
- .filter(function (key) { return !FORBIDDEN_HEADERS.has(key); })
27
+ .filter(function (key) { return !FORBIDDEN_HEADERS.has(key.toLowerCase()); })
34
28
  .forEach(function (key) { return headers[key] = headerOverrides_1[key]; });
35
29
  }
36
30
  catch (e) {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SSEClient = void 0;
4
+ var decorateHeaders_1 = require("../../../services/decorateHeaders");
4
5
  var lang_1 = require("../../../utils/lang");
5
6
  var objectAssign_1 = require("../../../utils/lang/objectAssign");
6
7
  var ABLY_API_VERSION = '1.1';
@@ -32,19 +33,16 @@ var SSEClient = /** @class */ (function () {
32
33
  * SSEClient constructor.
33
34
  *
34
35
  * @param settings Validated settings.
35
- * @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
36
36
  * @param platform object containing environment-specific dependencies
37
37
  * @throws 'EventSource API is not available.' if EventSource is not available.
38
38
  */
39
- function SSEClient(settings, useHeaders, _a) {
39
+ function SSEClient(settings, _a) {
40
40
  var getEventSource = _a.getEventSource, getOptions = _a.getOptions;
41
+ this.settings = settings;
41
42
  this.eventSource = getEventSource && getEventSource(settings);
42
43
  // if eventSource is not available, throw an exception
43
44
  if (!this.eventSource)
44
45
  throw new Error('EventSource API is not available.');
45
- this.streamingUrl = settings.urls.streaming + '/sse';
46
- // @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
47
- this.useHeaders = useHeaders;
48
46
  this.headers = buildSSEHeaders(settings);
49
47
  this.options = getOptions && getOptions(settings);
50
48
  }
@@ -58,18 +56,21 @@ var SSEClient = /** @class */ (function () {
58
56
  * @throws {TypeError} Will throw an error if `authToken` is undefined
59
57
  */
60
58
  SSEClient.prototype.open = function (authToken) {
59
+ var _a;
61
60
  this.close(); // it closes connection if previously opened
62
61
  var channelsQueryParam = Object.keys(authToken.channels).map(function (channel) {
63
62
  var params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
64
63
  return encodeURIComponent(params + channel);
65
64
  }).join(',');
66
- var url = this.streamingUrl + "?channels=" + channelsQueryParam + "&accessToken=" + authToken.token + "&v=" + ABLY_API_VERSION + "&heartbeats=true"; // same results using `&heartbeats=false`
65
+ var url = this.settings.urls.streaming + "/sse?channels=" + channelsQueryParam + "&accessToken=" + authToken.token + "&v=" + ABLY_API_VERSION + "&heartbeats=true"; // same results using `&heartbeats=false`
66
+ // use headers in server-side or if getHeaderOverrides is defined
67
+ var useHeaders = !this.settings.core.key || ((_a = this.settings.sync.requestOptions) === null || _a === void 0 ? void 0 : _a.getHeaderOverrides);
67
68
  this.connection = new this.eventSource(
68
69
  // For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
69
70
  // because native EventSource implementations for browser doesn't support headers.
70
- this.useHeaders ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
71
+ useHeaders ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
71
72
  // For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
72
- (0, objectAssign_1.objectAssign)(this.useHeaders ? { headers: this.headers } : {}, this.options));
73
+ (0, objectAssign_1.objectAssign)(useHeaders ? { headers: (0, decorateHeaders_1.decorateHeaders)(this.settings, this.headers) } : {}, this.options));
73
74
  if (this.handler) { // no need to check if SSEClient is used only by PushManager
74
75
  this.connection.addEventListener('open', this.handler.handleOpen);
75
76
  this.connection.addEventListener('message', this.handler.handleMessage);
@@ -32,7 +32,7 @@ function pushManagerFactory(params, pollingManager) {
32
32
  var sseClient;
33
33
  try {
34
34
  // `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
35
- sseClient = new SSEClient_1.SSEClient(settings, userKey ? false : true, platform);
35
+ sseClient = new SSEClient_1.SSEClient(settings, platform);
36
36
  }
37
37
  catch (e) {
38
38
  log.warn(constants_2.STREAMING_FALLBACK, [e]);
@@ -1,6 +1,7 @@
1
1
  import { objectAssign } from '../utils/lang/objectAssign';
2
2
  import { _Set } from '../utils/lang/sets';
3
3
  var FORBIDDEN_HEADERS = new _Set([
4
+ 'splitsdkclientkey',
4
5
  'splitsdkversion',
5
6
  'splitsdkmachineip',
6
7
  'splitsdkmachinename',
@@ -14,20 +15,13 @@ var FORBIDDEN_HEADERS = new _Set([
14
15
  'keep-alive',
15
16
  'x-fastly-debug'
16
17
  ]);
17
- function convertKeysToLowerCase(obj) {
18
- return Object.keys(obj).reduce(function (acc, key) {
19
- acc[key.toLowerCase()] = obj[key];
20
- return acc;
21
- }, {});
22
- }
23
18
  export function decorateHeaders(settings, headers) {
24
19
  var _a;
25
20
  if ((_a = settings.sync.requestOptions) === null || _a === void 0 ? void 0 : _a.getHeaderOverrides) {
26
- headers = convertKeysToLowerCase(headers);
27
21
  try {
28
- var headerOverrides_1 = convertKeysToLowerCase(settings.sync.requestOptions.getHeaderOverrides({ headers: objectAssign({}, headers) }));
22
+ var headerOverrides_1 = settings.sync.requestOptions.getHeaderOverrides({ headers: objectAssign({}, headers) });
29
23
  Object.keys(headerOverrides_1)
30
- .filter(function (key) { return !FORBIDDEN_HEADERS.has(key); })
24
+ .filter(function (key) { return !FORBIDDEN_HEADERS.has(key.toLowerCase()); })
31
25
  .forEach(function (key) { return headers[key] = headerOverrides_1[key]; });
32
26
  }
33
27
  catch (e) {
@@ -1,3 +1,4 @@
1
+ import { decorateHeaders } from '../../../services/decorateHeaders';
1
2
  import { isString } from '../../../utils/lang';
2
3
  import { objectAssign } from '../../../utils/lang/objectAssign';
3
4
  var ABLY_API_VERSION = '1.1';
@@ -29,19 +30,16 @@ var SSEClient = /** @class */ (function () {
29
30
  * SSEClient constructor.
30
31
  *
31
32
  * @param settings Validated settings.
32
- * @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
33
33
  * @param platform object containing environment-specific dependencies
34
34
  * @throws 'EventSource API is not available.' if EventSource is not available.
35
35
  */
36
- function SSEClient(settings, useHeaders, _a) {
36
+ function SSEClient(settings, _a) {
37
37
  var getEventSource = _a.getEventSource, getOptions = _a.getOptions;
38
+ this.settings = settings;
38
39
  this.eventSource = getEventSource && getEventSource(settings);
39
40
  // if eventSource is not available, throw an exception
40
41
  if (!this.eventSource)
41
42
  throw new Error('EventSource API is not available.');
42
- this.streamingUrl = settings.urls.streaming + '/sse';
43
- // @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
44
- this.useHeaders = useHeaders;
45
43
  this.headers = buildSSEHeaders(settings);
46
44
  this.options = getOptions && getOptions(settings);
47
45
  }
@@ -55,18 +53,21 @@ var SSEClient = /** @class */ (function () {
55
53
  * @throws {TypeError} Will throw an error if `authToken` is undefined
56
54
  */
57
55
  SSEClient.prototype.open = function (authToken) {
56
+ var _a;
58
57
  this.close(); // it closes connection if previously opened
59
58
  var channelsQueryParam = Object.keys(authToken.channels).map(function (channel) {
60
59
  var params = CONTROL_CHANNEL_REGEX.test(channel) ? '[?occupancy=metrics.publishers]' : '';
61
60
  return encodeURIComponent(params + channel);
62
61
  }).join(',');
63
- var url = this.streamingUrl + "?channels=" + channelsQueryParam + "&accessToken=" + authToken.token + "&v=" + ABLY_API_VERSION + "&heartbeats=true"; // same results using `&heartbeats=false`
62
+ var url = this.settings.urls.streaming + "/sse?channels=" + channelsQueryParam + "&accessToken=" + authToken.token + "&v=" + ABLY_API_VERSION + "&heartbeats=true"; // same results using `&heartbeats=false`
63
+ // use headers in server-side or if getHeaderOverrides is defined
64
+ var useHeaders = !this.settings.core.key || ((_a = this.settings.sync.requestOptions) === null || _a === void 0 ? void 0 : _a.getHeaderOverrides);
64
65
  this.connection = new this.eventSource(
65
66
  // For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
66
67
  // because native EventSource implementations for browser doesn't support headers.
67
- this.useHeaders ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
68
+ useHeaders ? url : url + ("&SplitSDKVersion=" + this.headers.SplitSDKVersion + "&SplitSDKClientKey=" + this.headers.SplitSDKClientKey),
68
69
  // For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
69
- objectAssign(this.useHeaders ? { headers: this.headers } : {}, this.options));
70
+ objectAssign(useHeaders ? { headers: decorateHeaders(this.settings, this.headers) } : {}, this.options));
70
71
  if (this.handler) { // no need to check if SSEClient is used only by PushManager
71
72
  this.connection.addEventListener('open', this.handler.handleOpen);
72
73
  this.connection.addEventListener('message', this.handler.handleMessage);
@@ -29,7 +29,7 @@ export function pushManagerFactory(params, pollingManager) {
29
29
  var sseClient;
30
30
  try {
31
31
  // `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
32
- sseClient = new SSEClient(settings, userKey ? false : true, platform);
32
+ sseClient = new SSEClient(settings, platform);
33
33
  }
34
34
  catch (e) {
35
35
  log.warn(STREAMING_FALLBACK, [e]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/splitio-commons",
3
- "version": "1.17.0-rc.0",
3
+ "version": "1.17.0-rc.2",
4
4
  "description": "Split JavaScript SDK common components",
5
5
  "main": "cjs/index.js",
6
6
  "module": "esm/index.js",
@@ -3,6 +3,7 @@ import { _Set } from '../utils/lang/sets';
3
3
  import { ISettings } from '../types';
4
4
 
5
5
  const FORBIDDEN_HEADERS = new _Set([
6
+ 'splitsdkclientkey',
6
7
  'splitsdkversion',
7
8
  'splitsdkmachineip',
8
9
  'splitsdkmachinename',
@@ -17,20 +18,12 @@ const FORBIDDEN_HEADERS = new _Set([
17
18
  'x-fastly-debug'
18
19
  ]);
19
20
 
20
- function convertKeysToLowerCase(obj: Record<string, string>) {
21
- return Object.keys(obj).reduce<Record<string, string>>((acc, key) => {
22
- acc[key.toLowerCase()] = obj[key];
23
- return acc;
24
- }, {});
25
- }
26
-
27
21
  export function decorateHeaders(settings: ISettings, headers: Record<string, string>) {
28
22
  if (settings.sync.requestOptions?.getHeaderOverrides) {
29
- headers = convertKeysToLowerCase(headers);
30
23
  try {
31
- const headerOverrides = convertKeysToLowerCase(settings.sync.requestOptions.getHeaderOverrides({ headers: objectAssign({}, headers) }));
24
+ const headerOverrides = settings.sync.requestOptions.getHeaderOverrides({ headers: objectAssign({}, headers) });
32
25
  Object.keys(headerOverrides)
33
- .filter(key => !FORBIDDEN_HEADERS.has(key))
26
+ .filter(key => !FORBIDDEN_HEADERS.has(key.toLowerCase()))
34
27
  .forEach(key => headers[key] = headerOverrides[key]);
35
28
  } catch (e) {
36
29
  settings.log.error('Problem adding custom headers to request decorator: ' + e);
@@ -1,4 +1,5 @@
1
1
  import { IPlatform } from '../../../sdkFactory/types';
2
+ import { decorateHeaders } from '../../../services/decorateHeaders';
2
3
  import { IEventSourceConstructor } from '../../../services/types';
3
4
  import { ISettings } from '../../../types';
4
5
  import { isString } from '../../../utils/lang';
@@ -36,10 +37,8 @@ function buildSSEHeaders(settings: ISettings) {
36
37
  export class SSEClient implements ISSEClient {
37
38
  // Instance properties:
38
39
  eventSource?: IEventSourceConstructor;
39
- streamingUrl: string;
40
40
  connection?: InstanceType<IEventSourceConstructor>;
41
41
  handler?: ISseEventHandler;
42
- useHeaders?: boolean;
43
42
  headers: Record<string, string>;
44
43
  options?: object;
45
44
 
@@ -47,18 +46,14 @@ export class SSEClient implements ISSEClient {
47
46
  * SSEClient constructor.
48
47
  *
49
48
  * @param settings Validated settings.
50
- * @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
51
49
  * @param platform object containing environment-specific dependencies
52
50
  * @throws 'EventSource API is not available.' if EventSource is not available.
53
51
  */
54
- constructor(settings: ISettings, useHeaders: boolean, { getEventSource, getOptions }: IPlatform) {
52
+ constructor(private settings: ISettings, { getEventSource, getOptions }: IPlatform) {
55
53
  this.eventSource = getEventSource && getEventSource(settings);
56
54
  // if eventSource is not available, throw an exception
57
55
  if (!this.eventSource) throw new Error('EventSource API is not available.');
58
56
 
59
- this.streamingUrl = settings.urls.streaming + '/sse';
60
- // @TODO get `useHeaders` flag from `getEventSource`, to use EventSource headers on client-side SDKs when possible.
61
- this.useHeaders = useHeaders;
62
57
  this.headers = buildSSEHeaders(settings);
63
58
  this.options = getOptions && getOptions(settings);
64
59
  }
@@ -82,14 +77,16 @@ export class SSEClient implements ISSEClient {
82
77
  return encodeURIComponent(params + channel);
83
78
  }
84
79
  ).join(',');
85
- const url = `${this.streamingUrl}?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${ABLY_API_VERSION}&heartbeats=true`; // same results using `&heartbeats=false`
80
+ const url = `${this.settings.urls.streaming}/sse?channels=${channelsQueryParam}&accessToken=${authToken.token}&v=${ABLY_API_VERSION}&heartbeats=true`; // same results using `&heartbeats=false`
81
+ // use headers in server-side or if getHeaderOverrides is defined
82
+ const useHeaders = !this.settings.core.key || this.settings.sync.requestOptions?.getHeaderOverrides;
86
83
 
87
84
  this.connection = new this.eventSource!(
88
85
  // For client-side SDKs, SplitSDKClientKey and SplitSDKClientKey metadata is passed as query params,
89
86
  // because native EventSource implementations for browser doesn't support headers.
90
- this.useHeaders ? url : url + `&SplitSDKVersion=${this.headers.SplitSDKVersion}&SplitSDKClientKey=${this.headers.SplitSDKClientKey}`,
87
+ useHeaders ? url : url + `&SplitSDKVersion=${this.headers.SplitSDKVersion}&SplitSDKClientKey=${this.headers.SplitSDKClientKey}`,
91
88
  // For server-side SDKs, metadata is passed via headers. EventSource must support headers, like 'eventsource' package for Node.
92
- objectAssign(this.useHeaders ? { headers: this.headers } : {}, this.options)
89
+ objectAssign(useHeaders ? { headers: decorateHeaders(this.settings, this.headers) } : {}, this.options)
93
90
  );
94
91
 
95
92
  if (this.handler) { // no need to check if SSEClient is used only by PushManager
@@ -42,7 +42,7 @@ export function pushManagerFactory(
42
42
  let sseClient: ISSEClient;
43
43
  try {
44
44
  // `useHeaders` false for client-side, even if the platform EventSource supports headers (e.g., React Native).
45
- sseClient = new SSEClient(settings, userKey ? false : true, platform);
45
+ sseClient = new SSEClient(settings, platform);
46
46
  } catch (e) {
47
47
  log.warn(STREAMING_FALLBACK, [e]);
48
48
  return;
@@ -7,22 +7,20 @@ import { ISSEClient, ISseEventHandler } from './types';
7
7
  * Handles streaming connections with EventSource API
8
8
  */
9
9
  export declare class SSEClient implements ISSEClient {
10
+ private settings;
10
11
  eventSource?: IEventSourceConstructor;
11
- streamingUrl: string;
12
12
  connection?: InstanceType<IEventSourceConstructor>;
13
13
  handler?: ISseEventHandler;
14
- useHeaders?: boolean;
15
14
  headers: Record<string, string>;
16
15
  options?: object;
17
16
  /**
18
17
  * SSEClient constructor.
19
18
  *
20
19
  * @param settings Validated settings.
21
- * @param useHeaders True to send metadata as headers or false to send as query params. If `true`, the provided EventSource must support headers.
22
20
  * @param platform object containing environment-specific dependencies
23
21
  * @throws 'EventSource API is not available.' if EventSource is not available.
24
22
  */
25
- constructor(settings: ISettings, useHeaders: boolean, { getEventSource, getOptions }: IPlatform);
23
+ constructor(settings: ISettings, { getEventSource, getOptions }: IPlatform);
26
24
  setEventHandler(handler: ISseEventHandler): void;
27
25
  /**
28
26
  * Open the connection with a given authToken