flagsmith-nodejs 5.0.1 → 5.1.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/.github/workflows/pull_request.yaml +6 -3
- package/build/cjs/index.d.ts +1 -1
- package/build/cjs/sdk/analytics.d.ts +32 -17
- package/build/cjs/sdk/analytics.js +23 -17
- package/build/cjs/sdk/index.d.ts +2 -1
- package/build/cjs/sdk/index.js +8 -6
- package/build/esm/index.d.ts +1 -1
- package/build/esm/sdk/analytics.d.ts +32 -17
- package/build/esm/sdk/analytics.js +22 -16
- package/build/esm/sdk/index.d.ts +2 -1
- package/build/esm/sdk/index.js +9 -7
- package/index.ts +1 -0
- package/package.json +1 -1
- package/sdk/analytics.ts +42 -17
- package/sdk/index.ts +12 -10
- package/tests/sdk/analytics.test.ts +1 -1
- package/tests/sdk/utils.ts +1 -1
|
@@ -12,6 +12,9 @@ on:
|
|
|
12
12
|
- main
|
|
13
13
|
jobs:
|
|
14
14
|
build-and-test:
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
node-version: [18.x, 20.x, 22.x]
|
|
15
18
|
runs-on: ubuntu-latest
|
|
16
19
|
steps:
|
|
17
20
|
- uses: actions/checkout@v4
|
|
@@ -19,14 +22,14 @@ jobs:
|
|
|
19
22
|
submodules: true
|
|
20
23
|
- uses: actions/setup-node@v4
|
|
21
24
|
with:
|
|
22
|
-
node-version: "
|
|
25
|
+
node-version: "${{ matrix.node-version }}"
|
|
23
26
|
- name: cache node modules
|
|
24
27
|
uses: actions/cache@v4
|
|
25
28
|
with:
|
|
26
29
|
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS
|
|
27
|
-
key: npm-${{ hashFiles('package-lock.json') }}
|
|
30
|
+
key: npm-${{ matrix.node-version }}-${{ hashFiles('package-lock.json') }}
|
|
28
31
|
restore-keys: |
|
|
29
|
-
npm-${{ hashFiles('package-lock.json') }}
|
|
32
|
+
npm-${{ matrix.node-version }}-${{ hashFiles('package-lock.json') }}
|
|
30
33
|
npm-
|
|
31
34
|
- run: npm ci
|
|
32
35
|
- run: npm test
|
package/build/cjs/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AnalyticsProcessor, FlagsmithAPIError, FlagsmithClientError, EnvironmentDataPollingManager, FlagsmithCache, DefaultFlag, Flags, Flagsmith, } from './sdk/index.js';
|
|
1
|
+
export { AnalyticsProcessor, AnalyticsProcessorOptions, FlagsmithAPIError, FlagsmithClientError, EnvironmentDataPollingManager, FlagsmithCache, DefaultFlag, Flags, Flagsmith, } from './sdk/index.js';
|
|
2
2
|
export { BaseOfflineHandler, LocalFileHandler, } from './sdk/offline_handlers.js';
|
|
3
3
|
export { FlagsmithConfig } from './sdk/types.js';
|
|
4
4
|
export { EnvironmentModel, FeatureStateModel, IdentityModel, TraitModel, SegmentModel, OrganisationModel } from './flagsmith-engine/index.js';
|
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
import { Logger } from 'pino';
|
|
2
2
|
import { Fetch } from "./types.js";
|
|
3
|
+
export declare const ANALYTICS_ENDPOINT = "./analytics/flags/";
|
|
4
|
+
export interface AnalyticsProcessorOptions {
|
|
5
|
+
/** URL of the Flagsmith analytics events API endpoint
|
|
6
|
+
* @example https://flagsmith.example.com/api/v1/analytics
|
|
7
|
+
*/
|
|
8
|
+
analyticsUrl?: string;
|
|
9
|
+
/** Client-side key of the environment that analytics will be recorded for. **/
|
|
10
|
+
environmentKey: string;
|
|
11
|
+
/** Duration in milliseconds to wait for API requests to complete before timing out. Defaults to {@link DEFAULT_REQUEST_TIMEOUT_MS}. **/
|
|
12
|
+
requestTimeoutMs?: number;
|
|
13
|
+
logger?: Logger;
|
|
14
|
+
/** Custom {@link fetch} implementation to use for API requests. **/
|
|
15
|
+
fetch?: Fetch;
|
|
16
|
+
/** @deprecated Use {@link analyticsUrl} instead. **/
|
|
17
|
+
baseApiUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Tracks how often individual features are evaluated whenever {@link trackFeature} is called.
|
|
21
|
+
*
|
|
22
|
+
* Analytics data is posted after {@link trackFeature} is called and at least {@link ANALYTICS_TIMER} seconds have
|
|
23
|
+
* passed since the previous analytics API request was made (if any), or by calling {@link flush}.
|
|
24
|
+
*
|
|
25
|
+
* Data will stay in memory indefinitely until it can be successfully posted to the API.
|
|
26
|
+
* @see https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
27
|
+
*/
|
|
3
28
|
export declare class AnalyticsProcessor {
|
|
4
|
-
private
|
|
29
|
+
private analyticsUrl;
|
|
5
30
|
private environmentKey;
|
|
6
31
|
private lastFlushed;
|
|
7
32
|
analyticsData: {
|
|
@@ -11,25 +36,15 @@ export declare class AnalyticsProcessor {
|
|
|
11
36
|
private logger;
|
|
12
37
|
private currentFlush;
|
|
13
38
|
private customFetch;
|
|
39
|
+
constructor(data: AnalyticsProcessorOptions);
|
|
14
40
|
/**
|
|
15
|
-
*
|
|
16
|
-
* the Flagsmith SDK. Docs: https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
17
|
-
*
|
|
18
|
-
* @param data.environmentKey environment key obtained from the Flagsmith UI
|
|
19
|
-
* @param data.baseApiUrl base api url to override when using self hosted version
|
|
20
|
-
* @param data.requestTimeoutMs used to tell requests to stop waiting for a response after a
|
|
21
|
-
given number of milliseconds
|
|
41
|
+
* Try to flush pending collected data to the Flagsmith analytics API.
|
|
22
42
|
*/
|
|
23
|
-
|
|
24
|
-
environmentKey: string;
|
|
25
|
-
baseApiUrl: string;
|
|
26
|
-
requestTimeoutMs?: number;
|
|
27
|
-
logger?: Logger;
|
|
28
|
-
fetch?: Fetch;
|
|
29
|
-
});
|
|
43
|
+
flush(): Promise<void>;
|
|
30
44
|
/**
|
|
31
|
-
*
|
|
45
|
+
* Track a single evaluation event for a feature.
|
|
46
|
+
*
|
|
47
|
+
* This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
|
|
32
48
|
*/
|
|
33
|
-
flush(): Promise<void>;
|
|
34
49
|
trackFeature(featureName: string): void;
|
|
35
50
|
}
|
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.AnalyticsProcessor = void 0;
|
|
3
|
+
exports.AnalyticsProcessor = exports.ANALYTICS_ENDPOINT = void 0;
|
|
4
4
|
const pino_1 = require("pino");
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
exports.ANALYTICS_ENDPOINT = './analytics/flags/';
|
|
6
|
+
/** Duration in seconds to wait before trying to flush collected data after {@link trackFeature} is called. **/
|
|
7
7
|
const ANALYTICS_TIMER = 10;
|
|
8
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3000;
|
|
9
|
+
/**
|
|
10
|
+
* Tracks how often individual features are evaluated whenever {@link trackFeature} is called.
|
|
11
|
+
*
|
|
12
|
+
* Analytics data is posted after {@link trackFeature} is called and at least {@link ANALYTICS_TIMER} seconds have
|
|
13
|
+
* passed since the previous analytics API request was made (if any), or by calling {@link flush}.
|
|
14
|
+
*
|
|
15
|
+
* Data will stay in memory indefinitely until it can be successfully posted to the API.
|
|
16
|
+
* @see https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
17
|
+
*/
|
|
8
18
|
class AnalyticsProcessor {
|
|
9
|
-
|
|
19
|
+
analyticsUrl;
|
|
10
20
|
environmentKey;
|
|
11
21
|
lastFlushed;
|
|
12
22
|
analyticsData;
|
|
13
|
-
requestTimeoutMs =
|
|
23
|
+
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
14
24
|
logger;
|
|
15
25
|
currentFlush;
|
|
16
26
|
customFetch;
|
|
17
|
-
/**
|
|
18
|
-
* AnalyticsProcessor is used to track how often individual Flags are evaluated within
|
|
19
|
-
* the Flagsmith SDK. Docs: https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
20
|
-
*
|
|
21
|
-
* @param data.environmentKey environment key obtained from the Flagsmith UI
|
|
22
|
-
* @param data.baseApiUrl base api url to override when using self hosted version
|
|
23
|
-
* @param data.requestTimeoutMs used to tell requests to stop waiting for a response after a
|
|
24
|
-
given number of milliseconds
|
|
25
|
-
*/
|
|
26
27
|
constructor(data) {
|
|
27
|
-
this.
|
|
28
|
+
this.analyticsUrl = data.analyticsUrl || data.baseApiUrl + exports.ANALYTICS_ENDPOINT;
|
|
28
29
|
this.environmentKey = data.environmentKey;
|
|
29
30
|
this.lastFlushed = Date.now();
|
|
30
31
|
this.analyticsData = {};
|
|
@@ -33,14 +34,14 @@ class AnalyticsProcessor {
|
|
|
33
34
|
this.customFetch = data.fetch ?? fetch;
|
|
34
35
|
}
|
|
35
36
|
/**
|
|
36
|
-
*
|
|
37
|
+
* Try to flush pending collected data to the Flagsmith analytics API.
|
|
37
38
|
*/
|
|
38
39
|
async flush() {
|
|
39
40
|
if (this.currentFlush || !Object.keys(this.analyticsData).length) {
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
try {
|
|
43
|
-
this.currentFlush = this.customFetch(this.
|
|
44
|
+
this.currentFlush = this.customFetch(this.analyticsUrl, {
|
|
44
45
|
method: 'POST',
|
|
45
46
|
body: JSON.stringify(this.analyticsData),
|
|
46
47
|
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
@@ -63,6 +64,11 @@ class AnalyticsProcessor {
|
|
|
63
64
|
this.analyticsData = {};
|
|
64
65
|
this.lastFlushed = Date.now();
|
|
65
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Track a single evaluation event for a feature.
|
|
69
|
+
*
|
|
70
|
+
* This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
|
|
71
|
+
*/
|
|
66
72
|
trackFeature(featureName) {
|
|
67
73
|
this.analyticsData[featureName] = (this.analyticsData[featureName] || 0) + 1;
|
|
68
74
|
if (Date.now() - this.lastFlushed > ANALYTICS_TIMER * 1000) {
|
package/build/cjs/sdk/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { DefaultFlag, Flags } from './models.js';
|
|
|
6
6
|
import { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
7
7
|
import { SegmentModel } from '../flagsmith-engine/index.js';
|
|
8
8
|
import { FlagsmithConfig, FlagsmithTraitValue, ITraitConfig } from './types.js';
|
|
9
|
-
export { AnalyticsProcessor } from './analytics.js';
|
|
9
|
+
export { AnalyticsProcessor, AnalyticsProcessorOptions } from './analytics.js';
|
|
10
10
|
export { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
|
|
11
11
|
export { DefaultFlag, Flags } from './models.js';
|
|
12
12
|
export { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
@@ -14,6 +14,7 @@ export { FlagsmithCache, FlagsmithConfig } from './types.js';
|
|
|
14
14
|
export declare class Flagsmith {
|
|
15
15
|
environmentKey?: string;
|
|
16
16
|
apiUrl?: string;
|
|
17
|
+
analyticsUrl?: string;
|
|
17
18
|
customHeaders?: {
|
|
18
19
|
[key: string]: any;
|
|
19
20
|
};
|
package/build/cjs/sdk/index.js
CHANGED
|
@@ -27,6 +27,7 @@ const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
|
|
|
27
27
|
class Flagsmith {
|
|
28
28
|
environmentKey = undefined;
|
|
29
29
|
apiUrl = undefined;
|
|
30
|
+
analyticsUrl = undefined;
|
|
30
31
|
customHeaders;
|
|
31
32
|
agent;
|
|
32
33
|
requestTimeoutMs;
|
|
@@ -125,6 +126,7 @@ class Flagsmith {
|
|
|
125
126
|
}
|
|
126
127
|
const apiUrl = data.apiUrl || DEFAULT_API_URL;
|
|
127
128
|
this.apiUrl = apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`;
|
|
129
|
+
this.analyticsUrl = this.analyticsUrl || new URL(analytics_js_1.ANALYTICS_ENDPOINT, new Request(this.apiUrl).url).href;
|
|
128
130
|
this.environmentFlagsUrl = `${this.apiUrl}flags/`;
|
|
129
131
|
this.identitiesUrl = `${this.apiUrl}identities/`;
|
|
130
132
|
this.environmentUrl = `${this.apiUrl}environment-document/`;
|
|
@@ -136,14 +138,14 @@ class Flagsmith {
|
|
|
136
138
|
this.environmentDataPollingManager.start();
|
|
137
139
|
this.updateEnvironment();
|
|
138
140
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
if (data.enableAnalytics) {
|
|
142
|
+
this.analyticsProcessor = new analytics_js_1.AnalyticsProcessor({
|
|
141
143
|
environmentKey: this.environmentKey,
|
|
142
|
-
|
|
144
|
+
analyticsUrl: this.analyticsUrl,
|
|
143
145
|
requestTimeoutMs: this.requestTimeoutMs,
|
|
144
|
-
logger: this.logger
|
|
145
|
-
})
|
|
146
|
-
|
|
146
|
+
logger: this.logger,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
/**
|
package/build/esm/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AnalyticsProcessor, FlagsmithAPIError, FlagsmithClientError, EnvironmentDataPollingManager, FlagsmithCache, DefaultFlag, Flags, Flagsmith, } from './sdk/index.js';
|
|
1
|
+
export { AnalyticsProcessor, AnalyticsProcessorOptions, FlagsmithAPIError, FlagsmithClientError, EnvironmentDataPollingManager, FlagsmithCache, DefaultFlag, Flags, Flagsmith, } from './sdk/index.js';
|
|
2
2
|
export { BaseOfflineHandler, LocalFileHandler, } from './sdk/offline_handlers.js';
|
|
3
3
|
export { FlagsmithConfig } from './sdk/types.js';
|
|
4
4
|
export { EnvironmentModel, FeatureStateModel, IdentityModel, TraitModel, SegmentModel, OrganisationModel } from './flagsmith-engine/index.js';
|
|
@@ -1,7 +1,32 @@
|
|
|
1
1
|
import { Logger } from 'pino';
|
|
2
2
|
import { Fetch } from "./types.js";
|
|
3
|
+
export declare const ANALYTICS_ENDPOINT = "./analytics/flags/";
|
|
4
|
+
export interface AnalyticsProcessorOptions {
|
|
5
|
+
/** URL of the Flagsmith analytics events API endpoint
|
|
6
|
+
* @example https://flagsmith.example.com/api/v1/analytics
|
|
7
|
+
*/
|
|
8
|
+
analyticsUrl?: string;
|
|
9
|
+
/** Client-side key of the environment that analytics will be recorded for. **/
|
|
10
|
+
environmentKey: string;
|
|
11
|
+
/** Duration in milliseconds to wait for API requests to complete before timing out. Defaults to {@link DEFAULT_REQUEST_TIMEOUT_MS}. **/
|
|
12
|
+
requestTimeoutMs?: number;
|
|
13
|
+
logger?: Logger;
|
|
14
|
+
/** Custom {@link fetch} implementation to use for API requests. **/
|
|
15
|
+
fetch?: Fetch;
|
|
16
|
+
/** @deprecated Use {@link analyticsUrl} instead. **/
|
|
17
|
+
baseApiUrl?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Tracks how often individual features are evaluated whenever {@link trackFeature} is called.
|
|
21
|
+
*
|
|
22
|
+
* Analytics data is posted after {@link trackFeature} is called and at least {@link ANALYTICS_TIMER} seconds have
|
|
23
|
+
* passed since the previous analytics API request was made (if any), or by calling {@link flush}.
|
|
24
|
+
*
|
|
25
|
+
* Data will stay in memory indefinitely until it can be successfully posted to the API.
|
|
26
|
+
* @see https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
27
|
+
*/
|
|
3
28
|
export declare class AnalyticsProcessor {
|
|
4
|
-
private
|
|
29
|
+
private analyticsUrl;
|
|
5
30
|
private environmentKey;
|
|
6
31
|
private lastFlushed;
|
|
7
32
|
analyticsData: {
|
|
@@ -11,25 +36,15 @@ export declare class AnalyticsProcessor {
|
|
|
11
36
|
private logger;
|
|
12
37
|
private currentFlush;
|
|
13
38
|
private customFetch;
|
|
39
|
+
constructor(data: AnalyticsProcessorOptions);
|
|
14
40
|
/**
|
|
15
|
-
*
|
|
16
|
-
* the Flagsmith SDK. Docs: https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
17
|
-
*
|
|
18
|
-
* @param data.environmentKey environment key obtained from the Flagsmith UI
|
|
19
|
-
* @param data.baseApiUrl base api url to override when using self hosted version
|
|
20
|
-
* @param data.requestTimeoutMs used to tell requests to stop waiting for a response after a
|
|
21
|
-
given number of milliseconds
|
|
41
|
+
* Try to flush pending collected data to the Flagsmith analytics API.
|
|
22
42
|
*/
|
|
23
|
-
|
|
24
|
-
environmentKey: string;
|
|
25
|
-
baseApiUrl: string;
|
|
26
|
-
requestTimeoutMs?: number;
|
|
27
|
-
logger?: Logger;
|
|
28
|
-
fetch?: Fetch;
|
|
29
|
-
});
|
|
43
|
+
flush(): Promise<void>;
|
|
30
44
|
/**
|
|
31
|
-
*
|
|
45
|
+
* Track a single evaluation event for a feature.
|
|
46
|
+
*
|
|
47
|
+
* This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
|
|
32
48
|
*/
|
|
33
|
-
flush(): Promise<void>;
|
|
34
49
|
trackFeature(featureName: string): void;
|
|
35
50
|
}
|
|
@@ -1,27 +1,28 @@
|
|
|
1
1
|
import { pino } from 'pino';
|
|
2
|
-
const ANALYTICS_ENDPOINT = 'analytics/flags/';
|
|
3
|
-
|
|
2
|
+
export const ANALYTICS_ENDPOINT = './analytics/flags/';
|
|
3
|
+
/** Duration in seconds to wait before trying to flush collected data after {@link trackFeature} is called. **/
|
|
4
4
|
const ANALYTICS_TIMER = 10;
|
|
5
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3000;
|
|
6
|
+
/**
|
|
7
|
+
* Tracks how often individual features are evaluated whenever {@link trackFeature} is called.
|
|
8
|
+
*
|
|
9
|
+
* Analytics data is posted after {@link trackFeature} is called and at least {@link ANALYTICS_TIMER} seconds have
|
|
10
|
+
* passed since the previous analytics API request was made (if any), or by calling {@link flush}.
|
|
11
|
+
*
|
|
12
|
+
* Data will stay in memory indefinitely until it can be successfully posted to the API.
|
|
13
|
+
* @see https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
14
|
+
*/
|
|
5
15
|
export class AnalyticsProcessor {
|
|
6
|
-
|
|
16
|
+
analyticsUrl;
|
|
7
17
|
environmentKey;
|
|
8
18
|
lastFlushed;
|
|
9
19
|
analyticsData;
|
|
10
|
-
requestTimeoutMs =
|
|
20
|
+
requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
11
21
|
logger;
|
|
12
22
|
currentFlush;
|
|
13
23
|
customFetch;
|
|
14
|
-
/**
|
|
15
|
-
* AnalyticsProcessor is used to track how often individual Flags are evaluated within
|
|
16
|
-
* the Flagsmith SDK. Docs: https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
17
|
-
*
|
|
18
|
-
* @param data.environmentKey environment key obtained from the Flagsmith UI
|
|
19
|
-
* @param data.baseApiUrl base api url to override when using self hosted version
|
|
20
|
-
* @param data.requestTimeoutMs used to tell requests to stop waiting for a response after a
|
|
21
|
-
given number of milliseconds
|
|
22
|
-
*/
|
|
23
24
|
constructor(data) {
|
|
24
|
-
this.
|
|
25
|
+
this.analyticsUrl = data.analyticsUrl || data.baseApiUrl + ANALYTICS_ENDPOINT;
|
|
25
26
|
this.environmentKey = data.environmentKey;
|
|
26
27
|
this.lastFlushed = Date.now();
|
|
27
28
|
this.analyticsData = {};
|
|
@@ -30,14 +31,14 @@ export class AnalyticsProcessor {
|
|
|
30
31
|
this.customFetch = data.fetch ?? fetch;
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
|
-
*
|
|
34
|
+
* Try to flush pending collected data to the Flagsmith analytics API.
|
|
34
35
|
*/
|
|
35
36
|
async flush() {
|
|
36
37
|
if (this.currentFlush || !Object.keys(this.analyticsData).length) {
|
|
37
38
|
return;
|
|
38
39
|
}
|
|
39
40
|
try {
|
|
40
|
-
this.currentFlush = this.customFetch(this.
|
|
41
|
+
this.currentFlush = this.customFetch(this.analyticsUrl, {
|
|
41
42
|
method: 'POST',
|
|
42
43
|
body: JSON.stringify(this.analyticsData),
|
|
43
44
|
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
@@ -60,6 +61,11 @@ export class AnalyticsProcessor {
|
|
|
60
61
|
this.analyticsData = {};
|
|
61
62
|
this.lastFlushed = Date.now();
|
|
62
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Track a single evaluation event for a feature.
|
|
66
|
+
*
|
|
67
|
+
* This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
|
|
68
|
+
*/
|
|
63
69
|
trackFeature(featureName) {
|
|
64
70
|
this.analyticsData[featureName] = (this.analyticsData[featureName] || 0) + 1;
|
|
65
71
|
if (Date.now() - this.lastFlushed > ANALYTICS_TIMER * 1000) {
|
package/build/esm/sdk/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { DefaultFlag, Flags } from './models.js';
|
|
|
6
6
|
import { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
7
7
|
import { SegmentModel } from '../flagsmith-engine/index.js';
|
|
8
8
|
import { FlagsmithConfig, FlagsmithTraitValue, ITraitConfig } from './types.js';
|
|
9
|
-
export { AnalyticsProcessor } from './analytics.js';
|
|
9
|
+
export { AnalyticsProcessor, AnalyticsProcessorOptions } from './analytics.js';
|
|
10
10
|
export { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
|
|
11
11
|
export { DefaultFlag, Flags } from './models.js';
|
|
12
12
|
export { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
@@ -14,6 +14,7 @@ export { FlagsmithCache, FlagsmithConfig } from './types.js';
|
|
|
14
14
|
export declare class Flagsmith {
|
|
15
15
|
environmentKey?: string;
|
|
16
16
|
apiUrl?: string;
|
|
17
|
+
analyticsUrl?: string;
|
|
17
18
|
customHeaders?: {
|
|
18
19
|
[key: string]: any;
|
|
19
20
|
};
|
package/build/esm/sdk/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { getEnvironmentFeatureStates, getIdentityFeatureStates } from '../flagsm
|
|
|
2
2
|
import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js';
|
|
3
3
|
import { IdentityModel } from '../flagsmith-engine/index.js';
|
|
4
4
|
import { TraitModel } from '../flagsmith-engine/index.js';
|
|
5
|
-
import { AnalyticsProcessor } from './analytics.js';
|
|
5
|
+
import { ANALYTICS_ENDPOINT, AnalyticsProcessor } from './analytics.js';
|
|
6
6
|
import { FlagsmithAPIError } from './errors.js';
|
|
7
7
|
import { Flags } from './models.js';
|
|
8
8
|
import { EnvironmentDataPollingManager } from './polling_manager.js';
|
|
@@ -18,6 +18,7 @@ const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
|
|
|
18
18
|
export class Flagsmith {
|
|
19
19
|
environmentKey = undefined;
|
|
20
20
|
apiUrl = undefined;
|
|
21
|
+
analyticsUrl = undefined;
|
|
21
22
|
customHeaders;
|
|
22
23
|
agent;
|
|
23
24
|
requestTimeoutMs;
|
|
@@ -116,6 +117,7 @@ export class Flagsmith {
|
|
|
116
117
|
}
|
|
117
118
|
const apiUrl = data.apiUrl || DEFAULT_API_URL;
|
|
118
119
|
this.apiUrl = apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`;
|
|
120
|
+
this.analyticsUrl = this.analyticsUrl || new URL(ANALYTICS_ENDPOINT, new Request(this.apiUrl).url).href;
|
|
119
121
|
this.environmentFlagsUrl = `${this.apiUrl}flags/`;
|
|
120
122
|
this.identitiesUrl = `${this.apiUrl}identities/`;
|
|
121
123
|
this.environmentUrl = `${this.apiUrl}environment-document/`;
|
|
@@ -127,14 +129,14 @@ export class Flagsmith {
|
|
|
127
129
|
this.environmentDataPollingManager.start();
|
|
128
130
|
this.updateEnvironment();
|
|
129
131
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
if (data.enableAnalytics) {
|
|
133
|
+
this.analyticsProcessor = new AnalyticsProcessor({
|
|
132
134
|
environmentKey: this.environmentKey,
|
|
133
|
-
|
|
135
|
+
analyticsUrl: this.analyticsUrl,
|
|
134
136
|
requestTimeoutMs: this.requestTimeoutMs,
|
|
135
|
-
logger: this.logger
|
|
136
|
-
})
|
|
137
|
-
|
|
137
|
+
logger: this.logger,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
/**
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flagsmith-nodejs",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.1.1",
|
|
4
4
|
"description": "Flagsmith lets you manage features flags and remote config across web, mobile and server side applications. Deliver true Continuous Integration. Get builds out faster. Control who has access to new features.",
|
|
5
5
|
"main": "./build/cjs/index.js",
|
|
6
6
|
"type": "module",
|
package/sdk/analytics.ts
CHANGED
|
@@ -1,32 +1,52 @@
|
|
|
1
1
|
import { pino, Logger } from 'pino';
|
|
2
2
|
import { Fetch } from "./types.js";
|
|
3
|
+
import { Flags } from "./models.js";
|
|
3
4
|
|
|
4
|
-
const ANALYTICS_ENDPOINT = 'analytics/flags/';
|
|
5
|
+
export const ANALYTICS_ENDPOINT = './analytics/flags/';
|
|
5
6
|
|
|
6
|
-
|
|
7
|
+
/** Duration in seconds to wait before trying to flush collected data after {@link trackFeature} is called. **/
|
|
7
8
|
const ANALYTICS_TIMER = 10;
|
|
8
9
|
|
|
10
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3000
|
|
11
|
+
|
|
12
|
+
export interface AnalyticsProcessorOptions {
|
|
13
|
+
/** URL of the Flagsmith analytics events API endpoint
|
|
14
|
+
* @example https://flagsmith.example.com/api/v1/analytics
|
|
15
|
+
*/
|
|
16
|
+
analyticsUrl?: string;
|
|
17
|
+
/** Client-side key of the environment that analytics will be recorded for. **/
|
|
18
|
+
environmentKey: string;
|
|
19
|
+
/** Duration in milliseconds to wait for API requests to complete before timing out. Defaults to {@link DEFAULT_REQUEST_TIMEOUT_MS}. **/
|
|
20
|
+
requestTimeoutMs?: number;
|
|
21
|
+
logger?: Logger;
|
|
22
|
+
/** Custom {@link fetch} implementation to use for API requests. **/
|
|
23
|
+
fetch?: Fetch
|
|
24
|
+
|
|
25
|
+
/** @deprecated Use {@link analyticsUrl} instead. **/
|
|
26
|
+
baseApiUrl?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Tracks how often individual features are evaluated whenever {@link trackFeature} is called.
|
|
31
|
+
*
|
|
32
|
+
* Analytics data is posted after {@link trackFeature} is called and at least {@link ANALYTICS_TIMER} seconds have
|
|
33
|
+
* passed since the previous analytics API request was made (if any), or by calling {@link flush}.
|
|
34
|
+
*
|
|
35
|
+
* Data will stay in memory indefinitely until it can be successfully posted to the API.
|
|
36
|
+
* @see https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
37
|
+
*/
|
|
9
38
|
export class AnalyticsProcessor {
|
|
10
|
-
private
|
|
39
|
+
private analyticsUrl: string;
|
|
11
40
|
private environmentKey: string;
|
|
12
41
|
private lastFlushed: number;
|
|
13
42
|
analyticsData: { [key: string]: any };
|
|
14
|
-
private requestTimeoutMs: number =
|
|
43
|
+
private requestTimeoutMs: number = DEFAULT_REQUEST_TIMEOUT_MS;
|
|
15
44
|
private logger: Logger;
|
|
16
45
|
private currentFlush: ReturnType<typeof fetch> | undefined;
|
|
17
46
|
private customFetch: Fetch;
|
|
18
47
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
* the Flagsmith SDK. Docs: https://docs.flagsmith.com/advanced-use/flag-analytics.
|
|
22
|
-
*
|
|
23
|
-
* @param data.environmentKey environment key obtained from the Flagsmith UI
|
|
24
|
-
* @param data.baseApiUrl base api url to override when using self hosted version
|
|
25
|
-
* @param data.requestTimeoutMs used to tell requests to stop waiting for a response after a
|
|
26
|
-
given number of milliseconds
|
|
27
|
-
*/
|
|
28
|
-
constructor(data: { environmentKey: string; baseApiUrl: string; requestTimeoutMs?: number, logger?: Logger, fetch?: Fetch }) {
|
|
29
|
-
this.analyticsEndpoint = data.baseApiUrl + ANALYTICS_ENDPOINT;
|
|
48
|
+
constructor(data: AnalyticsProcessorOptions) {
|
|
49
|
+
this.analyticsUrl = data.analyticsUrl || data.baseApiUrl + ANALYTICS_ENDPOINT;
|
|
30
50
|
this.environmentKey = data.environmentKey;
|
|
31
51
|
this.lastFlushed = Date.now();
|
|
32
52
|
this.analyticsData = {};
|
|
@@ -35,7 +55,7 @@ export class AnalyticsProcessor {
|
|
|
35
55
|
this.customFetch = data.fetch ?? fetch;
|
|
36
56
|
}
|
|
37
57
|
/**
|
|
38
|
-
*
|
|
58
|
+
* Try to flush pending collected data to the Flagsmith analytics API.
|
|
39
59
|
*/
|
|
40
60
|
async flush() {
|
|
41
61
|
if (this.currentFlush || !Object.keys(this.analyticsData).length) {
|
|
@@ -43,7 +63,7 @@ export class AnalyticsProcessor {
|
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
try {
|
|
46
|
-
this.currentFlush = this.customFetch(this.
|
|
66
|
+
this.currentFlush = this.customFetch(this.analyticsUrl, {
|
|
47
67
|
method: 'POST',
|
|
48
68
|
body: JSON.stringify(this.analyticsData),
|
|
49
69
|
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
@@ -66,6 +86,11 @@ export class AnalyticsProcessor {
|
|
|
66
86
|
this.lastFlushed = Date.now();
|
|
67
87
|
}
|
|
68
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Track a single evaluation event for a feature.
|
|
91
|
+
*
|
|
92
|
+
* This method is called whenever {@link Flags.isFeatureEnabled}, {@link Flags.getFeatureValue} or {@link Flags.getFlag} are called.
|
|
93
|
+
*/
|
|
69
94
|
trackFeature(featureName: string) {
|
|
70
95
|
this.analyticsData[featureName] = (this.analyticsData[featureName] || 0) + 1;
|
|
71
96
|
if (Date.now() - this.lastFlushed > ANALYTICS_TIMER * 1000) {
|
package/sdk/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { buildEnvironmentModel } from '../flagsmith-engine/environments/util.js'
|
|
|
5
5
|
import { IdentityModel } from '../flagsmith-engine/index.js';
|
|
6
6
|
import { TraitModel } from '../flagsmith-engine/index.js';
|
|
7
7
|
|
|
8
|
-
import { AnalyticsProcessor
|
|
8
|
+
import {ANALYTICS_ENDPOINT, AnalyticsProcessor} from './analytics.js';
|
|
9
9
|
import { BaseOfflineHandler } from './offline_handlers.js';
|
|
10
10
|
import { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
|
|
11
11
|
|
|
@@ -17,7 +17,7 @@ import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators.js'
|
|
|
17
17
|
import { Fetch, FlagsmithCache, FlagsmithConfig, FlagsmithTraitValue, ITraitConfig } from './types.js';
|
|
18
18
|
import { pino, Logger } from 'pino';
|
|
19
19
|
|
|
20
|
-
export { AnalyticsProcessor } from './analytics.js';
|
|
20
|
+
export { AnalyticsProcessor, AnalyticsProcessorOptions } from './analytics.js';
|
|
21
21
|
export { FlagsmithAPIError, FlagsmithClientError } from './errors.js';
|
|
22
22
|
|
|
23
23
|
export { DefaultFlag, Flags } from './models.js';
|
|
@@ -30,6 +30,7 @@ const DEFAULT_REQUEST_TIMEOUT_SECONDS = 10;
|
|
|
30
30
|
export class Flagsmith {
|
|
31
31
|
environmentKey?: string = undefined;
|
|
32
32
|
apiUrl?: string = undefined;
|
|
33
|
+
analyticsUrl?: string = undefined;
|
|
33
34
|
customHeaders?: { [key: string]: any };
|
|
34
35
|
agent?: Dispatcher;
|
|
35
36
|
requestTimeoutMs?: number;
|
|
@@ -138,6 +139,7 @@ export class Flagsmith {
|
|
|
138
139
|
|
|
139
140
|
const apiUrl = data.apiUrl || DEFAULT_API_URL;
|
|
140
141
|
this.apiUrl = apiUrl.endsWith('/') ? apiUrl : `${apiUrl}/`;
|
|
142
|
+
this.analyticsUrl = this.analyticsUrl || new URL(ANALYTICS_ENDPOINT, new Request(this.apiUrl).url).href
|
|
141
143
|
this.environmentFlagsUrl = `${this.apiUrl}flags/`;
|
|
142
144
|
this.identitiesUrl = `${this.apiUrl}identities/`;
|
|
143
145
|
this.environmentUrl = `${this.apiUrl}environment-document/`;
|
|
@@ -156,14 +158,14 @@ export class Flagsmith {
|
|
|
156
158
|
this.updateEnvironment();
|
|
157
159
|
}
|
|
158
160
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
161
|
+
if (data.enableAnalytics) {
|
|
162
|
+
this.analyticsProcessor = new AnalyticsProcessor({
|
|
163
|
+
environmentKey: this.environmentKey,
|
|
164
|
+
analyticsUrl: this.analyticsUrl,
|
|
165
|
+
requestTimeoutMs: this.requestTimeoutMs,
|
|
166
|
+
logger: this.logger,
|
|
167
|
+
})
|
|
168
|
+
}
|
|
167
169
|
}
|
|
168
170
|
}
|
|
169
171
|
/**
|
|
@@ -26,7 +26,7 @@ test('test_analytics_processor_flush_post_request_data_match_ananlytics_data', a
|
|
|
26
26
|
aP.trackFeature("myFeature2");
|
|
27
27
|
await aP.flush();
|
|
28
28
|
expect(fetch).toHaveBeenCalledTimes(1);
|
|
29
|
-
expect(fetch).toHaveBeenCalledWith('http://
|
|
29
|
+
expect(fetch).toHaveBeenCalledWith('http://testUrl/analytics/flags/', expect.objectContaining({
|
|
30
30
|
body: '{"myFeature1":1,"myFeature2":1}',
|
|
31
31
|
headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'test-key' },
|
|
32
32
|
method: 'POST',
|
package/tests/sdk/utils.ts
CHANGED