@featbit/js-client-sdk 3.0.11 → 3.0.13
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/LICENSE +21 -21
- package/README.md +301 -301
- package/dist/esm/FbClientCore.d.ts.map +1 -1
- package/dist/esm/FbClientCore.js +1 -1
- package/dist/esm/FbClientCore.js.map +1 -1
- package/dist/esm/data-sources/DataSourceUpdates.d.ts +2 -2
- package/dist/esm/data-sources/DataSourceUpdates.d.ts.map +1 -1
- package/dist/esm/data-sources/DataSourceUpdates.js +55 -51
- package/dist/esm/data-sources/DataSourceUpdates.js.map +1 -1
- package/dist/esm/data-sources/createStreamListeners.d.ts +0 -9
- package/dist/esm/data-sources/createStreamListeners.d.ts.map +1 -1
- package/dist/esm/data-sources/createStreamListeners.js +10 -10
- package/dist/esm/data-sources/createStreamListeners.js.map +1 -1
- package/dist/esm/data-sync/IDataSynchronizer.d.ts +1 -1
- package/dist/esm/data-sync/IDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/NullDataSynchronizer.d.ts +1 -1
- package/dist/esm/data-sync/NullDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/NullDataSynchronizer.js +11 -0
- package/dist/esm/data-sync/NullDataSynchronizer.js.map +1 -1
- package/dist/esm/data-sync/PollingDataSynchronizer.d.ts +1 -1
- package/dist/esm/data-sync/PollingDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/PollingDataSynchronizer.js +42 -17
- package/dist/esm/data-sync/PollingDataSynchronizer.js.map +1 -1
- package/dist/esm/data-sync/WebSocketDataSynchronizer.d.ts +2 -1
- package/dist/esm/data-sync/WebSocketDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/data-sync/WebSocketDataSynchronizer.js +20 -6
- package/dist/esm/data-sync/WebSocketDataSynchronizer.js.map +1 -1
- package/dist/esm/data-sync/types.d.ts +1 -1
- package/dist/esm/data-sync/types.d.ts.map +1 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.d.ts +1 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.d.ts.map +1 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.js +3 -1
- package/dist/esm/integrations/test_data/TestDataSynchronizer.js.map +1 -1
- package/dist/esm/platform/browser/BrowserWebSocket.d.ts.map +1 -1
- package/dist/esm/platform/browser/BrowserWebSocket.js +4 -0
- package/dist/esm/platform/browser/BrowserWebSocket.js.map +1 -1
- package/dist/esm/store/IDataSourceUpdates.d.ts +2 -2
- package/dist/esm/store/IDataSourceUpdates.d.ts.map +1 -1
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/umd/{featbit-js-client-sdk-3.0.11.js → featbit-js-client-sdk-3.0.13.js} +2 -2
- package/dist/umd/featbit-js-client-sdk-3.0.13.js.map +1 -0
- package/dist/umd/featbit-js-client-sdk.js +1 -1
- package/dist/umd/featbit-js-client-sdk.js.map +1 -1
- package/package.json +46 -46
- package/src/Configuration.ts +232 -232
- package/src/Context.ts +61 -61
- package/src/FbClientBuilder.ts +167 -167
- package/src/FbClientCore.ts +405 -401
- package/src/IContextProperty.ts +3 -3
- package/src/IDataKind.ts +11 -11
- package/src/IFbClient.ts +29 -29
- package/src/IFbClientCore.ts +290 -290
- package/src/IVersionedData.ts +18 -18
- package/src/bootstrap/IBootstrapProvider.ts +4 -4
- package/src/bootstrap/JsonBootstrapProvider.ts +34 -34
- package/src/bootstrap/NullBootstrapProvider.ts +20 -20
- package/src/bootstrap/index.ts +2 -2
- package/src/constants.ts +1 -1
- package/src/data-sources/DataSourceUpdates.ts +116 -116
- package/src/data-sources/createStreamListeners.ts +67 -66
- package/src/data-sources/index.ts +1 -1
- package/src/data-sync/DataSyncMode.ts +3 -3
- package/src/data-sync/IDataSynchronizer.ts +15 -15
- package/src/data-sync/IRequestor.ts +10 -10
- package/src/data-sync/NullDataSynchronizer.ts +14 -14
- package/src/data-sync/PollingDataSynchronizer.ts +125 -111
- package/src/data-sync/Requestor.ts +61 -61
- package/src/data-sync/WebSocketDataSynchronizer.ts +77 -73
- package/src/data-sync/index.ts +8 -8
- package/src/data-sync/types.ts +19 -19
- package/src/data-sync/utils.ts +31 -31
- package/src/errors.ts +47 -47
- package/src/evaluation/EvalResult.ts +35 -35
- package/src/evaluation/Evaluator.ts +26 -26
- package/src/evaluation/IEvalDetail.ts +23 -23
- package/src/evaluation/ReasonKinds.ts +9 -9
- package/src/evaluation/data/IFlag.ts +29 -29
- package/src/evaluation/index.ts +4 -4
- package/src/events/DefaultEventProcessor.ts +83 -83
- package/src/events/DefaultEventQueue.ts +49 -49
- package/src/events/DefaultEventSender.ts +73 -73
- package/src/events/DefaultEventSerializer.ts +11 -11
- package/src/events/EventDispatcher.ts +127 -127
- package/src/events/EventSerializer.ts +4 -4
- package/src/events/IEventProcessor.ts +8 -8
- package/src/events/IEventQueue.ts +16 -16
- package/src/events/IEventSender.ts +13 -13
- package/src/events/NullEventProcessor.ts +15 -15
- package/src/events/event.ts +129 -129
- package/src/events/index.ts +11 -11
- package/src/index.ts +21 -21
- package/src/integrations/TestLogger.ts +24 -24
- package/src/integrations/index.ts +1 -1
- package/src/integrations/test_data/FlagBuilder.ts +59 -59
- package/src/integrations/test_data/TestData.ts +57 -57
- package/src/integrations/test_data/TestDataSynchronizer.ts +49 -49
- package/src/integrations/test_data/index.ts +4 -4
- package/src/logging/BasicLogger.ts +108 -108
- package/src/logging/IBasicLoggerOptions.ts +46 -46
- package/src/logging/ILogger.ts +49 -49
- package/src/logging/LogLevel.ts +8 -8
- package/src/logging/SafeLogger.ts +69 -69
- package/src/logging/format.ts +154 -154
- package/src/logging/index.ts +5 -5
- package/src/options/ClientContext.ts +39 -39
- package/src/options/IClientContext.ts +53 -53
- package/src/options/IOptions.ts +123 -123
- package/src/options/IUser.ts +6 -6
- package/src/options/IValidatedOptions.ts +29 -29
- package/src/options/OptionMessages.ts +35 -35
- package/src/options/UserBuilder.ts +35 -35
- package/src/options/Validators.ts +300 -300
- package/src/options/index.ts +7 -7
- package/src/platform/IInfo.ts +102 -102
- package/src/platform/IPlatform.ts +20 -20
- package/src/platform/IStore.ts +112 -112
- package/src/platform/IWebSocket.ts +22 -22
- package/src/platform/browser/BrowserInfo.ts +24 -24
- package/src/platform/browser/BrowserPlatform.ts +19 -19
- package/src/platform/browser/BrowserRequests.ts +6 -6
- package/src/platform/browser/BrowserWebSocket.ts +147 -142
- package/src/platform/browser/FbClient.ts +65 -65
- package/src/platform/browser/LocalStorageStore.ts +59 -59
- package/src/platform/index.ts +11 -11
- package/src/platform/requests.ts +76 -76
- package/src/store/BaseStore.ts +125 -125
- package/src/store/DataKinds.ts +6 -6
- package/src/store/IDataSourceUpdates.ts +68 -68
- package/src/store/InMemoryStore.ts +36 -36
- package/src/store/index.ts +5 -5
- package/src/store/serialization.ts +52 -52
- package/src/store/store.ts +37 -37
- package/src/utils/Emits.ts +75 -75
- package/src/utils/EventEmitter.ts +128 -128
- package/src/utils/IEventEmitter.ts +14 -14
- package/src/utils/Regex.ts +21 -21
- package/src/utils/ValueConverters.ts +55 -55
- package/src/utils/canonicalizeUri.ts +3 -3
- package/src/utils/debounce.ts +33 -33
- package/src/utils/http.ts +40 -40
- package/src/utils/index.ts +5 -5
- package/src/utils/isNullOrUndefined.ts +2 -2
- package/src/utils/serializeUser.ts +27 -27
- package/src/utils/sleep.ts +5 -5
- package/src/version.ts +1 -1
- package/dist/umd/featbit-js-client-sdk-3.0.11.js.map +0 -1
|
@@ -1,301 +1,301 @@
|
|
|
1
|
-
/* eslint-disable class-methods-use-this */
|
|
2
|
-
/* eslint-disable max-classes-per-file */
|
|
3
|
-
|
|
4
|
-
// The classes here are static, but needs to be instantiated to
|
|
5
|
-
// support the generic functionality. Which is why we do not care about using
|
|
6
|
-
// `this`
|
|
7
|
-
|
|
8
|
-
// These validators are also of trivial complexity, so we are allowing more than
|
|
9
|
-
// one per file.
|
|
10
|
-
|
|
11
|
-
import OptionMessages from "./OptionMessages";
|
|
12
|
-
import { IFlagBase } from "../evaluation";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Interface for type validation.
|
|
16
|
-
*/
|
|
17
|
-
export interface TypeValidator {
|
|
18
|
-
// holding validation error messages
|
|
19
|
-
messages?: string[];
|
|
20
|
-
|
|
21
|
-
is(u: unknown): boolean;
|
|
22
|
-
|
|
23
|
-
getType(): string;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Validate a factory or instance.
|
|
28
|
-
*/
|
|
29
|
-
export class FactoryOrInstance implements TypeValidator {
|
|
30
|
-
is(factoryOrInstance: unknown) {
|
|
31
|
-
if (Array.isArray(factoryOrInstance)) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
const anyFactory = factoryOrInstance as any;
|
|
35
|
-
const typeOfFactory = typeof anyFactory;
|
|
36
|
-
return typeOfFactory === 'function' || typeOfFactory === 'object';
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
getType(): string {
|
|
40
|
-
return 'factory method or object';
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Validate a basic type.
|
|
46
|
-
*/
|
|
47
|
-
export class Type<T> implements TypeValidator {
|
|
48
|
-
private typeName: string;
|
|
49
|
-
|
|
50
|
-
protected typeOf: string;
|
|
51
|
-
|
|
52
|
-
constructor(typeName: string, example: T) {
|
|
53
|
-
this.typeName = typeName;
|
|
54
|
-
this.typeOf = typeof example;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
is(u: unknown): u is T {
|
|
58
|
-
if (Array.isArray(u)) {
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
return typeof u === this.typeOf;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
getType(): string {
|
|
65
|
-
return this.typeName;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Validate an array of the specified type.
|
|
71
|
-
*
|
|
72
|
-
* This does not validate instances of types. All class instances
|
|
73
|
-
* of classes will simply objects.
|
|
74
|
-
*/
|
|
75
|
-
export class TypeArray<T> implements TypeValidator {
|
|
76
|
-
private typeName: string;
|
|
77
|
-
|
|
78
|
-
protected typeOf: string;
|
|
79
|
-
|
|
80
|
-
constructor(typeName: string, example: T) {
|
|
81
|
-
this.typeName = typeName;
|
|
82
|
-
this.typeOf = typeof example;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
is(u: unknown): u is T {
|
|
86
|
-
if (Array.isArray(u)) {
|
|
87
|
-
if (u.length > 0) {
|
|
88
|
-
return u.every((val) => typeof val === this.typeOf);
|
|
89
|
-
}
|
|
90
|
-
return true;
|
|
91
|
-
}
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
getType(): string {
|
|
96
|
-
return this.typeName;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Validate a value is a number and is greater or eval than a minimum.
|
|
102
|
-
*/
|
|
103
|
-
export class NumberWithMinimum extends Type<number> {
|
|
104
|
-
readonly min: number;
|
|
105
|
-
|
|
106
|
-
constructor(min: number) {
|
|
107
|
-
super(`number with minimum value of ${ min }`, 0);
|
|
108
|
-
this.min = min;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
override is(u: unknown): u is number {
|
|
112
|
-
return typeof u === this.typeOf && (u as number) >= this.min;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Validate a value is a string and it matches the given expression.
|
|
118
|
-
*/
|
|
119
|
-
export class StringMatchingRegex extends Type<string> {
|
|
120
|
-
readonly expression: RegExp;
|
|
121
|
-
|
|
122
|
-
constructor(expression: RegExp) {
|
|
123
|
-
super(`string matching ${ expression }`, '');
|
|
124
|
-
this.expression = expression;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
override is(u: unknown): u is string {
|
|
128
|
-
return !!(u as string).match(this.expression);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* Validate a value is a function.
|
|
134
|
-
*/
|
|
135
|
-
export class Function implements TypeValidator {
|
|
136
|
-
is(u: unknown): u is (...args: any[]) => void {
|
|
137
|
-
// We cannot inspect the parameters and there isn't really
|
|
138
|
-
// a generic function type we can instantiate.
|
|
139
|
-
// So the type guard is here just to make TS comfortable
|
|
140
|
-
// calling something after using this guard.
|
|
141
|
-
return typeof u === 'function';
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
getType(): string {
|
|
145
|
-
return 'function';
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export class NullableBoolean implements TypeValidator {
|
|
150
|
-
is(u: unknown): boolean {
|
|
151
|
-
return typeof u === 'boolean' || typeof u === 'undefined' || u === null;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
getType(): string {
|
|
155
|
-
return 'boolean | undefined | null';
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export class BootstrapValidator implements TypeValidator {
|
|
160
|
-
messages: string[] = [];
|
|
161
|
-
|
|
162
|
-
is(u: unknown): boolean {
|
|
163
|
-
if (typeof u !== 'object' || u === null) {
|
|
164
|
-
this.messages.push(OptionMessages.invalidOptionValue('bootstrap'));
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
try {
|
|
169
|
-
const bootstrap = u as IFlagBase[];
|
|
170
|
-
for (let flag of bootstrap) {
|
|
171
|
-
const hasMandatoryKeys = ['id', 'variation'].every((key) => Object.keys(flag).includes(key));
|
|
172
|
-
const keys = Object.keys(flag);
|
|
173
|
-
|
|
174
|
-
if (keys.includes('id')) {
|
|
175
|
-
this.messages.push(OptionMessages.missingKeyInBootstrapValue('id'));
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (keys.includes('variation')) {
|
|
179
|
-
this.messages.push(OptionMessages.missingKeyInBootstrapValue('variation'));
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (this.messages.length > 0) {
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
} catch (_) {
|
|
187
|
-
this.messages.push(OptionMessages.wrongOptionType('bootstrap', this.getType(), typeof u));
|
|
188
|
-
return false;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return true;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
getType(): string {
|
|
195
|
-
return 'IFlagBase[]';
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export class UserValidator implements TypeValidator {
|
|
200
|
-
messages: string[] = [];
|
|
201
|
-
|
|
202
|
-
is(u: unknown): boolean {
|
|
203
|
-
if (typeof u !== 'object' || u === null) {
|
|
204
|
-
this.messages.push(OptionMessages.mandatory('user'));
|
|
205
|
-
return false;
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
const user = u as any;
|
|
209
|
-
|
|
210
|
-
if (typeof user.keyId !== 'string' || user.keyId.trim() === '') {
|
|
211
|
-
this.messages.push(OptionMessages.mandatory('user.keyId'));
|
|
212
|
-
return false;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (typeof user.name !== 'string' || user.name.trim() === '') {
|
|
216
|
-
this.messages.push(OptionMessages.mandatory('user.name'));
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
getType(): string {
|
|
224
|
-
return 'user';
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Our reference SDK, Go, parses date/time strings with the time.RFC3339Nano format.
|
|
229
|
-
// This regex should match strings that are valid in that format, and no others.
|
|
230
|
-
// Acceptable:
|
|
231
|
-
// 2019-10-31T23:59:59Z, 2019-10-31T23:59:59.100Z,
|
|
232
|
-
// 2019-10-31T23:59:59-07, 2019-10-31T23:59:59-07:00, etc.
|
|
233
|
-
// Unacceptable: no "T", no time zone designation
|
|
234
|
-
const DATE_REGEX = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d\d*)?(Z|[-+]\d\d(:\d\d)?)/;
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Validate a value is a date. Values which are numbers are treated as dates and any string
|
|
238
|
-
* which if compliant with `time.RFC3339Nano` is a date.
|
|
239
|
-
*/
|
|
240
|
-
export class DateValidator implements TypeValidator {
|
|
241
|
-
is(u: unknown): boolean {
|
|
242
|
-
return typeof u === 'number' || (typeof u === 'string' && DATE_REGEX.test(u));
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
getType(): string {
|
|
246
|
-
return 'date';
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Validates that a string is a valid kind.
|
|
252
|
-
*/
|
|
253
|
-
export class KindValidator extends StringMatchingRegex {
|
|
254
|
-
constructor() {
|
|
255
|
-
super(/^(\w|\.|-)+$/);
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
override is(u: unknown): u is string {
|
|
259
|
-
return super.is(u) && u !== 'kind';
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/**
|
|
264
|
-
* A set of standard type validators.
|
|
265
|
-
*/
|
|
266
|
-
export class TypeValidators {
|
|
267
|
-
static readonly String = new Type<string>('string', '');
|
|
268
|
-
|
|
269
|
-
static readonly Number = new Type<number>('number', 0);
|
|
270
|
-
|
|
271
|
-
static readonly ObjectOrFactory = new FactoryOrInstance();
|
|
272
|
-
|
|
273
|
-
static readonly Object = new Type<object>('object', {});
|
|
274
|
-
|
|
275
|
-
static readonly StringArray = new TypeArray<string>('string[]', '');
|
|
276
|
-
|
|
277
|
-
static readonly Boolean = new Type<boolean>('boolean', true);
|
|
278
|
-
|
|
279
|
-
static readonly User = new Type<object>('object', {});
|
|
280
|
-
|
|
281
|
-
static readonly Bootstrap = new Type<object>('object', {});
|
|
282
|
-
|
|
283
|
-
static readonly Function = new Function();
|
|
284
|
-
|
|
285
|
-
static createTypeArray<T>(typeName: string, example: T) {
|
|
286
|
-
return new TypeArray<T>(typeName, example);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
static numberWithMin(min: number): NumberWithMinimum {
|
|
290
|
-
return new NumberWithMinimum(min);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
static stringMatchingRegex(expression: RegExp): StringMatchingRegex {
|
|
294
|
-
return new StringMatchingRegex(expression);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
static readonly Date = new DateValidator();
|
|
298
|
-
|
|
299
|
-
static readonly Kind = new KindValidator();
|
|
300
|
-
static readonly NullableBoolean = new NullableBoolean();
|
|
1
|
+
/* eslint-disable class-methods-use-this */
|
|
2
|
+
/* eslint-disable max-classes-per-file */
|
|
3
|
+
|
|
4
|
+
// The classes here are static, but needs to be instantiated to
|
|
5
|
+
// support the generic functionality. Which is why we do not care about using
|
|
6
|
+
// `this`
|
|
7
|
+
|
|
8
|
+
// These validators are also of trivial complexity, so we are allowing more than
|
|
9
|
+
// one per file.
|
|
10
|
+
|
|
11
|
+
import OptionMessages from "./OptionMessages";
|
|
12
|
+
import { IFlagBase } from "../evaluation";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Interface for type validation.
|
|
16
|
+
*/
|
|
17
|
+
export interface TypeValidator {
|
|
18
|
+
// holding validation error messages
|
|
19
|
+
messages?: string[];
|
|
20
|
+
|
|
21
|
+
is(u: unknown): boolean;
|
|
22
|
+
|
|
23
|
+
getType(): string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Validate a factory or instance.
|
|
28
|
+
*/
|
|
29
|
+
export class FactoryOrInstance implements TypeValidator {
|
|
30
|
+
is(factoryOrInstance: unknown) {
|
|
31
|
+
if (Array.isArray(factoryOrInstance)) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
const anyFactory = factoryOrInstance as any;
|
|
35
|
+
const typeOfFactory = typeof anyFactory;
|
|
36
|
+
return typeOfFactory === 'function' || typeOfFactory === 'object';
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getType(): string {
|
|
40
|
+
return 'factory method or object';
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Validate a basic type.
|
|
46
|
+
*/
|
|
47
|
+
export class Type<T> implements TypeValidator {
|
|
48
|
+
private typeName: string;
|
|
49
|
+
|
|
50
|
+
protected typeOf: string;
|
|
51
|
+
|
|
52
|
+
constructor(typeName: string, example: T) {
|
|
53
|
+
this.typeName = typeName;
|
|
54
|
+
this.typeOf = typeof example;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
is(u: unknown): u is T {
|
|
58
|
+
if (Array.isArray(u)) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return typeof u === this.typeOf;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getType(): string {
|
|
65
|
+
return this.typeName;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Validate an array of the specified type.
|
|
71
|
+
*
|
|
72
|
+
* This does not validate instances of types. All class instances
|
|
73
|
+
* of classes will simply objects.
|
|
74
|
+
*/
|
|
75
|
+
export class TypeArray<T> implements TypeValidator {
|
|
76
|
+
private typeName: string;
|
|
77
|
+
|
|
78
|
+
protected typeOf: string;
|
|
79
|
+
|
|
80
|
+
constructor(typeName: string, example: T) {
|
|
81
|
+
this.typeName = typeName;
|
|
82
|
+
this.typeOf = typeof example;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
is(u: unknown): u is T {
|
|
86
|
+
if (Array.isArray(u)) {
|
|
87
|
+
if (u.length > 0) {
|
|
88
|
+
return u.every((val) => typeof val === this.typeOf);
|
|
89
|
+
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
getType(): string {
|
|
96
|
+
return this.typeName;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Validate a value is a number and is greater or eval than a minimum.
|
|
102
|
+
*/
|
|
103
|
+
export class NumberWithMinimum extends Type<number> {
|
|
104
|
+
readonly min: number;
|
|
105
|
+
|
|
106
|
+
constructor(min: number) {
|
|
107
|
+
super(`number with minimum value of ${ min }`, 0);
|
|
108
|
+
this.min = min;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
override is(u: unknown): u is number {
|
|
112
|
+
return typeof u === this.typeOf && (u as number) >= this.min;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Validate a value is a string and it matches the given expression.
|
|
118
|
+
*/
|
|
119
|
+
export class StringMatchingRegex extends Type<string> {
|
|
120
|
+
readonly expression: RegExp;
|
|
121
|
+
|
|
122
|
+
constructor(expression: RegExp) {
|
|
123
|
+
super(`string matching ${ expression }`, '');
|
|
124
|
+
this.expression = expression;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
override is(u: unknown): u is string {
|
|
128
|
+
return !!(u as string).match(this.expression);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Validate a value is a function.
|
|
134
|
+
*/
|
|
135
|
+
export class Function implements TypeValidator {
|
|
136
|
+
is(u: unknown): u is (...args: any[]) => void {
|
|
137
|
+
// We cannot inspect the parameters and there isn't really
|
|
138
|
+
// a generic function type we can instantiate.
|
|
139
|
+
// So the type guard is here just to make TS comfortable
|
|
140
|
+
// calling something after using this guard.
|
|
141
|
+
return typeof u === 'function';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
getType(): string {
|
|
145
|
+
return 'function';
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export class NullableBoolean implements TypeValidator {
|
|
150
|
+
is(u: unknown): boolean {
|
|
151
|
+
return typeof u === 'boolean' || typeof u === 'undefined' || u === null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
getType(): string {
|
|
155
|
+
return 'boolean | undefined | null';
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export class BootstrapValidator implements TypeValidator {
|
|
160
|
+
messages: string[] = [];
|
|
161
|
+
|
|
162
|
+
is(u: unknown): boolean {
|
|
163
|
+
if (typeof u !== 'object' || u === null) {
|
|
164
|
+
this.messages.push(OptionMessages.invalidOptionValue('bootstrap'));
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const bootstrap = u as IFlagBase[];
|
|
170
|
+
for (let flag of bootstrap) {
|
|
171
|
+
const hasMandatoryKeys = ['id', 'variation'].every((key) => Object.keys(flag).includes(key));
|
|
172
|
+
const keys = Object.keys(flag);
|
|
173
|
+
|
|
174
|
+
if (keys.includes('id')) {
|
|
175
|
+
this.messages.push(OptionMessages.missingKeyInBootstrapValue('id'));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (keys.includes('variation')) {
|
|
179
|
+
this.messages.push(OptionMessages.missingKeyInBootstrapValue('variation'));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (this.messages.length > 0) {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
} catch (_) {
|
|
187
|
+
this.messages.push(OptionMessages.wrongOptionType('bootstrap', this.getType(), typeof u));
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
getType(): string {
|
|
195
|
+
return 'IFlagBase[]';
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export class UserValidator implements TypeValidator {
|
|
200
|
+
messages: string[] = [];
|
|
201
|
+
|
|
202
|
+
is(u: unknown): boolean {
|
|
203
|
+
if (typeof u !== 'object' || u === null) {
|
|
204
|
+
this.messages.push(OptionMessages.mandatory('user'));
|
|
205
|
+
return false;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const user = u as any;
|
|
209
|
+
|
|
210
|
+
if (typeof user.keyId !== 'string' || user.keyId.trim() === '') {
|
|
211
|
+
this.messages.push(OptionMessages.mandatory('user.keyId'));
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (typeof user.name !== 'string' || user.name.trim() === '') {
|
|
216
|
+
this.messages.push(OptionMessages.mandatory('user.name'));
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
getType(): string {
|
|
224
|
+
return 'user';
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Our reference SDK, Go, parses date/time strings with the time.RFC3339Nano format.
|
|
229
|
+
// This regex should match strings that are valid in that format, and no others.
|
|
230
|
+
// Acceptable:
|
|
231
|
+
// 2019-10-31T23:59:59Z, 2019-10-31T23:59:59.100Z,
|
|
232
|
+
// 2019-10-31T23:59:59-07, 2019-10-31T23:59:59-07:00, etc.
|
|
233
|
+
// Unacceptable: no "T", no time zone designation
|
|
234
|
+
const DATE_REGEX = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d\d*)?(Z|[-+]\d\d(:\d\d)?)/;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Validate a value is a date. Values which are numbers are treated as dates and any string
|
|
238
|
+
* which if compliant with `time.RFC3339Nano` is a date.
|
|
239
|
+
*/
|
|
240
|
+
export class DateValidator implements TypeValidator {
|
|
241
|
+
is(u: unknown): boolean {
|
|
242
|
+
return typeof u === 'number' || (typeof u === 'string' && DATE_REGEX.test(u));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
getType(): string {
|
|
246
|
+
return 'date';
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Validates that a string is a valid kind.
|
|
252
|
+
*/
|
|
253
|
+
export class KindValidator extends StringMatchingRegex {
|
|
254
|
+
constructor() {
|
|
255
|
+
super(/^(\w|\.|-)+$/);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
override is(u: unknown): u is string {
|
|
259
|
+
return super.is(u) && u !== 'kind';
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* A set of standard type validators.
|
|
265
|
+
*/
|
|
266
|
+
export class TypeValidators {
|
|
267
|
+
static readonly String = new Type<string>('string', '');
|
|
268
|
+
|
|
269
|
+
static readonly Number = new Type<number>('number', 0);
|
|
270
|
+
|
|
271
|
+
static readonly ObjectOrFactory = new FactoryOrInstance();
|
|
272
|
+
|
|
273
|
+
static readonly Object = new Type<object>('object', {});
|
|
274
|
+
|
|
275
|
+
static readonly StringArray = new TypeArray<string>('string[]', '');
|
|
276
|
+
|
|
277
|
+
static readonly Boolean = new Type<boolean>('boolean', true);
|
|
278
|
+
|
|
279
|
+
static readonly User = new Type<object>('object', {});
|
|
280
|
+
|
|
281
|
+
static readonly Bootstrap = new Type<object>('object', {});
|
|
282
|
+
|
|
283
|
+
static readonly Function = new Function();
|
|
284
|
+
|
|
285
|
+
static createTypeArray<T>(typeName: string, example: T) {
|
|
286
|
+
return new TypeArray<T>(typeName, example);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
static numberWithMin(min: number): NumberWithMinimum {
|
|
290
|
+
return new NumberWithMinimum(min);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
static stringMatchingRegex(expression: RegExp): StringMatchingRegex {
|
|
294
|
+
return new StringMatchingRegex(expression);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static readonly Date = new DateValidator();
|
|
298
|
+
|
|
299
|
+
static readonly Kind = new KindValidator();
|
|
300
|
+
static readonly NullableBoolean = new NullableBoolean();
|
|
301
301
|
}
|
package/src/options/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * from './ClientContext';
|
|
2
|
-
export * from './IClientContext';
|
|
3
|
-
export * from './IOptions';
|
|
4
|
-
export * from './IUser';
|
|
5
|
-
export * from './IValidatedOptions';
|
|
6
|
-
export * from './OptionMessages';
|
|
7
|
-
export * from './UserBuilder';
|
|
1
|
+
export * from './ClientContext';
|
|
2
|
+
export * from './IClientContext';
|
|
3
|
+
export * from './IOptions';
|
|
4
|
+
export * from './IUser';
|
|
5
|
+
export * from './IValidatedOptions';
|
|
6
|
+
export * from './OptionMessages';
|
|
7
|
+
export * from './UserBuilder';
|
|
8
8
|
export * from './Validators';
|