@splitsoftware/openfeature-js-split-provider 1.0.6 → 1.1.0

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/CHANGES.txt CHANGED
@@ -1,14 +1,26 @@
1
- 1.0.0
2
- - First release. Up to date with spec 0.4.0, and @openfeature/nodejs-sdk v0.2.0
3
- 1.0.1
4
- - Fixes issues with flag details and error codes in negative cases, adds unit tests
5
- - Up to date with spec 0.4.0 and @openfeature/nodejs-sdk v0.3.2
6
- 1.0.2
7
- - Changes name from Node-specific implementation to generic JSON
8
- - Up to date with spec 0.4.0 and @openfeature/js-sdk 0.4.0
9
- 1.0.3
10
- - Adds types definitions for TypeScript
11
- - Up to date with spec 0.4.0 and @openfeature/js-sdk 0.4.0
1
+ 1.1.0 (September 12, 2025)
2
+ - Updated @openfeature/server-sdk to 1.19.0
3
+ - Updated @splitsoftware/splitio to 11.4.1
4
+ - Added support for tracking feature
5
+ - Added support for evaluate with details feature
6
+ - Added support for provider initialization using splitFactory and apiKey
7
+ - Replace @openfeature/js-sdk with @openfeature/server-sdk
8
+
12
9
  1.0.4
13
- - Fixes issue with TS build
14
- - Up to date with spec 0.5.0 and @openfeature/js-sdk 0.5.0
10
+ - Fixes issue with TS build
11
+ - Up to date with spec 0.5.0 and @openfeature/js-sdk 0.5.0
12
+
13
+ 1.0.3
14
+ - Adds types definitions for TypeScript
15
+ - Up to date with spec 0.4.0 and @openfeature/js-sdk 0.4.0
16
+
17
+ 1.0.2
18
+ - Changes name from Node-specific implementation to generic JSON
19
+ - Up to date with spec 0.4.0 and @openfeature/js-sdk 0.4.0
20
+
21
+ 1.0.1
22
+ - Fixes issues with flag details and error codes in negative cases, adds unit tests
23
+ - Up to date with spec 0.4.0 and @openfeature/nodejs-sdk v0.3.2
24
+
25
+ 1.0.0
26
+ - First release. Up to date with spec 0.4.0, and @openfeature/nodejs-sdk v0.2.0
package/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright © 2022 Split Software, Inc.
1
+ Copyright © 2025 Split Software, Inc.
2
2
 
3
3
  Licensed under the Apache License, Version 2.0 (the "License");
4
4
  you may not use this file except in compliance with the License.
package/README.md CHANGED
@@ -5,6 +5,7 @@
5
5
  This Provider is designed to allow the use of OpenFeature with Split, the platform for controlled rollouts, serving features to your users via the Split feature flag to manage your complete customer experience.
6
6
 
7
7
  ## Compatibility
8
+ It supports **Node.js version 14.x or later**.
8
9
 
9
10
 
10
11
  ## Getting started
@@ -19,19 +20,41 @@ npm install @splitsoftware/openfeature-js-split-provider
19
20
  ### Confirm peer dependencies are installed
20
21
  ```sh
21
22
  npm install @splitsoftware/splitio
22
- npm install @openfeature/js-sdk
23
+ npm install @openfeature/server-sdk
23
24
  ```
24
25
 
25
- ### Register the Split provider with OpenFeature
26
+ ### Register the Split provider with OpenFeature using sdk apiKey
26
27
  ```js
27
- const OpenFeature = require('@openfeature/js-sdk').OpenFeature;
28
+ const OpenFeature = require('@openfeature/server-sdk').OpenFeature;
29
+ const OpenFeatureSplitProvider = require('@splitsoftware/openfeature-js-split-provider').OpenFeatureSplitProvider;
30
+
31
+ const authorizationKey = 'your auth key'
32
+ const provider = new OpenFeatureSplitProvider(authorizationKey);
33
+ OpenFeature.setProvider(provider);
34
+ ```
35
+
36
+ ### Register the Split provider with OpenFeature using splitFactory
37
+ ```js
38
+ const OpenFeature = require('@openfeature/server-sdk').OpenFeature;
39
+ const SplitFactory = require('@splitsoftware/splitio').SplitFactory;
40
+ const OpenFeatureSplitProvider = require('@splitsoftware/openfeature-js-split-provider').OpenFeatureSplitProvider;
41
+
42
+ const authorizationKey = 'your auth key'
43
+ const splitFactory = SplitFactory({core: {authorizationKey}});
44
+ const provider = new OpenFeatureSplitProvider(splitFactory);
45
+ OpenFeature.setProvider(provider);
46
+ ```
47
+
48
+ ### Register the Split provider with OpenFeature using splitClient
49
+ ```js
50
+ const OpenFeature = require('@openfeature/server-sdk').OpenFeature;
28
51
  const SplitFactory = require('@splitsoftware/splitio').SplitFactory;
29
52
  const OpenFeatureSplitProvider = require('@splitsoftware/openfeature-js-split-provider').OpenFeatureSplitProvider;
30
53
 
31
54
  const authorizationKey = 'your auth key'
32
55
  const splitClient = SplitFactory({core: {authorizationKey}}).client();
33
56
  const provider = new OpenFeatureSplitProvider({splitClient});
34
- openFeature.setProvider(provider);
57
+ OpenFeature.setProvider(provider);
35
58
  ```
36
59
 
37
60
  ## Use of OpenFeature with Split
@@ -59,9 +82,40 @@ const context: EvaluationContext = {
59
82
  targetingKey: 'TARGETING_KEY',
60
83
  };
61
84
  OpenFeatureAPI.getInstance().setCtx(context)
62
- ````
85
+ ```
63
86
  If the context was set at the client or api level, it is not required to provide it during flag evaluation.
64
87
 
88
+ ## Evaluate with details
89
+ Use the get*Details(...) APIs to get the value and rich context (variant, reason, error code, metadata). This provider includes the Split treatment config as a raw JSON string under flagMetadata["config"]
90
+
91
+ ```js
92
+ const booleanTreatment = await client.getBooleanDetails('boolFlag', false, context);
93
+
94
+ const config = booleanTreatment.flagMetadata.config
95
+ ```
96
+
97
+ ## Tracking
98
+
99
+ To use track(eventName, context, details) you must provide:
100
+
101
+ - A non-blank `eventName`.
102
+ - A context with:
103
+ - `targetingKey` (non-blank).
104
+ - `trafficType` (string, e.g. "user" or "account").
105
+
106
+ Optional:
107
+
108
+ - details with:
109
+ - `value`: numeric event value (defaults to 0).
110
+ - `properties`: map of attributes (prefer primitives: string/number/boolean/null).
111
+
112
+ Example:
113
+ ```js
114
+ const context = { targetingKey: 'user-123', trafficType: 'account' }
115
+ const details = { value: 19.99, plan: 'pro', coupon: 'WELCOME10' }
116
+
117
+ client.track('checkout.completed', context, details)
118
+ ```
65
119
  ## Submitting issues
66
120
 
67
121
  The Split team monitors all issues submitted to this [issue tracker](https://github.com/splitio/split-openfeature-provider-nodejs/issues). We encourage you to use this issue tracker to submit any bug reports, feedback, and feature enhancements. We'll do our best to respond in a timely manner.
@@ -80,16 +134,24 @@ To learn more about Split, contact hello@split.io, or get started with feature f
80
134
 
81
135
  Split has built and maintains SDKs for:
82
136
 
83
- * Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
84
- * Javascript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
85
- * Node [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
86
137
  * .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
87
- * Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
88
- * PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
89
- * Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
90
- * GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
91
138
  * Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
139
+ * Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
140
+ * Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://help.split.io/hc/en-us/articles/26988707417869-Elixir-Thin-Client-SDK)
141
+ * Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin)
142
+ * GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
92
143
  * iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
144
+ * Java [Github](https://github.com/splitio/java-client) [Docs](https://help.split.io/hc/en-us/articles/360020405151-Java-SDK)
145
+ * JavaScript [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK)
146
+ * JavaScript for Browser [Github](https://github.com/splitio/javascript-browser-client) [Docs](https://help.split.io/hc/en-us/articles/360058730852-Browser-SDK)
147
+ * Node.js [Github](https://github.com/splitio/javascript-client) [Docs](https://help.split.io/hc/en-us/articles/360020564931-Node-js-SDK)
148
+ * PHP [Github](https://github.com/splitio/php-client) [Docs](https://help.split.io/hc/en-us/articles/360020350372-PHP-SDK)
149
+ * PHP thin-client [Github](https://github.com/splitio/php-thin-client) [Docs](https://help.split.io/hc/en-us/articles/18305128673933-PHP-Thin-Client-SDK)
150
+ * Python [Github](https://github.com/splitio/python-client) [Docs](https://help.split.io/hc/en-us/articles/360020359652-Python-SDK)
151
+ * React [Github](https://github.com/splitio/react-client) [Docs](https://help.split.io/hc/en-us/articles/360038825091-React-SDK)
152
+ * React Native [Github](https://github.com/splitio/react-native-client) [Docs](https://help.split.io/hc/en-us/articles/4406066357901-React-Native-SDK)
153
+ * Redux [Github](https://github.com/splitio/redux-client) [Docs](https://help.split.io/hc/en-us/articles/360038851551-Redux-SDK)
154
+ * Ruby [Github](https://github.com/splitio/ruby-client) [Docs](https://help.split.io/hc/en-us/articles/360020673251-Ruby-SDK)
93
155
 
94
156
  For a comprehensive list of open source projects visit our [Github page](https://github.com/splitio?utf8=%E2%9C%93&query=%20only%3Apublic%20).
95
157
 
package/es/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './lib/js-split-provider.js';
@@ -0,0 +1,152 @@
1
+ import { FlagNotFoundError, InvalidContextError, OpenFeatureEventEmitter, ParseError, ProviderEvents, StandardResolutionReasons, TargetingKeyMissingError } from '@openfeature/server-sdk';
2
+ import { SplitFactory } from '@splitsoftware/splitio';
3
+ const CONTROL_VALUE_ERROR_MESSAGE = 'Received the "control" value from Split.';
4
+ const CONTROL_TREATMENT = 'control';
5
+ export class OpenFeatureSplitProvider {
6
+ getSplitClient(options) {
7
+ if (typeof (options) === 'string') {
8
+ const splitFactory = SplitFactory({ core: { authorizationKey: options } });
9
+ return splitFactory.client();
10
+ }
11
+ let splitClient;
12
+ try {
13
+ splitClient = options.client();
14
+ }
15
+ catch {
16
+ splitClient = options.splitClient;
17
+ }
18
+ return splitClient;
19
+ }
20
+ constructor(options) {
21
+ this.metadata = {
22
+ name: 'split',
23
+ };
24
+ this.events = new OpenFeatureEventEmitter();
25
+ this.client = this.getSplitClient(options);
26
+ this.client.on(this.client.Event.SDK_UPDATE, () => {
27
+ this.events.emit(ProviderEvents.ConfigurationChanged);
28
+ });
29
+ this.initialized = new Promise((resolve) => {
30
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
+ if (this.client.__getStatus().isReady) {
32
+ console.log(`${this.metadata.name} provider initialized`);
33
+ resolve();
34
+ }
35
+ else {
36
+ this.client.on(this.client.Event.SDK_READY, () => {
37
+ console.log(`${this.metadata.name} provider initialized`);
38
+ resolve();
39
+ });
40
+ }
41
+ });
42
+ }
43
+ async resolveBooleanEvaluation(flagKey, _, context) {
44
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
45
+ const treatment = details.value.toLowerCase();
46
+ if (treatment === 'on' || treatment === 'true') {
47
+ return { ...details, value: true };
48
+ }
49
+ if (treatment === 'off' || treatment === 'false') {
50
+ return { ...details, value: false };
51
+ }
52
+ throw new ParseError(`Invalid boolean value for ${treatment}`);
53
+ }
54
+ async resolveStringEvaluation(flagKey, _, context) {
55
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
56
+ return details;
57
+ }
58
+ async resolveNumberEvaluation(flagKey, _, context) {
59
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
60
+ return { ...details, value: this.parseValidNumber(details.value) };
61
+ }
62
+ async resolveObjectEvaluation(flagKey, _, context) {
63
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
64
+ return { ...details, value: this.parseValidJsonObject(details.value) };
65
+ }
66
+ async evaluateTreatment(flagKey, consumer) {
67
+ if (!consumer.key) {
68
+ throw new TargetingKeyMissingError('The Split provider requires a targeting key.');
69
+ }
70
+ if (flagKey == null || flagKey === '') {
71
+ throw new FlagNotFoundError('flagKey must be a non-empty string');
72
+ }
73
+ await this.initialized;
74
+ const { treatment: value, config } = await this.client.getTreatmentWithConfig(consumer.key, flagKey, consumer.attributes);
75
+ if (value === CONTROL_TREATMENT) {
76
+ throw new FlagNotFoundError(CONTROL_VALUE_ERROR_MESSAGE);
77
+ }
78
+ const flagMetadata = { config: config ? config : '' };
79
+ const details = {
80
+ value: value,
81
+ variant: value,
82
+ flagMetadata: flagMetadata,
83
+ reason: StandardResolutionReasons.TARGETING_MATCH,
84
+ };
85
+ return details;
86
+ }
87
+ async track(trackingEventName, context, details) {
88
+ // targetingKey is always required
89
+ const { targetingKey } = context;
90
+ if (targetingKey == null || targetingKey === '')
91
+ throw new TargetingKeyMissingError('Missing targetingKey, required to track');
92
+ // eventName is always required
93
+ if (trackingEventName == null || trackingEventName === '')
94
+ throw new ParseError('Missing eventName, required to track');
95
+ // trafficType is always required
96
+ const ttVal = context['trafficType'];
97
+ const trafficType = ttVal != null && typeof ttVal === 'string' && ttVal.trim() !== ''
98
+ ? ttVal
99
+ : null;
100
+ if (trafficType == null || trafficType === '')
101
+ throw new InvalidContextError('Missing trafficType variable, required to track');
102
+ let value;
103
+ let properties = {};
104
+ if (details != null) {
105
+ if (details.value != null) {
106
+ value = details.value;
107
+ }
108
+ if (details.properties != null) {
109
+ properties = details.properties;
110
+ }
111
+ }
112
+ this.client.track(targetingKey, trafficType, trackingEventName, value, properties);
113
+ }
114
+ async onClose() {
115
+ return this.client.destroy();
116
+ }
117
+ //Transform the context into an object useful for the Split API, an key string with arbitrary Split 'Attributes'.
118
+ transformContext(context) {
119
+ const { targetingKey, ...attributes } = context;
120
+ return {
121
+ key: targetingKey,
122
+ // Stringify context objects include date.
123
+ attributes: JSON.parse(JSON.stringify(attributes)),
124
+ };
125
+ }
126
+ parseValidNumber(stringValue) {
127
+ if (stringValue === undefined) {
128
+ throw new ParseError(`Invalid 'undefined' value.`);
129
+ }
130
+ const result = Number.parseFloat(stringValue);
131
+ if (Number.isNaN(result)) {
132
+ throw new ParseError(`Invalid numeric value ${stringValue}`);
133
+ }
134
+ return result;
135
+ }
136
+ parseValidJsonObject(stringValue) {
137
+ if (stringValue === undefined) {
138
+ throw new ParseError(`Invalid 'undefined' JSON value.`);
139
+ }
140
+ // we may want to allow the parsing to be customized.
141
+ try {
142
+ const value = JSON.parse(stringValue);
143
+ if (typeof value !== 'object') {
144
+ throw new ParseError(`Flag value ${stringValue} had unexpected type ${typeof value}, expected "object"`);
145
+ }
146
+ return value;
147
+ }
148
+ catch (err) {
149
+ throw new ParseError(`Error parsing ${stringValue} as JSON, ${err}`);
150
+ }
151
+ }
152
+ }
package/lib/index.js ADDED
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./lib/js-split-provider"), exports);
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OpenFeatureSplitProvider = void 0;
4
+ const server_sdk_1 = require("@openfeature/server-sdk");
5
+ const splitio_1 = require("@splitsoftware/splitio");
6
+ const CONTROL_VALUE_ERROR_MESSAGE = 'Received the "control" value from Split.';
7
+ const CONTROL_TREATMENT = 'control';
8
+ class OpenFeatureSplitProvider {
9
+ getSplitClient(options) {
10
+ if (typeof (options) === 'string') {
11
+ const splitFactory = (0, splitio_1.SplitFactory)({ core: { authorizationKey: options } });
12
+ return splitFactory.client();
13
+ }
14
+ let splitClient;
15
+ try {
16
+ splitClient = options.client();
17
+ }
18
+ catch {
19
+ splitClient = options.splitClient;
20
+ }
21
+ return splitClient;
22
+ }
23
+ constructor(options) {
24
+ this.metadata = {
25
+ name: 'split',
26
+ };
27
+ this.events = new server_sdk_1.OpenFeatureEventEmitter();
28
+ this.client = this.getSplitClient(options);
29
+ this.client.on(this.client.Event.SDK_UPDATE, () => {
30
+ this.events.emit(server_sdk_1.ProviderEvents.ConfigurationChanged);
31
+ });
32
+ this.initialized = new Promise((resolve) => {
33
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
+ if (this.client.__getStatus().isReady) {
35
+ console.log(`${this.metadata.name} provider initialized`);
36
+ resolve();
37
+ }
38
+ else {
39
+ this.client.on(this.client.Event.SDK_READY, () => {
40
+ console.log(`${this.metadata.name} provider initialized`);
41
+ resolve();
42
+ });
43
+ }
44
+ });
45
+ }
46
+ async resolveBooleanEvaluation(flagKey, _, context) {
47
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
48
+ const treatment = details.value.toLowerCase();
49
+ if (treatment === 'on' || treatment === 'true') {
50
+ return { ...details, value: true };
51
+ }
52
+ if (treatment === 'off' || treatment === 'false') {
53
+ return { ...details, value: false };
54
+ }
55
+ throw new server_sdk_1.ParseError(`Invalid boolean value for ${treatment}`);
56
+ }
57
+ async resolveStringEvaluation(flagKey, _, context) {
58
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
59
+ return details;
60
+ }
61
+ async resolveNumberEvaluation(flagKey, _, context) {
62
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
63
+ return { ...details, value: this.parseValidNumber(details.value) };
64
+ }
65
+ async resolveObjectEvaluation(flagKey, _, context) {
66
+ const details = await this.evaluateTreatment(flagKey, this.transformContext(context));
67
+ return { ...details, value: this.parseValidJsonObject(details.value) };
68
+ }
69
+ async evaluateTreatment(flagKey, consumer) {
70
+ if (!consumer.key) {
71
+ throw new server_sdk_1.TargetingKeyMissingError('The Split provider requires a targeting key.');
72
+ }
73
+ if (flagKey == null || flagKey === '') {
74
+ throw new server_sdk_1.FlagNotFoundError('flagKey must be a non-empty string');
75
+ }
76
+ await this.initialized;
77
+ const { treatment: value, config } = await this.client.getTreatmentWithConfig(consumer.key, flagKey, consumer.attributes);
78
+ if (value === CONTROL_TREATMENT) {
79
+ throw new server_sdk_1.FlagNotFoundError(CONTROL_VALUE_ERROR_MESSAGE);
80
+ }
81
+ const flagMetadata = { config: config ? config : '' };
82
+ const details = {
83
+ value: value,
84
+ variant: value,
85
+ flagMetadata: flagMetadata,
86
+ reason: server_sdk_1.StandardResolutionReasons.TARGETING_MATCH,
87
+ };
88
+ return details;
89
+ }
90
+ async track(trackingEventName, context, details) {
91
+ // targetingKey is always required
92
+ const { targetingKey } = context;
93
+ if (targetingKey == null || targetingKey === '')
94
+ throw new server_sdk_1.TargetingKeyMissingError('Missing targetingKey, required to track');
95
+ // eventName is always required
96
+ if (trackingEventName == null || trackingEventName === '')
97
+ throw new server_sdk_1.ParseError('Missing eventName, required to track');
98
+ // trafficType is always required
99
+ const ttVal = context['trafficType'];
100
+ const trafficType = ttVal != null && typeof ttVal === 'string' && ttVal.trim() !== ''
101
+ ? ttVal
102
+ : null;
103
+ if (trafficType == null || trafficType === '')
104
+ throw new server_sdk_1.InvalidContextError('Missing trafficType variable, required to track');
105
+ let value;
106
+ let properties = {};
107
+ if (details != null) {
108
+ if (details.value != null) {
109
+ value = details.value;
110
+ }
111
+ if (details.properties != null) {
112
+ properties = details.properties;
113
+ }
114
+ }
115
+ this.client.track(targetingKey, trafficType, trackingEventName, value, properties);
116
+ }
117
+ async onClose() {
118
+ return this.client.destroy();
119
+ }
120
+ //Transform the context into an object useful for the Split API, an key string with arbitrary Split 'Attributes'.
121
+ transformContext(context) {
122
+ const { targetingKey, ...attributes } = context;
123
+ return {
124
+ key: targetingKey,
125
+ // Stringify context objects include date.
126
+ attributes: JSON.parse(JSON.stringify(attributes)),
127
+ };
128
+ }
129
+ parseValidNumber(stringValue) {
130
+ if (stringValue === undefined) {
131
+ throw new server_sdk_1.ParseError(`Invalid 'undefined' value.`);
132
+ }
133
+ const result = Number.parseFloat(stringValue);
134
+ if (Number.isNaN(result)) {
135
+ throw new server_sdk_1.ParseError(`Invalid numeric value ${stringValue}`);
136
+ }
137
+ return result;
138
+ }
139
+ parseValidJsonObject(stringValue) {
140
+ if (stringValue === undefined) {
141
+ throw new server_sdk_1.ParseError(`Invalid 'undefined' JSON value.`);
142
+ }
143
+ // we may want to allow the parsing to be customized.
144
+ try {
145
+ const value = JSON.parse(stringValue);
146
+ if (typeof value !== 'object') {
147
+ throw new server_sdk_1.ParseError(`Flag value ${stringValue} had unexpected type ${typeof value}, expected "object"`);
148
+ }
149
+ return value;
150
+ }
151
+ catch (err) {
152
+ throw new server_sdk_1.ParseError(`Error parsing ${stringValue} as JSON, ${err}`);
153
+ }
154
+ }
155
+ }
156
+ exports.OpenFeatureSplitProvider = OpenFeatureSplitProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@splitsoftware/openfeature-js-split-provider",
3
- "version": "1.0.6",
3
+ "version": "1.1.0",
4
4
  "description": "Split OpenFeature Provider",
5
5
  "files": [
6
6
  "README.md",
@@ -17,29 +17,47 @@
17
17
  "bugs": "https://github.com/splitio/openfeature-split-provider-js/issues",
18
18
  "license": "Apache-2.0",
19
19
  "author": "Josh Sirota <josh.sirota@split.io>",
20
- "main": "src/index.js",
21
- "types": "types",
20
+ "contributors": [
21
+ "Nicolas Zelaya <nicolas.zelaya@harness.io> (https://github.com/NicoZelaya)",
22
+ "Emiliano Sanchez <emiliano.sanchez@harness.io> (https://github.com/EmilianoSanchez)",
23
+ "Emmanuel Zamora <emmanuel.zamora@harness.io> (https://github.com/ZamoraEmmanuel)",
24
+ "SDK Team <sdks@harness.io>"
25
+ ],
26
+ "main": "lib/index.js",
27
+ "types": "types/index.d.ts",
22
28
  "engines": {
23
- "npm": ">=3",
24
- "node": ">=6"
29
+ "node": ">=14"
30
+ },
31
+ "exports": {
32
+ ".": {
33
+ "require": "./lib/index.js",
34
+ "import": "./es/index.js"
35
+ }
25
36
  },
26
- "dependencies": {},
27
37
  "peerDependencies": {
28
- "@openfeature/js-sdk": "^1.0.0",
29
- "@splitsoftware/splitio": "^10.22.1"
38
+ "@openfeature/server-sdk": "^1.19.0",
39
+ "@splitsoftware/splitio": "^11.4.1"
30
40
  },
31
41
  "devDependencies": {
32
- "@openfeature/js-sdk": "^1.0.0",
33
- "@splitsoftware/splitio": "^10.22.1",
42
+ "@eslint/js": "^9.35.0",
43
+ "@openfeature/server-sdk": "^1.19.0",
44
+ "@splitsoftware/splitio": "^11.4.1",
45
+ "@types/jest": "^30.0.0",
46
+ "@types/node": "^24.3.1",
34
47
  "copyfiles": "^2.4.1",
35
48
  "cross-env": "^7.0.3",
49
+ "eslint": "^9.35.0",
50
+ "eslint-plugin-jest": "^28.14.0",
51
+ "globals": "^16.3.0",
52
+ "jest": "^29.7.0",
53
+ "jiti": "^2.5.1",
54
+ "redis-server": "^1.2.2",
36
55
  "replace": "^1.2.1",
37
56
  "rimraf": "^3.0.2",
38
- "tap-min": "^2.0.0",
39
- "tape": "4.13.2",
40
- "tape-catch": "1.0.6",
57
+ "ts-jest": "^29.4.1",
41
58
  "ts-node": "^10.5.0",
42
- "typescript": "4.4.4"
59
+ "typescript": "^4.9.5",
60
+ "typescript-eslint": "^8.43.0"
43
61
  },
44
62
  "scripts": {
45
63
  "build-esm": "rimraf es && tsc -outDir es",
@@ -47,20 +65,10 @@
47
65
  "build-cjs": "rimraf lib && tsc -outDir lib -m CommonJS",
48
66
  "postbuild-cjs": "cross-env NODE_ENV=cjs node scripts/copy.packages.json.js && ./scripts/build_cjs_replace_imports.sh",
49
67
  "build": "rimraf lib es && npm run build-cjs && npm run build-esm",
50
- "check": "npm run check:version",
51
- "check:version": "cross-env NODE_ENV=test tape -r ./ts-node.register src/settings/__tests__/defaults.spec.js",
52
- "pretest-ts-decls": "npm run build-esm && npm run build-cjs && npm link",
53
- "test-ts-decls": "./scripts/ts-tests.sh",
54
- "posttest-ts-decls": "npm unlink && npm install",
55
- "test": "cross-env NODE_ENV=test tape -r ./ts-node.register src/__tests__/node.spec.js | tap-min",
56
- "publish:rc": "npm run check && npm run build && npm publish --tag canary",
57
- "publish:stable": "npm run check && npm run build && npm publish"
58
- },
59
- "greenkeeper": {
60
- "ignore": [
61
- "karma",
62
- "karma-tap",
63
- "karma-webpack"
64
- ]
68
+ "check": "npm run check:lint",
69
+ "check:lint": "eslint src",
70
+ "test": "cross-env NODE_ENV=test jest",
71
+ "publish:rc": "npm run check && npm run test && npm run build && npm publish --tag rc",
72
+ "publish:stable": "npm run check && npm run test && npm run build && npm publish"
65
73
  }
66
74
  }
@@ -0,0 +1,11 @@
1
+ FLUSHDB
2
+ DEL 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT'
3
+ SADD 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT' UT_Segment_member
4
+ SET 'REDIS_NODE_UT.SPLITIO.segment.UT_SEGMENT.till' 1492721958710
5
+ SET 'REDIS_NODE_UT.SPLITIO.split.UT_IN_SEGMENT' '{"changeNumber":1492722104980,"trafficTypeName":"machine","name":"UT_IN_SEGMENT","seed":-202209840,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"","attribute":""},"matcherType":"IN_SEGMENT","negate":false,"userDefinedSegmentMatcherData":{"segmentName":"UT_SEGMENT"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100}],"label":"whitelisted segment"},{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":0},{"treatment":"off","size":100}],"label":"in segment all"}]}'
6
+ SET 'REDIS_NODE_UT.SPLITIO.split.UT_NOT_IN_SEGMENT' '{"changeNumber":1492722747908,"trafficTypeName":"machine","name":"UT_NOT_IN_SEGMENT","seed":-56653132,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":""},"matcherType":"IN_SEGMENT","negate":true,"userDefinedSegmentMatcherData":{"segmentName":"UT_SEGMENT"},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"not in segment UT_SEGMENT"}]}'
7
+ SET 'REDIS_NODE_UT.SPLITIO.split.UT_NOT_SET_MATCHER' '{"changeNumber":1492723024413,"trafficTypeName":"machine","name":"UT_NOT_SET_MATCHER","seed":-93553840,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":"permissions"},"matcherType":"CONTAINS_ANY_OF_SET","negate":true,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["create","delete","update"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"permissions does not contain any of [create, delete, ...]"}]}'
8
+ SET 'REDIS_NODE_UT.SPLITIO.split.UT_SET_MATCHER' '{"changeNumber":1492722926004,"trafficTypeName":"machine","name":"UT_SET_MATCHER","seed":-1995997836,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"machine","attribute":"permissions"},"matcherType":"CONTAINS_ANY_OF_SET","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":["admin","premium","idol"]},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"permissions contains any of [admin, premium, ...]"}]}'
9
+ SET 'REDIS_NODE_UT.SPLITIO.split.always-on' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-on","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"on","size":100},{"treatment":"off","size":0}],"label":"in segment all"}]}'
10
+ SET 'REDIS_NODE_UT.SPLITIO.split.always-o.n-with-config' '{"changeNumber":1487277320548,"trafficTypeName":"user","name":"always-o.n-with-config","seed":1684183541,"status":"ACTIVE","killed":false,"defaultTreatment":"off","conditions":[{"matcherGroup":{"combiner":"AND","matchers":[{"keySelector":{"trafficType":"user","attribute":""},"matcherType":"ALL_KEYS","negate":false,"userDefinedSegmentMatcherData":{"segmentName":""},"unaryNumericMatcherData":{"dataType":"","value":0},"whitelistMatcherData":{"whitelist":null},"betweenMatcherData":{"dataType":"","start":0,"end":0}}]},"partitions":[{"treatment":"o.n","size":100},{"treatment":"off","size":0}],"label":"in segment all"}],"configurations":{"o.n":"{\"color\":\"brown\"}"}}'
11
+ SET 'REDIS_NODE_UT.SPLITIO.splits.till' 1492723024413