1
1
import {
2
2
EvaluationContext ,
3
- FlagNotFoundError ,
4
3
JsonValue ,
5
4
OpenFeatureEventEmitter ,
6
5
Paradigm ,
@@ -13,12 +12,20 @@ import {
13
12
} from '@openfeature/web-sdk' ;
14
13
import {
15
14
isType ,
15
+ parseError ,
16
16
PrimitiveType ,
17
17
PrimitiveTypeName ,
18
18
toResolutionDetails ,
19
19
transformContext ,
20
20
} from '@openfeature/config-cat-core' ;
21
- import { getClient , IConfig , IConfigCatClient , OptionsForPollingMode , PollingMode } from 'configcat-js-ssr' ;
21
+ import {
22
+ getClient ,
23
+ IConfig ,
24
+ IConfigCatClient ,
25
+ OptionsForPollingMode ,
26
+ PollingMode ,
27
+ SettingValue ,
28
+ } from 'configcat-js-ssr' ;
22
29
23
30
export class ConfigCatWebProvider implements Provider {
24
31
public readonly events = new OpenFeatureEventEmitter ( ) ;
@@ -84,71 +91,81 @@ export class ConfigCatWebProvider implements Provider {
84
91
defaultValue : boolean ,
85
92
context : EvaluationContext ,
86
93
) : ResolutionDetails < boolean > {
87
- return this . evaluate ( flagKey , 'boolean' , context ) ;
94
+ return this . evaluate ( flagKey , 'boolean' , defaultValue , context ) ;
88
95
}
89
96
90
97
public resolveStringEvaluation (
91
98
flagKey : string ,
92
99
defaultValue : string ,
93
100
context : EvaluationContext ,
94
101
) : ResolutionDetails < string > {
95
- return this . evaluate ( flagKey , 'string' , context ) ;
102
+ return this . evaluate ( flagKey , 'string' , defaultValue , context ) ;
96
103
}
97
104
98
105
public resolveNumberEvaluation (
99
106
flagKey : string ,
100
107
defaultValue : number ,
101
108
context : EvaluationContext ,
102
109
) : ResolutionDetails < number > {
103
- return this . evaluate ( flagKey , 'number' , context ) ;
110
+ return this . evaluate ( flagKey , 'number' , defaultValue , context ) ;
104
111
}
105
112
106
113
public resolveObjectEvaluation < U extends JsonValue > (
107
114
flagKey : string ,
108
115
defaultValue : U ,
109
116
context : EvaluationContext ,
110
117
) : ResolutionDetails < U > {
111
- const objectValue = this . evaluate ( flagKey , 'object' , context ) ;
118
+ const objectValue = this . evaluate ( flagKey , 'object' , defaultValue , context ) ;
112
119
return objectValue as ResolutionDetails < U > ;
113
120
}
114
121
115
122
protected evaluate < T extends PrimitiveTypeName > (
116
123
flagKey : string ,
117
124
flagType : T ,
125
+ defaultValue : PrimitiveType < T > ,
118
126
context : EvaluationContext ,
119
127
) : ResolutionDetails < PrimitiveType < T > > {
120
128
if ( ! this . _client ) {
121
129
throw new ProviderNotReadyError ( 'Provider is not initialized' ) ;
122
130
}
123
131
132
+ // Make sure that the user-provided `defaultValue` is compatible with `flagType` as there is
133
+ // no guarantee that it actually is. (User may bypass type checking or may not use TypeScript at all.)
134
+ if ( ! isType ( flagType , defaultValue ) ) {
135
+ throw new TypeMismatchError ( ) ;
136
+ }
137
+
138
+ const configCatDefaultValue = flagType !== 'object' ? ( defaultValue as SettingValue ) : JSON . stringify ( defaultValue ) ;
139
+
124
140
const { value, ...evaluationData } = this . _client
125
141
. snapshot ( )
126
- . getValueDetails ( flagKey , undefined , transformContext ( context ) ) ;
142
+ . getValueDetails ( flagKey , configCatDefaultValue , transformContext ( context ) ) ;
127
143
128
144
if ( this . _hasError && ! evaluationData . errorMessage && ! evaluationData . errorException ) {
129
145
this . _hasError = false ;
130
146
this . events . emit ( ProviderEvents . Ready ) ;
131
147
}
132
148
133
- if ( typeof value === 'undefined' ) {
134
- throw new FlagNotFoundError ( ) ;
149
+ if ( evaluationData . isDefaultValue ) {
150
+ throw parseError ( evaluationData . errorMessage ) ;
135
151
}
136
152
137
153
if ( flagType !== 'object' ) {
138
- return toResolutionDetails ( flagType , value , evaluationData ) ;
139
- }
140
-
141
- if ( ! isType ( 'string' , value ) ) {
142
- throw new TypeMismatchError ( ) ;
154
+ // When `flagType` (more precisely, `configCatDefaultValue`) is boolean, string or number,
155
+ // ConfigCat SDK guarantees that the returned `value` is compatible with `PrimitiveType<T>`.
156
+ // See also: https://configcat.com/docs/sdk-reference/js-ssr/#setting-type-mapping
157
+ return toResolutionDetails ( value as PrimitiveType < T > , evaluationData ) ;
143
158
}
144
159
145
160
let json : JsonValue ;
146
161
try {
147
- json = JSON . parse ( value ) ;
162
+ // In this case we can be sure that `value` is string since `configCatDefaultValue` is string,
163
+ // which means that ConfigCat SDK is guaranteed to return a string value.
164
+ json = JSON . parse ( value as string ) ;
148
165
} catch ( e ) {
149
166
throw new ParseError ( `Unable to parse "${ value } " as JSON` ) ;
150
167
}
151
168
152
- return toResolutionDetails ( flagType , json , evaluationData ) ;
169
+ return toResolutionDetails ( json as PrimitiveType < T > , evaluationData ) ;
153
170
}
154
171
}
0 commit comments