@splitsoftware/splitio-commons 1.17.0-rc.0 → 1.17.0-rc.1
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.
- package/cjs/services/decorateHeaders.js +4 -3
- package/cjs/sync/streaming/SSEClient/index.js +9 -8
- package/cjs/sync/streaming/pushManager.js +1 -1
- package/esm/services/decorateHeaders.js +4 -3
- package/esm/sync/streaming/SSEClient/index.js +9 -8
- package/esm/sync/streaming/pushManager.js +1 -1
- package/package.json +1 -1
- package/src/services/decorateHeaders.ts +4 -3
- package/src/sync/streaming/SSEClient/index.ts +7 -10
- package/src/sync/streaming/pushManager.ts +1 -1
- package/types/sync/streaming/SSEClient/index.d.ts +2 -4
|
@@ -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,7 +18,7 @@ var FORBIDDEN_HEADERS = new sets_1._Set([
|
|
|
17
18
|
'keep-alive',
|
|
18
19
|
'x-fastly-debug'
|
|
19
20
|
]);
|
|
20
|
-
function
|
|
21
|
+
function copyToLowerCase(obj) {
|
|
21
22
|
return Object.keys(obj).reduce(function (acc, key) {
|
|
22
23
|
acc[key.toLowerCase()] = obj[key];
|
|
23
24
|
return acc;
|
|
@@ -26,9 +27,9 @@ function convertKeysToLowerCase(obj) {
|
|
|
26
27
|
function decorateHeaders(settings, headers) {
|
|
27
28
|
var _a;
|
|
28
29
|
if ((_a = settings.sync.requestOptions) === null || _a === void 0 ? void 0 : _a.getHeaderOverrides) {
|
|
29
|
-
headers =
|
|
30
|
+
headers = copyToLowerCase(headers);
|
|
30
31
|
try {
|
|
31
|
-
var headerOverrides_1 =
|
|
32
|
+
var headerOverrides_1 = copyToLowerCase(settings.sync.requestOptions.getHeaderOverrides({ headers: (0, objectAssign_1.objectAssign)({}, headers) }));
|
|
32
33
|
Object.keys(headerOverrides_1)
|
|
33
34
|
.filter(function (key) { return !FORBIDDEN_HEADERS.has(key); })
|
|
34
35
|
.forEach(function (key) { return headers[key] = headerOverrides_1[key]; });
|
|
@@ -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,
|
|
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.
|
|
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
|
-
|
|
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)(
|
|
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,
|
|
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,7 +15,7 @@ var FORBIDDEN_HEADERS = new _Set([
|
|
|
14
15
|
'keep-alive',
|
|
15
16
|
'x-fastly-debug'
|
|
16
17
|
]);
|
|
17
|
-
function
|
|
18
|
+
function copyToLowerCase(obj) {
|
|
18
19
|
return Object.keys(obj).reduce(function (acc, key) {
|
|
19
20
|
acc[key.toLowerCase()] = obj[key];
|
|
20
21
|
return acc;
|
|
@@ -23,9 +24,9 @@ function convertKeysToLowerCase(obj) {
|
|
|
23
24
|
export function decorateHeaders(settings, headers) {
|
|
24
25
|
var _a;
|
|
25
26
|
if ((_a = settings.sync.requestOptions) === null || _a === void 0 ? void 0 : _a.getHeaderOverrides) {
|
|
26
|
-
headers =
|
|
27
|
+
headers = copyToLowerCase(headers);
|
|
27
28
|
try {
|
|
28
|
-
var headerOverrides_1 =
|
|
29
|
+
var headerOverrides_1 = copyToLowerCase(settings.sync.requestOptions.getHeaderOverrides({ headers: objectAssign({}, headers) }));
|
|
29
30
|
Object.keys(headerOverrides_1)
|
|
30
31
|
.filter(function (key) { return !FORBIDDEN_HEADERS.has(key); })
|
|
31
32
|
.forEach(function (key) { return headers[key] = headerOverrides_1[key]; });
|
|
@@ -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,
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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,
|
|
32
|
+
sseClient = new SSEClient(settings, platform);
|
|
33
33
|
}
|
|
34
34
|
catch (e) {
|
|
35
35
|
log.warn(STREAMING_FALLBACK, [e]);
|
package/package.json
CHANGED
|
@@ -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,7 +18,7 @@ const FORBIDDEN_HEADERS = new _Set([
|
|
|
17
18
|
'x-fastly-debug'
|
|
18
19
|
]);
|
|
19
20
|
|
|
20
|
-
function
|
|
21
|
+
function copyToLowerCase(obj: Record<string, string>) {
|
|
21
22
|
return Object.keys(obj).reduce<Record<string, string>>((acc, key) => {
|
|
22
23
|
acc[key.toLowerCase()] = obj[key];
|
|
23
24
|
return acc;
|
|
@@ -26,9 +27,9 @@ function convertKeysToLowerCase(obj: Record<string, string>) {
|
|
|
26
27
|
|
|
27
28
|
export function decorateHeaders(settings: ISettings, headers: Record<string, string>) {
|
|
28
29
|
if (settings.sync.requestOptions?.getHeaderOverrides) {
|
|
29
|
-
headers =
|
|
30
|
+
headers = copyToLowerCase(headers);
|
|
30
31
|
try {
|
|
31
|
-
const headerOverrides =
|
|
32
|
+
const headerOverrides = copyToLowerCase(settings.sync.requestOptions.getHeaderOverrides({ headers: objectAssign({}, headers) }));
|
|
32
33
|
Object.keys(headerOverrides)
|
|
33
34
|
.filter(key => !FORBIDDEN_HEADERS.has(key))
|
|
34
35
|
.forEach(key => headers[key] = headerOverrides[key]);
|
|
@@ -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,
|
|
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.
|
|
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
|
-
|
|
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(
|
|
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,
|
|
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,
|
|
23
|
+
constructor(settings: ISettings, { getEventSource, getOptions }: IPlatform);
|
|
26
24
|
setEventHandler(handler: ISseEventHandler): void;
|
|
27
25
|
/**
|
|
28
26
|
* Open the connection with a given authToken
|