@openfeature/flagd-provider 0.10.2 → 0.10.4
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/README.md +15 -3
- package/index.cjs.js +158 -40
- package/index.esm.js +158 -40
- package/package.json +3 -3
- package/src/lib/configuration.d.ts +6 -0
- package/src/lib/service/grpc/grpc-service.d.ts +1 -1
- package/src/lib/service/in-process/data-fetch.d.ts +28 -2
- package/src/lib/service/in-process/file/file-fetch.d.ts +9 -0
- package/src/lib/service/in-process/grpc/grpc-fetch.d.ts +16 -4
- package/src/lib/service/in-process/in-process-service.d.ts +14 -2
- package/src/lib/service/service.d.ts +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# Server-Side flagd Provider
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
It is designed to conform to OpenFeature schema for flag definitions.
|
|
3
|
+
This provider is designed to use flagd's [evaluation protocol](https://github.com/open-feature/schemas/blob/main/protobuf/schema/v1/schema.proto), or locally evaluate flags defined in a flagd [flag definition](https://github.com/open-feature/schemas/blob/main/json/flagd-definitions.json).
|
|
5
4
|
This repository and package provides the client code for interacting with it via the OpenFeature server-side JavaScript SDK.
|
|
6
5
|
|
|
7
6
|
## Installation
|
|
@@ -45,7 +44,7 @@ Below are examples of usage patterns.
|
|
|
45
44
|
|
|
46
45
|
This is the default mode of operation of the provider.
|
|
47
46
|
In this mode, FlagdProvider communicates with flagd via the gRPC protocol.
|
|
48
|
-
Flag evaluations take place remotely
|
|
47
|
+
Flag evaluations take place remotely on the connected [flagd](https://flagd.dev/) instance.
|
|
49
48
|
|
|
50
49
|
```ts
|
|
51
50
|
OpenFeature.setProvider(new FlagdProvider())
|
|
@@ -74,6 +73,19 @@ Flag configurations for evaluation are obtained via gRPC protocol using [sync pr
|
|
|
74
73
|
|
|
75
74
|
In the above example, the provider expects a flag sync service implementation to be available at `localhost:8013` (default host and port).
|
|
76
75
|
|
|
76
|
+
In-process resolver can also work in an offline mode.
|
|
77
|
+
To enable this mode, you should provide a valid flag configuration file with the option `offlineFlagSourcePath`.
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
OpenFeature.setProvider(new FlagdProvider({
|
|
81
|
+
resolverType: 'in-process',
|
|
82
|
+
offlineFlagSourcePath: './flags.json',
|
|
83
|
+
}))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Offline mode uses `fs.watchFile` and polls every 5 seconds for changes to the file.
|
|
87
|
+
This mode is useful for local development, test cases, and for offline applications.
|
|
88
|
+
|
|
77
89
|
### Supported Events
|
|
78
90
|
|
|
79
91
|
The flagd provider emits `PROVIDER_READY`, `PROVIDER_ERROR` and `PROVIDER_CONFIGURATION_CHANGED` events.
|
package/index.cjs.js
CHANGED
|
@@ -8,6 +8,8 @@ var connectivityState = require('@grpc/grpc-js/build/src/connectivity-state');
|
|
|
8
8
|
var lruCache = require('lru-cache');
|
|
9
9
|
var util$6 = require('util');
|
|
10
10
|
var flagdCore = require('@openfeature/flagd-core');
|
|
11
|
+
var core = require('@openfeature/core');
|
|
12
|
+
var fs = require('fs');
|
|
11
13
|
|
|
12
14
|
const EVENT_CONFIGURATION_CHANGE = 'configuration_change';
|
|
13
15
|
const EVENT_PROVIDER_READY = 'provider_ready';
|
|
@@ -5942,13 +5944,15 @@ class GRPCService {
|
|
|
5942
5944
|
if (data && typeof data === 'object' && 'flags' in data && (data === null || data === void 0 ? void 0 : data['flags'])) {
|
|
5943
5945
|
const flagChangeMessage = data;
|
|
5944
5946
|
const flagsChanged = Object.keys(flagChangeMessage.flags || []);
|
|
5945
|
-
|
|
5946
|
-
|
|
5947
|
-
|
|
5948
|
-
|
|
5949
|
-
(
|
|
5950
|
-
|
|
5951
|
-
|
|
5947
|
+
if (this._cacheEnabled) {
|
|
5948
|
+
// remove each changed key from cache
|
|
5949
|
+
flagsChanged.forEach((key) => {
|
|
5950
|
+
var _a, _b;
|
|
5951
|
+
if ((_a = this._cache) === null || _a === void 0 ? void 0 : _a.delete(key)) {
|
|
5952
|
+
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(`${FlagdProvider.name}: evicted key: ${key} from cache.`);
|
|
5953
|
+
}
|
|
5954
|
+
});
|
|
5955
|
+
}
|
|
5952
5956
|
changedCallback(flagsChanged);
|
|
5953
5957
|
}
|
|
5954
5958
|
}
|
|
@@ -5961,7 +5965,7 @@ class GRPCService {
|
|
|
5961
5965
|
}
|
|
5962
5966
|
handleError(reconnectCallback, changedCallback, disconnectCallback) {
|
|
5963
5967
|
var _a, _b;
|
|
5964
|
-
disconnectCallback();
|
|
5968
|
+
disconnectCallback('streaming connection error, will attempt reconnect...');
|
|
5965
5969
|
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`${FlagdProvider.name}: streaming connection error, will attempt reconnect...`);
|
|
5966
5970
|
(_b = this._cache) === null || _b === void 0 ? void 0 : _b.clear();
|
|
5967
5971
|
this.reconnect(reconnectCallback, changedCallback, disconnectCallback);
|
|
@@ -6356,6 +6360,18 @@ function isSet(value) {
|
|
|
6356
6360
|
*/
|
|
6357
6361
|
class GrpcFetch {
|
|
6358
6362
|
constructor(config, syncServiceClient, logger) {
|
|
6363
|
+
/**
|
|
6364
|
+
* Initialized will be set to true once the initial connection is successful
|
|
6365
|
+
* and the first payload has been received. Subsequent reconnects will not
|
|
6366
|
+
* change the initialized value.
|
|
6367
|
+
*/
|
|
6368
|
+
this._initialized = false;
|
|
6369
|
+
/**
|
|
6370
|
+
* Is connected represents the current known connection state. It will be
|
|
6371
|
+
* set to true once the first payload has been received.but will be set to
|
|
6372
|
+
* false if the connection is lost.
|
|
6373
|
+
*/
|
|
6374
|
+
this._isConnected = false;
|
|
6359
6375
|
const { host, port, tls, socketPath, selector } = config;
|
|
6360
6376
|
this._syncClient = syncServiceClient
|
|
6361
6377
|
? syncServiceClient
|
|
@@ -6363,54 +6379,131 @@ class GrpcFetch {
|
|
|
6363
6379
|
this._logger = logger;
|
|
6364
6380
|
this._request = { providerId: '', selector: selector ? selector : '' };
|
|
6365
6381
|
}
|
|
6366
|
-
connect(
|
|
6367
|
-
return new Promise((resolve, reject) => this.listen(
|
|
6382
|
+
connect(dataCallback, reconnectCallback, changedCallback, disconnectCallback) {
|
|
6383
|
+
return new Promise((resolve, reject) => this.listen(dataCallback, reconnectCallback, changedCallback, disconnectCallback, resolve, reject)).then(() => {
|
|
6384
|
+
this._initialized = true;
|
|
6385
|
+
});
|
|
6368
6386
|
}
|
|
6369
6387
|
disconnect() {
|
|
6370
6388
|
var _a;
|
|
6371
|
-
(
|
|
6372
|
-
|
|
6373
|
-
|
|
6389
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6390
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Disconnecting gRPC sync connection');
|
|
6391
|
+
closeStreamIfDefined(this._syncStream);
|
|
6392
|
+
this._syncClient.close();
|
|
6393
|
+
});
|
|
6374
6394
|
}
|
|
6375
|
-
listen(
|
|
6395
|
+
listen(dataCallback, reconnectCallback, changedCallback, disconnectCallback, resolveConnect, rejectConnect) {
|
|
6396
|
+
var _a;
|
|
6397
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Starting gRPC sync connection');
|
|
6376
6398
|
closeStreamIfDefined(this._syncStream);
|
|
6377
6399
|
this._syncStream = this._syncClient.syncFlags(this._request);
|
|
6378
6400
|
this._syncStream.on('data', (data) => {
|
|
6379
|
-
var _a;
|
|
6380
|
-
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(
|
|
6381
|
-
|
|
6382
|
-
|
|
6383
|
-
|
|
6401
|
+
var _a, _b, _c, _d;
|
|
6402
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`Received sync payload`);
|
|
6403
|
+
try {
|
|
6404
|
+
const changes = dataCallback(data.flagConfiguration);
|
|
6405
|
+
if (this._initialized && changes.length > 0) {
|
|
6406
|
+
changedCallback(changes);
|
|
6407
|
+
}
|
|
6408
|
+
}
|
|
6409
|
+
catch (err) {
|
|
6410
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug('Error processing sync payload: ', (_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : 'unknown error');
|
|
6411
|
+
}
|
|
6384
6412
|
if (resolveConnect) {
|
|
6385
6413
|
resolveConnect();
|
|
6386
6414
|
}
|
|
6387
|
-
else {
|
|
6415
|
+
else if (!this._isConnected) {
|
|
6416
|
+
// Not the first connection and there's no active connection.
|
|
6417
|
+
(_d = this._logger) === null || _d === void 0 ? void 0 : _d.debug('Reconnected to gRPC sync');
|
|
6388
6418
|
reconnectCallback();
|
|
6389
6419
|
}
|
|
6420
|
+
this._isConnected = true;
|
|
6390
6421
|
});
|
|
6391
6422
|
this._syncStream.on('error', (err) => {
|
|
6392
|
-
var _a;
|
|
6393
|
-
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error('Connection error, attempting to reconnect'
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6423
|
+
var _a, _b, _c;
|
|
6424
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error('Connection error, attempting to reconnect');
|
|
6425
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(err);
|
|
6426
|
+
this._isConnected = false;
|
|
6427
|
+
const errorMessage = (_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : 'Failed to connect to syncFlags stream';
|
|
6428
|
+
disconnectCallback(errorMessage);
|
|
6429
|
+
rejectConnect === null || rejectConnect === void 0 ? void 0 : rejectConnect(new serverSdk.GeneralError(errorMessage));
|
|
6430
|
+
this.reconnect(dataCallback, reconnectCallback, changedCallback, disconnectCallback);
|
|
6397
6431
|
});
|
|
6398
6432
|
}
|
|
6399
|
-
reconnect(
|
|
6433
|
+
reconnect(dataCallback, reconnectCallback, changedCallback, disconnectCallback) {
|
|
6400
6434
|
const channel = this._syncClient.getChannel();
|
|
6401
6435
|
channel.watchConnectivityState(channel.getConnectivityState(true), Infinity, () => {
|
|
6402
|
-
this.listen(
|
|
6436
|
+
this.listen(dataCallback, reconnectCallback, changedCallback, disconnectCallback);
|
|
6437
|
+
});
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
|
|
6441
|
+
const encoding = 'utf8';
|
|
6442
|
+
class FileFetch {
|
|
6443
|
+
constructor(filename, logger) {
|
|
6444
|
+
this._filename = filename;
|
|
6445
|
+
this._logger = logger;
|
|
6446
|
+
}
|
|
6447
|
+
connect(dataFillCallback, _, changedCallback) {
|
|
6448
|
+
var _a, _b;
|
|
6449
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6450
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Starting file sync connection');
|
|
6451
|
+
try {
|
|
6452
|
+
const output = yield fs.promises.readFile(this._filename, encoding);
|
|
6453
|
+
// Don't emit the change event for the initial read
|
|
6454
|
+
dataFillCallback(output);
|
|
6455
|
+
// Using watchFile instead of watch to support virtualized host file systems.
|
|
6456
|
+
fs.watchFile(this._filename, () => __awaiter(this, void 0, void 0, function* () {
|
|
6457
|
+
var _c;
|
|
6458
|
+
try {
|
|
6459
|
+
const data = yield fs.promises.readFile(this._filename, encoding);
|
|
6460
|
+
const changes = dataFillCallback(data);
|
|
6461
|
+
if (changes.length > 0) {
|
|
6462
|
+
changedCallback(changes);
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
catch (err) {
|
|
6466
|
+
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.error(`Error reading file: ${err}`);
|
|
6467
|
+
}
|
|
6468
|
+
}));
|
|
6469
|
+
}
|
|
6470
|
+
catch (err) {
|
|
6471
|
+
if (err instanceof core.OpenFeatureError) {
|
|
6472
|
+
throw err;
|
|
6473
|
+
}
|
|
6474
|
+
else {
|
|
6475
|
+
switch (err === null || err === void 0 ? void 0 : err.code) {
|
|
6476
|
+
case 'ENOENT':
|
|
6477
|
+
throw new core.GeneralError(`File not found: ${this._filename}`);
|
|
6478
|
+
case 'EACCES':
|
|
6479
|
+
throw new core.GeneralError(`File not accessible: ${this._filename}`);
|
|
6480
|
+
default:
|
|
6481
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(`Error reading file: ${err}`);
|
|
6482
|
+
throw new core.GeneralError();
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
});
|
|
6487
|
+
}
|
|
6488
|
+
disconnect() {
|
|
6489
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6490
|
+
fs.unwatchFile(this._filename);
|
|
6403
6491
|
});
|
|
6404
6492
|
}
|
|
6405
6493
|
}
|
|
6406
6494
|
|
|
6407
6495
|
class InProcessService {
|
|
6408
6496
|
constructor(config, dataFetcher, logger) {
|
|
6409
|
-
this.
|
|
6410
|
-
this.
|
|
6497
|
+
this.config = config;
|
|
6498
|
+
this._flagdCore = new flagdCore.FlagdCore(undefined, logger);
|
|
6499
|
+
this._dataFetcher = dataFetcher
|
|
6500
|
+
? dataFetcher
|
|
6501
|
+
: config.offlineFlagSourcePath
|
|
6502
|
+
? new FileFetch(config.offlineFlagSourcePath, logger)
|
|
6503
|
+
: new GrpcFetch(config, undefined, logger);
|
|
6411
6504
|
}
|
|
6412
6505
|
connect(reconnectCallback, changedCallback, disconnectCallback) {
|
|
6413
|
-
return this._dataFetcher.connect(this.
|
|
6506
|
+
return this._dataFetcher.connect(this.setFlagConfiguration.bind(this), reconnectCallback, changedCallback, disconnectCallback);
|
|
6414
6507
|
}
|
|
6415
6508
|
disconnect() {
|
|
6416
6509
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -6418,19 +6511,43 @@ class InProcessService {
|
|
|
6418
6511
|
});
|
|
6419
6512
|
}
|
|
6420
6513
|
resolveBoolean(flagKey, defaultValue, context, logger) {
|
|
6421
|
-
return
|
|
6514
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6515
|
+
return this.evaluate('boolean', flagKey, defaultValue, context, logger);
|
|
6516
|
+
});
|
|
6422
6517
|
}
|
|
6423
6518
|
resolveNumber(flagKey, defaultValue, context, logger) {
|
|
6424
|
-
return
|
|
6519
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6520
|
+
return this.evaluate('number', flagKey, defaultValue, context, logger);
|
|
6521
|
+
});
|
|
6425
6522
|
}
|
|
6426
6523
|
resolveString(flagKey, defaultValue, context, logger) {
|
|
6427
|
-
return
|
|
6524
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6525
|
+
return this.evaluate('string', flagKey, defaultValue, context, logger);
|
|
6526
|
+
});
|
|
6428
6527
|
}
|
|
6429
6528
|
resolveObject(flagKey, defaultValue, context, logger) {
|
|
6430
|
-
return
|
|
6529
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6530
|
+
return this.evaluate('object', flagKey, defaultValue, context, logger);
|
|
6531
|
+
});
|
|
6532
|
+
}
|
|
6533
|
+
evaluate(type, flagKey, defaultValue, context, logger) {
|
|
6534
|
+
const details = this._flagdCore.resolve(type, flagKey, defaultValue, context, logger);
|
|
6535
|
+
return Object.assign(Object.assign({}, details), { flagMetadata: this.addFlagMetadata() });
|
|
6431
6536
|
}
|
|
6432
|
-
|
|
6433
|
-
|
|
6537
|
+
/**
|
|
6538
|
+
* Adds the flag metadata to the resolution details
|
|
6539
|
+
*/
|
|
6540
|
+
addFlagMetadata() {
|
|
6541
|
+
return Object.assign({}, (this.config.selector ? { scope: this.config.selector } : {}));
|
|
6542
|
+
}
|
|
6543
|
+
/**
|
|
6544
|
+
* Sets the flag configuration
|
|
6545
|
+
* @param flags The flags to set as stringified JSON
|
|
6546
|
+
* @returns {string[]} The flags that have changed
|
|
6547
|
+
* @throws — {Error} If the configuration string is invalid.
|
|
6548
|
+
*/
|
|
6549
|
+
setFlagConfiguration(flags) {
|
|
6550
|
+
return this._flagdCore.setConfigurations(flags);
|
|
6434
6551
|
}
|
|
6435
6552
|
}
|
|
6436
6553
|
|
|
@@ -6477,9 +6594,10 @@ class FlagdProvider {
|
|
|
6477
6594
|
this._status = serverSdk.ProviderStatus.READY;
|
|
6478
6595
|
})
|
|
6479
6596
|
.catch((err) => {
|
|
6480
|
-
var _a;
|
|
6597
|
+
var _a, _b;
|
|
6481
6598
|
this._status = serverSdk.ProviderStatus.ERROR;
|
|
6482
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`${this.metadata.name}: error during initialization: ${err.message}
|
|
6599
|
+
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`${this.metadata.name}: error during initialization: ${err.message}`);
|
|
6600
|
+
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(err);
|
|
6483
6601
|
throw err;
|
|
6484
6602
|
});
|
|
6485
6603
|
}
|
|
@@ -6512,9 +6630,9 @@ class FlagdProvider {
|
|
|
6512
6630
|
this._status = serverSdk.ProviderStatus.READY;
|
|
6513
6631
|
this._events.emit(serverSdk.ProviderEvents.Ready);
|
|
6514
6632
|
}
|
|
6515
|
-
handleError() {
|
|
6633
|
+
handleError(message) {
|
|
6516
6634
|
this._status = serverSdk.ProviderStatus.ERROR;
|
|
6517
|
-
this._events.emit(serverSdk.ProviderEvents.Error);
|
|
6635
|
+
this._events.emit(serverSdk.ProviderEvents.Error, { message });
|
|
6518
6636
|
}
|
|
6519
6637
|
handleChanged(flagsChanged) {
|
|
6520
6638
|
this._events.emit(serverSdk.ProviderEvents.ConfigurationChanged, { flagsChanged });
|
package/index.esm.js
CHANGED
|
@@ -4,6 +4,8 @@ import { ConnectivityState } from '@grpc/grpc-js/build/src/connectivity-state';
|
|
|
4
4
|
import { LRUCache } from 'lru-cache';
|
|
5
5
|
import { promisify } from 'util';
|
|
6
6
|
import { FlagdCore } from '@openfeature/flagd-core';
|
|
7
|
+
import { OpenFeatureError, GeneralError as GeneralError$1 } from '@openfeature/core';
|
|
8
|
+
import { promises, watchFile, unwatchFile } from 'fs';
|
|
7
9
|
|
|
8
10
|
const EVENT_CONFIGURATION_CHANGE = 'configuration_change';
|
|
9
11
|
const EVENT_PROVIDER_READY = 'provider_ready';
|
|
@@ -5938,13 +5940,15 @@ class GRPCService {
|
|
|
5938
5940
|
if (data && typeof data === 'object' && 'flags' in data && (data === null || data === void 0 ? void 0 : data['flags'])) {
|
|
5939
5941
|
const flagChangeMessage = data;
|
|
5940
5942
|
const flagsChanged = Object.keys(flagChangeMessage.flags || []);
|
|
5941
|
-
|
|
5942
|
-
|
|
5943
|
-
|
|
5944
|
-
|
|
5945
|
-
(
|
|
5946
|
-
|
|
5947
|
-
|
|
5943
|
+
if (this._cacheEnabled) {
|
|
5944
|
+
// remove each changed key from cache
|
|
5945
|
+
flagsChanged.forEach((key) => {
|
|
5946
|
+
var _a, _b;
|
|
5947
|
+
if ((_a = this._cache) === null || _a === void 0 ? void 0 : _a.delete(key)) {
|
|
5948
|
+
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(`${FlagdProvider.name}: evicted key: ${key} from cache.`);
|
|
5949
|
+
}
|
|
5950
|
+
});
|
|
5951
|
+
}
|
|
5948
5952
|
changedCallback(flagsChanged);
|
|
5949
5953
|
}
|
|
5950
5954
|
}
|
|
@@ -5957,7 +5961,7 @@ class GRPCService {
|
|
|
5957
5961
|
}
|
|
5958
5962
|
handleError(reconnectCallback, changedCallback, disconnectCallback) {
|
|
5959
5963
|
var _a, _b;
|
|
5960
|
-
disconnectCallback();
|
|
5964
|
+
disconnectCallback('streaming connection error, will attempt reconnect...');
|
|
5961
5965
|
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`${FlagdProvider.name}: streaming connection error, will attempt reconnect...`);
|
|
5962
5966
|
(_b = this._cache) === null || _b === void 0 ? void 0 : _b.clear();
|
|
5963
5967
|
this.reconnect(reconnectCallback, changedCallback, disconnectCallback);
|
|
@@ -6352,6 +6356,18 @@ function isSet(value) {
|
|
|
6352
6356
|
*/
|
|
6353
6357
|
class GrpcFetch {
|
|
6354
6358
|
constructor(config, syncServiceClient, logger) {
|
|
6359
|
+
/**
|
|
6360
|
+
* Initialized will be set to true once the initial connection is successful
|
|
6361
|
+
* and the first payload has been received. Subsequent reconnects will not
|
|
6362
|
+
* change the initialized value.
|
|
6363
|
+
*/
|
|
6364
|
+
this._initialized = false;
|
|
6365
|
+
/**
|
|
6366
|
+
* Is connected represents the current known connection state. It will be
|
|
6367
|
+
* set to true once the first payload has been received.but will be set to
|
|
6368
|
+
* false if the connection is lost.
|
|
6369
|
+
*/
|
|
6370
|
+
this._isConnected = false;
|
|
6355
6371
|
const { host, port, tls, socketPath, selector } = config;
|
|
6356
6372
|
this._syncClient = syncServiceClient
|
|
6357
6373
|
? syncServiceClient
|
|
@@ -6359,54 +6375,131 @@ class GrpcFetch {
|
|
|
6359
6375
|
this._logger = logger;
|
|
6360
6376
|
this._request = { providerId: '', selector: selector ? selector : '' };
|
|
6361
6377
|
}
|
|
6362
|
-
connect(
|
|
6363
|
-
return new Promise((resolve, reject) => this.listen(
|
|
6378
|
+
connect(dataCallback, reconnectCallback, changedCallback, disconnectCallback) {
|
|
6379
|
+
return new Promise((resolve, reject) => this.listen(dataCallback, reconnectCallback, changedCallback, disconnectCallback, resolve, reject)).then(() => {
|
|
6380
|
+
this._initialized = true;
|
|
6381
|
+
});
|
|
6364
6382
|
}
|
|
6365
6383
|
disconnect() {
|
|
6366
6384
|
var _a;
|
|
6367
|
-
(
|
|
6368
|
-
|
|
6369
|
-
|
|
6385
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6386
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Disconnecting gRPC sync connection');
|
|
6387
|
+
closeStreamIfDefined(this._syncStream);
|
|
6388
|
+
this._syncClient.close();
|
|
6389
|
+
});
|
|
6370
6390
|
}
|
|
6371
|
-
listen(
|
|
6391
|
+
listen(dataCallback, reconnectCallback, changedCallback, disconnectCallback, resolveConnect, rejectConnect) {
|
|
6392
|
+
var _a;
|
|
6393
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Starting gRPC sync connection');
|
|
6372
6394
|
closeStreamIfDefined(this._syncStream);
|
|
6373
6395
|
this._syncStream = this._syncClient.syncFlags(this._request);
|
|
6374
6396
|
this._syncStream.on('data', (data) => {
|
|
6375
|
-
var _a;
|
|
6376
|
-
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(
|
|
6377
|
-
|
|
6378
|
-
|
|
6379
|
-
|
|
6397
|
+
var _a, _b, _c, _d;
|
|
6398
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug(`Received sync payload`);
|
|
6399
|
+
try {
|
|
6400
|
+
const changes = dataCallback(data.flagConfiguration);
|
|
6401
|
+
if (this._initialized && changes.length > 0) {
|
|
6402
|
+
changedCallback(changes);
|
|
6403
|
+
}
|
|
6404
|
+
}
|
|
6405
|
+
catch (err) {
|
|
6406
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug('Error processing sync payload: ', (_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : 'unknown error');
|
|
6407
|
+
}
|
|
6380
6408
|
if (resolveConnect) {
|
|
6381
6409
|
resolveConnect();
|
|
6382
6410
|
}
|
|
6383
|
-
else {
|
|
6411
|
+
else if (!this._isConnected) {
|
|
6412
|
+
// Not the first connection and there's no active connection.
|
|
6413
|
+
(_d = this._logger) === null || _d === void 0 ? void 0 : _d.debug('Reconnected to gRPC sync');
|
|
6384
6414
|
reconnectCallback();
|
|
6385
6415
|
}
|
|
6416
|
+
this._isConnected = true;
|
|
6386
6417
|
});
|
|
6387
6418
|
this._syncStream.on('error', (err) => {
|
|
6388
|
-
var _a;
|
|
6389
|
-
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error('Connection error, attempting to reconnect'
|
|
6390
|
-
|
|
6391
|
-
|
|
6392
|
-
|
|
6419
|
+
var _a, _b, _c;
|
|
6420
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.error('Connection error, attempting to reconnect');
|
|
6421
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(err);
|
|
6422
|
+
this._isConnected = false;
|
|
6423
|
+
const errorMessage = (_c = err === null || err === void 0 ? void 0 : err.message) !== null && _c !== void 0 ? _c : 'Failed to connect to syncFlags stream';
|
|
6424
|
+
disconnectCallback(errorMessage);
|
|
6425
|
+
rejectConnect === null || rejectConnect === void 0 ? void 0 : rejectConnect(new GeneralError(errorMessage));
|
|
6426
|
+
this.reconnect(dataCallback, reconnectCallback, changedCallback, disconnectCallback);
|
|
6393
6427
|
});
|
|
6394
6428
|
}
|
|
6395
|
-
reconnect(
|
|
6429
|
+
reconnect(dataCallback, reconnectCallback, changedCallback, disconnectCallback) {
|
|
6396
6430
|
const channel = this._syncClient.getChannel();
|
|
6397
6431
|
channel.watchConnectivityState(channel.getConnectivityState(true), Infinity, () => {
|
|
6398
|
-
this.listen(
|
|
6432
|
+
this.listen(dataCallback, reconnectCallback, changedCallback, disconnectCallback);
|
|
6433
|
+
});
|
|
6434
|
+
}
|
|
6435
|
+
}
|
|
6436
|
+
|
|
6437
|
+
const encoding = 'utf8';
|
|
6438
|
+
class FileFetch {
|
|
6439
|
+
constructor(filename, logger) {
|
|
6440
|
+
this._filename = filename;
|
|
6441
|
+
this._logger = logger;
|
|
6442
|
+
}
|
|
6443
|
+
connect(dataFillCallback, _, changedCallback) {
|
|
6444
|
+
var _a, _b;
|
|
6445
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6446
|
+
(_a = this._logger) === null || _a === void 0 ? void 0 : _a.debug('Starting file sync connection');
|
|
6447
|
+
try {
|
|
6448
|
+
const output = yield promises.readFile(this._filename, encoding);
|
|
6449
|
+
// Don't emit the change event for the initial read
|
|
6450
|
+
dataFillCallback(output);
|
|
6451
|
+
// Using watchFile instead of watch to support virtualized host file systems.
|
|
6452
|
+
watchFile(this._filename, () => __awaiter(this, void 0, void 0, function* () {
|
|
6453
|
+
var _c;
|
|
6454
|
+
try {
|
|
6455
|
+
const data = yield promises.readFile(this._filename, encoding);
|
|
6456
|
+
const changes = dataFillCallback(data);
|
|
6457
|
+
if (changes.length > 0) {
|
|
6458
|
+
changedCallback(changes);
|
|
6459
|
+
}
|
|
6460
|
+
}
|
|
6461
|
+
catch (err) {
|
|
6462
|
+
(_c = this._logger) === null || _c === void 0 ? void 0 : _c.error(`Error reading file: ${err}`);
|
|
6463
|
+
}
|
|
6464
|
+
}));
|
|
6465
|
+
}
|
|
6466
|
+
catch (err) {
|
|
6467
|
+
if (err instanceof OpenFeatureError) {
|
|
6468
|
+
throw err;
|
|
6469
|
+
}
|
|
6470
|
+
else {
|
|
6471
|
+
switch (err === null || err === void 0 ? void 0 : err.code) {
|
|
6472
|
+
case 'ENOENT':
|
|
6473
|
+
throw new GeneralError$1(`File not found: ${this._filename}`);
|
|
6474
|
+
case 'EACCES':
|
|
6475
|
+
throw new GeneralError$1(`File not accessible: ${this._filename}`);
|
|
6476
|
+
default:
|
|
6477
|
+
(_b = this._logger) === null || _b === void 0 ? void 0 : _b.debug(`Error reading file: ${err}`);
|
|
6478
|
+
throw new GeneralError$1();
|
|
6479
|
+
}
|
|
6480
|
+
}
|
|
6481
|
+
}
|
|
6482
|
+
});
|
|
6483
|
+
}
|
|
6484
|
+
disconnect() {
|
|
6485
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6486
|
+
unwatchFile(this._filename);
|
|
6399
6487
|
});
|
|
6400
6488
|
}
|
|
6401
6489
|
}
|
|
6402
6490
|
|
|
6403
6491
|
class InProcessService {
|
|
6404
6492
|
constructor(config, dataFetcher, logger) {
|
|
6405
|
-
this.
|
|
6406
|
-
this.
|
|
6493
|
+
this.config = config;
|
|
6494
|
+
this._flagdCore = new FlagdCore(undefined, logger);
|
|
6495
|
+
this._dataFetcher = dataFetcher
|
|
6496
|
+
? dataFetcher
|
|
6497
|
+
: config.offlineFlagSourcePath
|
|
6498
|
+
? new FileFetch(config.offlineFlagSourcePath, logger)
|
|
6499
|
+
: new GrpcFetch(config, undefined, logger);
|
|
6407
6500
|
}
|
|
6408
6501
|
connect(reconnectCallback, changedCallback, disconnectCallback) {
|
|
6409
|
-
return this._dataFetcher.connect(this.
|
|
6502
|
+
return this._dataFetcher.connect(this.setFlagConfiguration.bind(this), reconnectCallback, changedCallback, disconnectCallback);
|
|
6410
6503
|
}
|
|
6411
6504
|
disconnect() {
|
|
6412
6505
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -6414,19 +6507,43 @@ class InProcessService {
|
|
|
6414
6507
|
});
|
|
6415
6508
|
}
|
|
6416
6509
|
resolveBoolean(flagKey, defaultValue, context, logger) {
|
|
6417
|
-
return
|
|
6510
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6511
|
+
return this.evaluate('boolean', flagKey, defaultValue, context, logger);
|
|
6512
|
+
});
|
|
6418
6513
|
}
|
|
6419
6514
|
resolveNumber(flagKey, defaultValue, context, logger) {
|
|
6420
|
-
return
|
|
6515
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6516
|
+
return this.evaluate('number', flagKey, defaultValue, context, logger);
|
|
6517
|
+
});
|
|
6421
6518
|
}
|
|
6422
6519
|
resolveString(flagKey, defaultValue, context, logger) {
|
|
6423
|
-
return
|
|
6520
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6521
|
+
return this.evaluate('string', flagKey, defaultValue, context, logger);
|
|
6522
|
+
});
|
|
6424
6523
|
}
|
|
6425
6524
|
resolveObject(flagKey, defaultValue, context, logger) {
|
|
6426
|
-
return
|
|
6525
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6526
|
+
return this.evaluate('object', flagKey, defaultValue, context, logger);
|
|
6527
|
+
});
|
|
6528
|
+
}
|
|
6529
|
+
evaluate(type, flagKey, defaultValue, context, logger) {
|
|
6530
|
+
const details = this._flagdCore.resolve(type, flagKey, defaultValue, context, logger);
|
|
6531
|
+
return Object.assign(Object.assign({}, details), { flagMetadata: this.addFlagMetadata() });
|
|
6427
6532
|
}
|
|
6428
|
-
|
|
6429
|
-
|
|
6533
|
+
/**
|
|
6534
|
+
* Adds the flag metadata to the resolution details
|
|
6535
|
+
*/
|
|
6536
|
+
addFlagMetadata() {
|
|
6537
|
+
return Object.assign({}, (this.config.selector ? { scope: this.config.selector } : {}));
|
|
6538
|
+
}
|
|
6539
|
+
/**
|
|
6540
|
+
* Sets the flag configuration
|
|
6541
|
+
* @param flags The flags to set as stringified JSON
|
|
6542
|
+
* @returns {string[]} The flags that have changed
|
|
6543
|
+
* @throws — {Error} If the configuration string is invalid.
|
|
6544
|
+
*/
|
|
6545
|
+
setFlagConfiguration(flags) {
|
|
6546
|
+
return this._flagdCore.setConfigurations(flags);
|
|
6430
6547
|
}
|
|
6431
6548
|
}
|
|
6432
6549
|
|
|
@@ -6473,9 +6590,10 @@ class FlagdProvider {
|
|
|
6473
6590
|
this._status = ProviderStatus.READY;
|
|
6474
6591
|
})
|
|
6475
6592
|
.catch((err) => {
|
|
6476
|
-
var _a;
|
|
6593
|
+
var _a, _b;
|
|
6477
6594
|
this._status = ProviderStatus.ERROR;
|
|
6478
|
-
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`${this.metadata.name}: error during initialization: ${err.message}
|
|
6595
|
+
(_a = this.logger) === null || _a === void 0 ? void 0 : _a.error(`${this.metadata.name}: error during initialization: ${err.message}`);
|
|
6596
|
+
(_b = this.logger) === null || _b === void 0 ? void 0 : _b.debug(err);
|
|
6479
6597
|
throw err;
|
|
6480
6598
|
});
|
|
6481
6599
|
}
|
|
@@ -6508,9 +6626,9 @@ class FlagdProvider {
|
|
|
6508
6626
|
this._status = ProviderStatus.READY;
|
|
6509
6627
|
this._events.emit(ProviderEvents.Ready);
|
|
6510
6628
|
}
|
|
6511
|
-
handleError() {
|
|
6629
|
+
handleError(message) {
|
|
6512
6630
|
this._status = ProviderStatus.ERROR;
|
|
6513
|
-
this._events.emit(ProviderEvents.Error);
|
|
6631
|
+
this._events.emit(ProviderEvents.Error, { message });
|
|
6514
6632
|
}
|
|
6515
6633
|
handleChanged(flagsChanged) {
|
|
6516
6634
|
this._events.emit(ProviderEvents.ConfigurationChanged, { flagsChanged });
|
package/package.json
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openfeature/flagd-provider",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.4",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"publish-if-not-exists": "cp $NPM_CONFIG_USERCONFIG .npmrc && if [ \"$(npm show $npm_package_name@$npm_package_version version)\" = \"$(npm run current-version -s)\" ]; then echo 'already published, skipping'; else npm publish --access public; fi",
|
|
6
6
|
"current-version": "echo $npm_package_version"
|
|
7
7
|
},
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@openfeature/flagd-core": "
|
|
9
|
+
"@openfeature/flagd-core": "~0.1.7",
|
|
10
10
|
"@protobuf-ts/runtime-rpc": "2.9.0",
|
|
11
11
|
"lru-cache": "10.0.1",
|
|
12
12
|
"util": "0.12.5"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"@grpc/grpc-js": "~1.8.0 || ~1.9.0",
|
|
16
|
-
"@openfeature/server-sdk": ">=1.
|
|
16
|
+
"@openfeature/server-sdk": ">=1.8.0"
|
|
17
17
|
},
|
|
18
18
|
"exports": {
|
|
19
19
|
"./package.json": "./package.json",
|
|
@@ -36,6 +36,11 @@ export interface Config {
|
|
|
36
36
|
* @default 'rpc'
|
|
37
37
|
*/
|
|
38
38
|
resolverType?: ResolverType;
|
|
39
|
+
/**
|
|
40
|
+
* File source of flags to be used by offline mode.
|
|
41
|
+
* Setting this enables the offline mode of the in-process provider.
|
|
42
|
+
*/
|
|
43
|
+
offlineFlagSourcePath?: string;
|
|
39
44
|
/**
|
|
40
45
|
* Selector to be used with flag sync gRPC contract.
|
|
41
46
|
*/
|
|
@@ -60,6 +65,7 @@ export declare function getConfig(options?: FlagdProviderOptions): {
|
|
|
60
65
|
tls: boolean;
|
|
61
66
|
socketPath?: string | undefined;
|
|
62
67
|
resolverType?: ResolverType | undefined;
|
|
68
|
+
offlineFlagSourcePath?: string | undefined;
|
|
63
69
|
selector?: string | undefined;
|
|
64
70
|
cache?: CacheOption | undefined;
|
|
65
71
|
maxCacheSize?: number | undefined;
|
|
@@ -26,7 +26,7 @@ export declare class GRPCService implements Service {
|
|
|
26
26
|
private _eventStream;
|
|
27
27
|
private get _cacheActive();
|
|
28
28
|
constructor(config: Config, client?: ServiceClient, logger?: Logger | undefined);
|
|
29
|
-
connect(reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: () => void): Promise<void>;
|
|
29
|
+
connect(reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: (message: string) => void): Promise<void>;
|
|
30
30
|
disconnect(): Promise<void>;
|
|
31
31
|
resolveBoolean(flagKey: string, _: boolean, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>>;
|
|
32
32
|
resolveString(flagKey: string, _: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
|
|
@@ -2,6 +2,32 @@
|
|
|
2
2
|
* Contract of in-process resolver's data fetcher
|
|
3
3
|
*/
|
|
4
4
|
export interface DataFetch {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Connects the data fetcher
|
|
7
|
+
*/
|
|
8
|
+
connect(
|
|
9
|
+
/**
|
|
10
|
+
* Callback that runs when data is received from the source
|
|
11
|
+
* @param flags The flags from the source
|
|
12
|
+
* @returns The flags that have changed
|
|
13
|
+
*/
|
|
14
|
+
dataCallback: (flags: string) => string[],
|
|
15
|
+
/**
|
|
16
|
+
* Callback that runs when the connection is re-established
|
|
17
|
+
*/
|
|
18
|
+
reconnectCallback: () => void,
|
|
19
|
+
/**
|
|
20
|
+
* Callback that runs when flags have changed
|
|
21
|
+
* @param flagsChanged The flags that have changed
|
|
22
|
+
*/
|
|
23
|
+
changedCallback: (flagsChanged: string[]) => void,
|
|
24
|
+
/**
|
|
25
|
+
* Callback that runs when the connection is disconnected
|
|
26
|
+
* @param message The reason for the disconnection
|
|
27
|
+
*/
|
|
28
|
+
disconnectCallback: (message: string) => void): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Disconnects the data fetcher
|
|
31
|
+
*/
|
|
32
|
+
disconnect(): Promise<void>;
|
|
7
33
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Logger } from '@openfeature/core';
|
|
2
|
+
import { DataFetch } from '../data-fetch';
|
|
3
|
+
export declare class FileFetch implements DataFetch {
|
|
4
|
+
private _filename;
|
|
5
|
+
private _logger;
|
|
6
|
+
constructor(filename: string, logger?: Logger);
|
|
7
|
+
connect(dataFillCallback: (flags: string) => string[], _: () => void, changedCallback: (flagsChanged: string[]) => void): Promise<void>;
|
|
8
|
+
disconnect(): Promise<void>;
|
|
9
|
+
}
|
|
@@ -6,13 +6,25 @@ import { DataFetch } from '../data-fetch';
|
|
|
6
6
|
* Implements the gRPC sync contract to fetch flag data.
|
|
7
7
|
*/
|
|
8
8
|
export declare class GrpcFetch implements DataFetch {
|
|
9
|
-
private _syncClient;
|
|
10
|
-
private _syncStream;
|
|
9
|
+
private readonly _syncClient;
|
|
11
10
|
private readonly _request;
|
|
11
|
+
private _syncStream;
|
|
12
12
|
private _logger;
|
|
13
|
+
/**
|
|
14
|
+
* Initialized will be set to true once the initial connection is successful
|
|
15
|
+
* and the first payload has been received. Subsequent reconnects will not
|
|
16
|
+
* change the initialized value.
|
|
17
|
+
*/
|
|
18
|
+
private _initialized;
|
|
19
|
+
/**
|
|
20
|
+
* Is connected represents the current known connection state. It will be
|
|
21
|
+
* set to true once the first payload has been received.but will be set to
|
|
22
|
+
* false if the connection is lost.
|
|
23
|
+
*/
|
|
24
|
+
private _isConnected;
|
|
13
25
|
constructor(config: Config, syncServiceClient?: FlagSyncServiceClient, logger?: Logger);
|
|
14
|
-
connect(
|
|
15
|
-
disconnect(): void
|
|
26
|
+
connect(dataCallback: (flags: string) => string[], reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: (message: string) => void): Promise<void>;
|
|
27
|
+
disconnect(): Promise<void>;
|
|
16
28
|
private listen;
|
|
17
29
|
private reconnect;
|
|
18
30
|
}
|
|
@@ -3,14 +3,26 @@ import { EvaluationContext, JsonValue, Logger, ResolutionDetails } from '@openfe
|
|
|
3
3
|
import { Config } from '../../configuration';
|
|
4
4
|
import { DataFetch } from './data-fetch';
|
|
5
5
|
export declare class InProcessService implements Service {
|
|
6
|
+
private readonly config;
|
|
6
7
|
private _flagdCore;
|
|
7
8
|
private _dataFetcher;
|
|
8
9
|
constructor(config: Config, dataFetcher?: DataFetch, logger?: Logger);
|
|
9
|
-
connect(reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: () => void): Promise<void>;
|
|
10
|
+
connect(reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: (message: string) => void): Promise<void>;
|
|
10
11
|
disconnect(): Promise<void>;
|
|
11
12
|
resolveBoolean(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>>;
|
|
12
13
|
resolveNumber(flagKey: string, defaultValue: number, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<number>>;
|
|
13
14
|
resolveString(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
|
|
14
15
|
resolveObject<T extends JsonValue>(flagKey: string, defaultValue: T, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<T>>;
|
|
15
|
-
private
|
|
16
|
+
private evaluate;
|
|
17
|
+
/**
|
|
18
|
+
* Adds the flag metadata to the resolution details
|
|
19
|
+
*/
|
|
20
|
+
private addFlagMetadata;
|
|
21
|
+
/**
|
|
22
|
+
* Sets the flag configuration
|
|
23
|
+
* @param flags The flags to set as stringified JSON
|
|
24
|
+
* @returns {string[]} The flags that have changed
|
|
25
|
+
* @throws — {Error} If the configuration string is invalid.
|
|
26
|
+
*/
|
|
27
|
+
private setFlagConfiguration;
|
|
16
28
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EvaluationContext, JsonValue, Logger, ResolutionDetails } from '@openfeature/server-sdk';
|
|
2
2
|
export interface Service {
|
|
3
|
-
connect(reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: () => void): Promise<void>;
|
|
3
|
+
connect(reconnectCallback: () => void, changedCallback: (flagsChanged: string[]) => void, disconnectCallback: (message: string) => void): Promise<void>;
|
|
4
4
|
disconnect(): Promise<void>;
|
|
5
5
|
resolveBoolean(flagKey: string, defaultValue: boolean, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<boolean>>;
|
|
6
6
|
resolveString(flagKey: string, defaultValue: string, context: EvaluationContext, logger: Logger): Promise<ResolutionDetails<string>>;
|