@openfeature/web-sdk 1.6.1 → 1.7.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/LICENSE +1 -1
- package/README.md +59 -2
- package/dist/cjs/index.js +542 -24
- package/dist/cjs/index.js.map +4 -4
- package/dist/esm/index.js +524 -6
- package/dist/esm/index.js.map +4 -4
- package/dist/global/index.js +527 -8
- package/dist/global/index.js.map +4 -4
- package/dist/global/index.min.js +1 -1
- package/dist/global/index.min.js.map +4 -4
- package/dist/types.d.ts +145 -3
- package/package.json +1 -1
package/dist/global/index.js
CHANGED
|
@@ -235,12 +235,18 @@ var OpenFeature = (() => {
|
|
|
235
235
|
// src/index.ts
|
|
236
236
|
var index_exports = {};
|
|
237
237
|
__export(index_exports, {
|
|
238
|
+
AggregateError: () => AggregateError,
|
|
238
239
|
AllProviderEvents: () => ClientProviderEvents,
|
|
239
240
|
AllProviderStatus: () => ClientProviderStatus,
|
|
241
|
+
BaseEvaluationStrategy: () => BaseEvaluationStrategy,
|
|
240
242
|
ClientProviderEvents: () => ClientProviderEvents,
|
|
241
243
|
ClientProviderStatus: () => ClientProviderStatus,
|
|
244
|
+
ComparisonStrategy: () => ComparisonStrategy,
|
|
242
245
|
DefaultLogger: () => DefaultLogger,
|
|
243
246
|
ErrorCode: () => ErrorCode,
|
|
247
|
+
ErrorWithCode: () => ErrorWithCode,
|
|
248
|
+
FirstMatchStrategy: () => FirstMatchStrategy,
|
|
249
|
+
FirstSuccessfulStrategy: () => FirstSuccessfulStrategy,
|
|
244
250
|
FlagNotFoundError: () => FlagNotFoundError,
|
|
245
251
|
GeneralError: () => GeneralError,
|
|
246
252
|
GenericEventEmitter: () => GenericEventEmitter,
|
|
@@ -248,6 +254,7 @@ var OpenFeature = (() => {
|
|
|
248
254
|
InvalidContextError: () => InvalidContextError,
|
|
249
255
|
LOG_LEVELS: () => LOG_LEVELS,
|
|
250
256
|
MapHookData: () => MapHookData,
|
|
257
|
+
MultiProvider: () => MultiProvider,
|
|
251
258
|
NOOP_PROVIDER: () => NOOP_PROVIDER,
|
|
252
259
|
OpenFeature: () => OpenFeature,
|
|
253
260
|
OpenFeatureAPI: () => OpenFeatureAPI,
|
|
@@ -268,13 +275,15 @@ var OpenFeature = (() => {
|
|
|
268
275
|
TelemetryAttribute: () => TelemetryAttribute,
|
|
269
276
|
TelemetryFlagMetadata: () => TelemetryFlagMetadata,
|
|
270
277
|
TypeMismatchError: () => TypeMismatchError,
|
|
278
|
+
constructAggregateError: () => constructAggregateError,
|
|
271
279
|
createEvaluationEvent: () => createEvaluationEvent,
|
|
272
280
|
instantiateErrorByErrorCode: () => instantiateErrorByErrorCode,
|
|
273
281
|
isObject: () => isObject,
|
|
274
282
|
isString: () => isString,
|
|
275
283
|
objectOrUndefined: () => objectOrUndefined,
|
|
276
284
|
statusMatchesEvent: () => statusMatchesEvent,
|
|
277
|
-
stringOrUndefined: () => stringOrUndefined
|
|
285
|
+
stringOrUndefined: () => stringOrUndefined,
|
|
286
|
+
throwAggregateErrorFromPromiseResults: () => throwAggregateErrorFromPromiseResults
|
|
278
287
|
});
|
|
279
288
|
|
|
280
289
|
// ../shared/src/hooks/hook-data.ts
|
|
@@ -667,7 +676,6 @@ var OpenFeature = (() => {
|
|
|
667
676
|
*
|
|
668
677
|
* - type: `undefined`
|
|
669
678
|
* - requirement level: `conditionally required`
|
|
670
|
-
* - condition: variant is not defined on the evaluation details
|
|
671
679
|
* - example: `#ff0000`; `1`; `true`
|
|
672
680
|
*/
|
|
673
681
|
VALUE: "feature_flag.result.value",
|
|
@@ -734,7 +742,7 @@ var OpenFeature = (() => {
|
|
|
734
742
|
// ../shared/src/telemetry/evaluation-event.ts
|
|
735
743
|
var FLAG_EVALUATION_EVENT_NAME = "feature_flag.evaluation";
|
|
736
744
|
function createEvaluationEvent(hookContext, evaluationDetails) {
|
|
737
|
-
var _a, _b;
|
|
745
|
+
var _a, _b, _c;
|
|
738
746
|
const attributes = {
|
|
739
747
|
[TelemetryAttribute.KEY]: hookContext.flagKey,
|
|
740
748
|
[TelemetryAttribute.PROVIDER]: hookContext.providerMetadata.name,
|
|
@@ -742,10 +750,18 @@ var OpenFeature = (() => {
|
|
|
742
750
|
};
|
|
743
751
|
if (evaluationDetails.variant) {
|
|
744
752
|
attributes[TelemetryAttribute.VARIANT] = evaluationDetails.variant;
|
|
745
|
-
} else {
|
|
746
|
-
attributes[TelemetryAttribute.VALUE] = evaluationDetails.value;
|
|
747
753
|
}
|
|
748
|
-
|
|
754
|
+
if (evaluationDetails.value !== null) {
|
|
755
|
+
if (typeof evaluationDetails.value !== "object") {
|
|
756
|
+
attributes[TelemetryAttribute.VALUE] = evaluationDetails.value;
|
|
757
|
+
} else {
|
|
758
|
+
try {
|
|
759
|
+
attributes[TelemetryAttribute.VALUE] = JSON.stringify(evaluationDetails.value);
|
|
760
|
+
} catch (e) {
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
const contextId = (_b = evaluationDetails.flagMetadata[TelemetryFlagMetadata.CONTEXT_ID]) != null ? _b : hookContext.context.targetingKey;
|
|
749
765
|
if (contextId) {
|
|
750
766
|
attributes[TelemetryAttribute.CONTEXT_ID] = contextId;
|
|
751
767
|
}
|
|
@@ -758,7 +774,7 @@ var OpenFeature = (() => {
|
|
|
758
774
|
attributes[TelemetryAttribute.VERSION] = version;
|
|
759
775
|
}
|
|
760
776
|
if (evaluationDetails.reason === StandardResolutionReasons.ERROR) {
|
|
761
|
-
attributes[TelemetryAttribute.ERROR_CODE] = ((
|
|
777
|
+
attributes[TelemetryAttribute.ERROR_CODE] = ((_c = evaluationDetails.errorCode) != null ? _c : "GENERAL" /* GENERAL */).toLowerCase();
|
|
762
778
|
if (evaluationDetails.errorMessage) {
|
|
763
779
|
attributes[TelemetryAttribute.ERROR_MESSAGE] = evaluationDetails.errorMessage;
|
|
764
780
|
}
|
|
@@ -1252,6 +1268,509 @@ var OpenFeature = (() => {
|
|
|
1252
1268
|
}
|
|
1253
1269
|
};
|
|
1254
1270
|
|
|
1271
|
+
// src/provider/multi-provider/hook-executor.ts
|
|
1272
|
+
var HookExecutor = class {
|
|
1273
|
+
constructor(logger) {
|
|
1274
|
+
this.logger = logger;
|
|
1275
|
+
}
|
|
1276
|
+
beforeHooks(hooks, hookContext, hints) {
|
|
1277
|
+
var _a;
|
|
1278
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
1279
|
+
(_a = hook == null ? void 0 : hook.before) == null ? void 0 : _a.call(hook, hookContext, Object.freeze(hints));
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
afterHooks(hooks, hookContext, evaluationDetails, hints) {
|
|
1283
|
+
var _a;
|
|
1284
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
1285
|
+
(_a = hook == null ? void 0 : hook.after) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
errorHooks(hooks, hookContext, err, hints) {
|
|
1289
|
+
var _a;
|
|
1290
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
1291
|
+
try {
|
|
1292
|
+
(_a = hook == null ? void 0 : hook.error) == null ? void 0 : _a.call(hook, hookContext, err, hints);
|
|
1293
|
+
} catch (err2) {
|
|
1294
|
+
this.logger.error(`Unhandled error during 'error' hook: ${err2}`);
|
|
1295
|
+
if (err2 instanceof Error) {
|
|
1296
|
+
this.logger.error(err2.stack);
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
finallyHooks(hooks, hookContext, evaluationDetails, hints) {
|
|
1302
|
+
var _a;
|
|
1303
|
+
for (const hook of hooks != null ? hooks : []) {
|
|
1304
|
+
try {
|
|
1305
|
+
(_a = hook == null ? void 0 : hook.finally) == null ? void 0 : _a.call(hook, hookContext, evaluationDetails, hints);
|
|
1306
|
+
} catch (err) {
|
|
1307
|
+
this.logger.error(`Unhandled error during 'finally' hook: ${err}`);
|
|
1308
|
+
if (err instanceof Error) {
|
|
1309
|
+
this.logger.error(err.stack);
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
};
|
|
1315
|
+
|
|
1316
|
+
// src/provider/multi-provider/errors.ts
|
|
1317
|
+
var ErrorWithCode = class extends OpenFeatureError {
|
|
1318
|
+
constructor(code, message) {
|
|
1319
|
+
super(message);
|
|
1320
|
+
this.code = code;
|
|
1321
|
+
}
|
|
1322
|
+
};
|
|
1323
|
+
var AggregateError = class extends GeneralError {
|
|
1324
|
+
constructor(message, originalErrors) {
|
|
1325
|
+
super(message);
|
|
1326
|
+
this.originalErrors = originalErrors;
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
var constructAggregateError = (providerErrors) => {
|
|
1330
|
+
const errorsWithSource = providerErrors.map(({ providerName, error }) => {
|
|
1331
|
+
return { source: providerName, error };
|
|
1332
|
+
}).flat();
|
|
1333
|
+
return new AggregateError(
|
|
1334
|
+
`Provider errors occurred: ${errorsWithSource[0].source}: ${errorsWithSource[0].error}`,
|
|
1335
|
+
errorsWithSource
|
|
1336
|
+
);
|
|
1337
|
+
};
|
|
1338
|
+
var throwAggregateErrorFromPromiseResults = (result, providerEntries) => {
|
|
1339
|
+
const errors = result.map((r, i) => {
|
|
1340
|
+
if (r.status === "rejected") {
|
|
1341
|
+
return { error: r.reason, providerName: providerEntries[i].name };
|
|
1342
|
+
}
|
|
1343
|
+
return null;
|
|
1344
|
+
}).filter((val) => Boolean(val));
|
|
1345
|
+
if (errors.length) {
|
|
1346
|
+
throw constructAggregateError(errors);
|
|
1347
|
+
}
|
|
1348
|
+
};
|
|
1349
|
+
|
|
1350
|
+
// src/provider/multi-provider/strategies/base-evaluation-strategy.ts
|
|
1351
|
+
var BaseEvaluationStrategy = class {
|
|
1352
|
+
shouldEvaluateThisProvider(strategyContext, _evalContext) {
|
|
1353
|
+
if (strategyContext.providerStatus === "NOT_READY" /* NOT_READY */ || strategyContext.providerStatus === "FATAL" /* FATAL */) {
|
|
1354
|
+
return false;
|
|
1355
|
+
}
|
|
1356
|
+
return true;
|
|
1357
|
+
}
|
|
1358
|
+
shouldEvaluateNextProvider(_strategyContext, _context, _result) {
|
|
1359
|
+
return true;
|
|
1360
|
+
}
|
|
1361
|
+
shouldTrackWithThisProvider(strategyContext, _context, _trackingEventName, _trackingEventDetails) {
|
|
1362
|
+
if (strategyContext.providerStatus === "NOT_READY" /* NOT_READY */ || strategyContext.providerStatus === "FATAL" /* FATAL */) {
|
|
1363
|
+
return false;
|
|
1364
|
+
}
|
|
1365
|
+
return true;
|
|
1366
|
+
}
|
|
1367
|
+
hasError(resolution) {
|
|
1368
|
+
return "thrownError" in resolution || !!resolution.details.errorCode;
|
|
1369
|
+
}
|
|
1370
|
+
hasErrorWithCode(resolution, code) {
|
|
1371
|
+
var _a;
|
|
1372
|
+
return "thrownError" in resolution ? ((_a = resolution.thrownError) == null ? void 0 : _a.code) === code : resolution.details.errorCode === code;
|
|
1373
|
+
}
|
|
1374
|
+
collectProviderErrors(resolutions) {
|
|
1375
|
+
var _a;
|
|
1376
|
+
const errors = [];
|
|
1377
|
+
for (const resolution of resolutions) {
|
|
1378
|
+
if ("thrownError" in resolution) {
|
|
1379
|
+
errors.push({ providerName: resolution.providerName, error: resolution.thrownError });
|
|
1380
|
+
} else if (resolution.details.errorCode) {
|
|
1381
|
+
errors.push({
|
|
1382
|
+
providerName: resolution.providerName,
|
|
1383
|
+
error: new ErrorWithCode(resolution.details.errorCode, (_a = resolution.details.errorMessage) != null ? _a : "unknown error")
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
}
|
|
1387
|
+
return { errors };
|
|
1388
|
+
}
|
|
1389
|
+
resolutionToFinalResult(resolution) {
|
|
1390
|
+
return { details: resolution.details, provider: resolution.provider, providerName: resolution.providerName };
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
// src/provider/multi-provider/strategies/first-match-strategy.ts
|
|
1395
|
+
var FirstMatchStrategy = class extends BaseEvaluationStrategy {
|
|
1396
|
+
shouldEvaluateNextProvider(strategyContext, context, result) {
|
|
1397
|
+
if (this.hasErrorWithCode(result, "FLAG_NOT_FOUND" /* FLAG_NOT_FOUND */)) {
|
|
1398
|
+
return true;
|
|
1399
|
+
}
|
|
1400
|
+
if (this.hasError(result)) {
|
|
1401
|
+
return false;
|
|
1402
|
+
}
|
|
1403
|
+
return false;
|
|
1404
|
+
}
|
|
1405
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
1406
|
+
const finalResolution = resolutions[resolutions.length - 1];
|
|
1407
|
+
if (this.hasError(finalResolution)) {
|
|
1408
|
+
return this.collectProviderErrors(resolutions);
|
|
1409
|
+
}
|
|
1410
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
1411
|
+
}
|
|
1412
|
+
};
|
|
1413
|
+
|
|
1414
|
+
// src/provider/multi-provider/strategies/first-successful-strategy.ts
|
|
1415
|
+
var FirstSuccessfulStrategy = class extends BaseEvaluationStrategy {
|
|
1416
|
+
shouldEvaluateNextProvider(strategyContext, context, result) {
|
|
1417
|
+
return this.hasError(result);
|
|
1418
|
+
}
|
|
1419
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
1420
|
+
const finalResolution = resolutions[resolutions.length - 1];
|
|
1421
|
+
if (this.hasError(finalResolution)) {
|
|
1422
|
+
return this.collectProviderErrors(resolutions);
|
|
1423
|
+
}
|
|
1424
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
|
|
1428
|
+
// src/provider/multi-provider/strategies/comparison-strategy.ts
|
|
1429
|
+
var ComparisonStrategy = class extends BaseEvaluationStrategy {
|
|
1430
|
+
constructor(fallbackProvider, onMismatch) {
|
|
1431
|
+
super();
|
|
1432
|
+
this.fallbackProvider = fallbackProvider;
|
|
1433
|
+
this.onMismatch = onMismatch;
|
|
1434
|
+
}
|
|
1435
|
+
determineFinalResult(strategyContext, context, resolutions) {
|
|
1436
|
+
var _a;
|
|
1437
|
+
let value;
|
|
1438
|
+
let fallbackResolution;
|
|
1439
|
+
let finalResolution;
|
|
1440
|
+
let mismatch = false;
|
|
1441
|
+
for (const [i, resolution] of resolutions.entries()) {
|
|
1442
|
+
if (this.hasError(resolution)) {
|
|
1443
|
+
return this.collectProviderErrors(resolutions);
|
|
1444
|
+
}
|
|
1445
|
+
if (resolution.provider === this.fallbackProvider) {
|
|
1446
|
+
fallbackResolution = resolution;
|
|
1447
|
+
}
|
|
1448
|
+
if (i === 0) {
|
|
1449
|
+
finalResolution = resolution;
|
|
1450
|
+
}
|
|
1451
|
+
if (typeof value !== "undefined" && value !== resolution.details.value) {
|
|
1452
|
+
mismatch = true;
|
|
1453
|
+
} else {
|
|
1454
|
+
value = resolution.details.value;
|
|
1455
|
+
}
|
|
1456
|
+
}
|
|
1457
|
+
if (!fallbackResolution) {
|
|
1458
|
+
throw new GeneralError("Fallback provider not found in resolution results");
|
|
1459
|
+
}
|
|
1460
|
+
if (!finalResolution) {
|
|
1461
|
+
throw new GeneralError("Final resolution not found in resolution results");
|
|
1462
|
+
}
|
|
1463
|
+
if (mismatch) {
|
|
1464
|
+
(_a = this.onMismatch) == null ? void 0 : _a.call(this, resolutions);
|
|
1465
|
+
return {
|
|
1466
|
+
details: fallbackResolution.details,
|
|
1467
|
+
provider: fallbackResolution.provider
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
return this.resolutionToFinalResult(finalResolution);
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
|
|
1474
|
+
// src/provider/multi-provider/status-tracker.ts
|
|
1475
|
+
var StatusTracker = class {
|
|
1476
|
+
constructor(events) {
|
|
1477
|
+
this.events = events;
|
|
1478
|
+
this.providerStatuses = {};
|
|
1479
|
+
}
|
|
1480
|
+
wrapEventHandler(providerEntry) {
|
|
1481
|
+
var _a, _b, _c, _d, _e;
|
|
1482
|
+
const provider = providerEntry.provider;
|
|
1483
|
+
(_a = provider.events) == null ? void 0 : _a.addHandler("PROVIDER_ERROR" /* Error */, (details) => {
|
|
1484
|
+
this.changeProviderStatus(providerEntry.name, "ERROR" /* ERROR */, details);
|
|
1485
|
+
});
|
|
1486
|
+
(_b = provider.events) == null ? void 0 : _b.addHandler("PROVIDER_STALE" /* Stale */, (details) => {
|
|
1487
|
+
this.changeProviderStatus(providerEntry.name, "STALE" /* STALE */, details);
|
|
1488
|
+
});
|
|
1489
|
+
(_c = provider.events) == null ? void 0 : _c.addHandler("PROVIDER_CONFIGURATION_CHANGED" /* ConfigurationChanged */, (details) => {
|
|
1490
|
+
this.events.emit("PROVIDER_CONFIGURATION_CHANGED" /* ConfigurationChanged */, details);
|
|
1491
|
+
});
|
|
1492
|
+
(_d = provider.events) == null ? void 0 : _d.addHandler("PROVIDER_READY" /* Ready */, (details) => {
|
|
1493
|
+
this.changeProviderStatus(providerEntry.name, "READY" /* READY */, details);
|
|
1494
|
+
});
|
|
1495
|
+
(_e = provider.events) == null ? void 0 : _e.addHandler("PROVIDER_RECONCILING" /* Reconciling */, (details) => {
|
|
1496
|
+
this.changeProviderStatus(providerEntry.name, "RECONCILING" /* RECONCILING */, details);
|
|
1497
|
+
});
|
|
1498
|
+
}
|
|
1499
|
+
providerStatus(name) {
|
|
1500
|
+
return this.providerStatuses[name];
|
|
1501
|
+
}
|
|
1502
|
+
getStatusFromProviderStatuses() {
|
|
1503
|
+
const statuses = Object.values(this.providerStatuses);
|
|
1504
|
+
if (statuses.includes("FATAL" /* FATAL */)) {
|
|
1505
|
+
return "FATAL" /* FATAL */;
|
|
1506
|
+
} else if (statuses.includes("NOT_READY" /* NOT_READY */)) {
|
|
1507
|
+
return "NOT_READY" /* NOT_READY */;
|
|
1508
|
+
} else if (statuses.includes("ERROR" /* ERROR */)) {
|
|
1509
|
+
return "ERROR" /* ERROR */;
|
|
1510
|
+
} else if (statuses.includes("STALE" /* STALE */)) {
|
|
1511
|
+
return "STALE" /* STALE */;
|
|
1512
|
+
} else if (statuses.includes("RECONCILING" /* RECONCILING */)) {
|
|
1513
|
+
return "RECONCILING" /* RECONCILING */;
|
|
1514
|
+
}
|
|
1515
|
+
return "READY" /* READY */;
|
|
1516
|
+
}
|
|
1517
|
+
changeProviderStatus(name, status, details) {
|
|
1518
|
+
const currentStatus = this.getStatusFromProviderStatuses();
|
|
1519
|
+
this.providerStatuses[name] = status;
|
|
1520
|
+
const newStatus = this.getStatusFromProviderStatuses();
|
|
1521
|
+
if (currentStatus !== newStatus) {
|
|
1522
|
+
if (newStatus === "FATAL" /* FATAL */ || newStatus === "ERROR" /* ERROR */) {
|
|
1523
|
+
this.events.emit("PROVIDER_ERROR" /* Error */, details);
|
|
1524
|
+
} else if (newStatus === "STALE" /* STALE */) {
|
|
1525
|
+
this.events.emit("PROVIDER_STALE" /* Stale */, details);
|
|
1526
|
+
} else if (newStatus === "READY" /* READY */) {
|
|
1527
|
+
this.events.emit("PROVIDER_READY" /* Ready */, details);
|
|
1528
|
+
} else if (newStatus === "RECONCILING" /* RECONCILING */) {
|
|
1529
|
+
this.events.emit("PROVIDER_RECONCILING" /* Reconciling */, details);
|
|
1530
|
+
}
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
|
|
1535
|
+
// src/provider/multi-provider/multi-provider-web.ts
|
|
1536
|
+
var MultiProvider = class _MultiProvider {
|
|
1537
|
+
constructor(constructorProviders, evaluationStrategy = new FirstMatchStrategy(), logger = new DefaultLogger()) {
|
|
1538
|
+
this.constructorProviders = constructorProviders;
|
|
1539
|
+
this.evaluationStrategy = evaluationStrategy;
|
|
1540
|
+
this.logger = logger;
|
|
1541
|
+
this.runsOn = "client";
|
|
1542
|
+
this.events = new OpenFeatureEventEmitter();
|
|
1543
|
+
this.hookContexts = /* @__PURE__ */ new WeakMap();
|
|
1544
|
+
this.hookHints = /* @__PURE__ */ new WeakMap();
|
|
1545
|
+
this.providerEntries = [];
|
|
1546
|
+
this.providerEntriesByName = {};
|
|
1547
|
+
this.statusTracker = new StatusTracker(this.events);
|
|
1548
|
+
this.hookExecutor = new HookExecutor(this.logger);
|
|
1549
|
+
this.registerProviders(constructorProviders);
|
|
1550
|
+
const aggregateMetadata = Object.keys(this.providerEntriesByName).reduce((acc, name) => {
|
|
1551
|
+
return __spreadProps(__spreadValues({}, acc), { [name]: this.providerEntriesByName[name].provider.metadata });
|
|
1552
|
+
}, {});
|
|
1553
|
+
this.metadata = __spreadProps(__spreadValues({}, aggregateMetadata), {
|
|
1554
|
+
name: _MultiProvider.name
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
registerProviders(constructorProviders) {
|
|
1558
|
+
var _a, _b;
|
|
1559
|
+
const providersByName = {};
|
|
1560
|
+
for (const constructorProvider of constructorProviders) {
|
|
1561
|
+
const providerName = constructorProvider.provider.metadata.name;
|
|
1562
|
+
const candidateName = (_a = constructorProvider.name) != null ? _a : providerName;
|
|
1563
|
+
if (constructorProvider.name && providersByName[constructorProvider.name]) {
|
|
1564
|
+
throw new Error("Provider names must be unique");
|
|
1565
|
+
}
|
|
1566
|
+
(_b = providersByName[candidateName]) != null ? _b : providersByName[candidateName] = [];
|
|
1567
|
+
providersByName[candidateName].push(constructorProvider.provider);
|
|
1568
|
+
}
|
|
1569
|
+
for (const name of Object.keys(providersByName)) {
|
|
1570
|
+
const useIndexedNames = providersByName[name].length > 1;
|
|
1571
|
+
for (let i = 0; i < providersByName[name].length; i++) {
|
|
1572
|
+
const indexedName = useIndexedNames ? `${name}-${i + 1}` : name;
|
|
1573
|
+
this.providerEntriesByName[indexedName] = { provider: providersByName[name][i], name: indexedName };
|
|
1574
|
+
this.providerEntries.push(this.providerEntriesByName[indexedName]);
|
|
1575
|
+
this.statusTracker.wrapEventHandler(this.providerEntriesByName[indexedName]);
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
Object.freeze(this.providerEntries);
|
|
1579
|
+
Object.freeze(this.providerEntriesByName);
|
|
1580
|
+
}
|
|
1581
|
+
initialize(context) {
|
|
1582
|
+
return __async(this, null, function* () {
|
|
1583
|
+
const result = yield Promise.allSettled(
|
|
1584
|
+
this.providerEntries.map((provider) => {
|
|
1585
|
+
var _a, _b;
|
|
1586
|
+
return (_b = (_a = provider.provider).initialize) == null ? void 0 : _b.call(_a, context);
|
|
1587
|
+
})
|
|
1588
|
+
);
|
|
1589
|
+
throwAggregateErrorFromPromiseResults(result, this.providerEntries);
|
|
1590
|
+
});
|
|
1591
|
+
}
|
|
1592
|
+
onClose() {
|
|
1593
|
+
return __async(this, null, function* () {
|
|
1594
|
+
const result = yield Promise.allSettled(this.providerEntries.map((provider) => {
|
|
1595
|
+
var _a, _b;
|
|
1596
|
+
return (_b = (_a = provider.provider).onClose) == null ? void 0 : _b.call(_a);
|
|
1597
|
+
}));
|
|
1598
|
+
throwAggregateErrorFromPromiseResults(result, this.providerEntries);
|
|
1599
|
+
});
|
|
1600
|
+
}
|
|
1601
|
+
onContextChange(oldContext, newContext) {
|
|
1602
|
+
return __async(this, null, function* () {
|
|
1603
|
+
var _a, _b;
|
|
1604
|
+
for (const providerEntry of this.providerEntries) {
|
|
1605
|
+
yield (_b = (_a = providerEntry.provider).onContextChange) == null ? void 0 : _b.call(_a, oldContext, newContext);
|
|
1606
|
+
}
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
resolveBooleanEvaluation(flagKey, defaultValue, context) {
|
|
1610
|
+
return this.flagResolutionProxy(flagKey, "boolean", defaultValue, context);
|
|
1611
|
+
}
|
|
1612
|
+
resolveStringEvaluation(flagKey, defaultValue, context) {
|
|
1613
|
+
return this.flagResolutionProxy(flagKey, "string", defaultValue, context);
|
|
1614
|
+
}
|
|
1615
|
+
resolveNumberEvaluation(flagKey, defaultValue, context) {
|
|
1616
|
+
return this.flagResolutionProxy(flagKey, "number", defaultValue, context);
|
|
1617
|
+
}
|
|
1618
|
+
resolveObjectEvaluation(flagKey, defaultValue, context) {
|
|
1619
|
+
return this.flagResolutionProxy(flagKey, "object", defaultValue, context);
|
|
1620
|
+
}
|
|
1621
|
+
track(trackingEventName, context, trackingEventDetails) {
|
|
1622
|
+
var _a, _b;
|
|
1623
|
+
for (const providerEntry of this.providerEntries) {
|
|
1624
|
+
if (!providerEntry.provider.track) {
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
const strategyContext = {
|
|
1628
|
+
provider: providerEntry.provider,
|
|
1629
|
+
providerName: providerEntry.name,
|
|
1630
|
+
providerStatus: this.statusTracker.providerStatus(providerEntry.name)
|
|
1631
|
+
};
|
|
1632
|
+
if (this.evaluationStrategy.shouldTrackWithThisProvider(
|
|
1633
|
+
strategyContext,
|
|
1634
|
+
context,
|
|
1635
|
+
trackingEventName,
|
|
1636
|
+
trackingEventDetails
|
|
1637
|
+
)) {
|
|
1638
|
+
try {
|
|
1639
|
+
(_b = (_a = providerEntry.provider).track) == null ? void 0 : _b.call(_a, trackingEventName, context, trackingEventDetails);
|
|
1640
|
+
} catch (error) {
|
|
1641
|
+
this.logger.error(
|
|
1642
|
+
`Error tracking event "${trackingEventName}" with provider "${providerEntry.name}":`,
|
|
1643
|
+
error
|
|
1644
|
+
);
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
flagResolutionProxy(flagKey, flagType, defaultValue, context) {
|
|
1650
|
+
var _a;
|
|
1651
|
+
const hookContext = this.hookContexts.get(context);
|
|
1652
|
+
const hookHints = this.hookHints.get(context);
|
|
1653
|
+
if (!hookContext || !hookHints) {
|
|
1654
|
+
throw new GeneralError("Hook context not available for evaluation");
|
|
1655
|
+
}
|
|
1656
|
+
const results = [];
|
|
1657
|
+
for (const providerEntry of this.providerEntries) {
|
|
1658
|
+
const [shouldEvaluateNext, result] = this.evaluateProviderEntry(
|
|
1659
|
+
flagKey,
|
|
1660
|
+
flagType,
|
|
1661
|
+
defaultValue,
|
|
1662
|
+
providerEntry,
|
|
1663
|
+
hookContext,
|
|
1664
|
+
hookHints,
|
|
1665
|
+
context
|
|
1666
|
+
);
|
|
1667
|
+
results.push(result);
|
|
1668
|
+
if (!shouldEvaluateNext) {
|
|
1669
|
+
break;
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
const resolutions = results.filter((r) => Boolean(r));
|
|
1673
|
+
const finalResult = this.evaluationStrategy.determineFinalResult({ flagKey, flagType }, context, resolutions);
|
|
1674
|
+
if ((_a = finalResult.errors) == null ? void 0 : _a.length) {
|
|
1675
|
+
throw constructAggregateError(finalResult.errors);
|
|
1676
|
+
}
|
|
1677
|
+
if (!finalResult.details) {
|
|
1678
|
+
throw new GeneralError("No result was returned from any provider");
|
|
1679
|
+
}
|
|
1680
|
+
return finalResult.details;
|
|
1681
|
+
}
|
|
1682
|
+
evaluateProviderEntry(flagKey, flagType, defaultValue, providerEntry, hookContext, hookHints, context) {
|
|
1683
|
+
let evaluationResult = void 0;
|
|
1684
|
+
const provider = providerEntry.provider;
|
|
1685
|
+
const strategyContext = {
|
|
1686
|
+
flagKey,
|
|
1687
|
+
flagType,
|
|
1688
|
+
provider,
|
|
1689
|
+
providerName: providerEntry.name,
|
|
1690
|
+
providerStatus: this.statusTracker.providerStatus(providerEntry.name)
|
|
1691
|
+
};
|
|
1692
|
+
if (!this.evaluationStrategy.shouldEvaluateThisProvider(strategyContext, context)) {
|
|
1693
|
+
return [true, null];
|
|
1694
|
+
}
|
|
1695
|
+
let resolution;
|
|
1696
|
+
try {
|
|
1697
|
+
evaluationResult = this.evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints);
|
|
1698
|
+
resolution = {
|
|
1699
|
+
details: evaluationResult,
|
|
1700
|
+
provider,
|
|
1701
|
+
providerName: providerEntry.name
|
|
1702
|
+
};
|
|
1703
|
+
} catch (error) {
|
|
1704
|
+
resolution = {
|
|
1705
|
+
thrownError: error,
|
|
1706
|
+
provider,
|
|
1707
|
+
providerName: providerEntry.name
|
|
1708
|
+
};
|
|
1709
|
+
}
|
|
1710
|
+
return [this.evaluationStrategy.shouldEvaluateNextProvider(strategyContext, context, resolution), resolution];
|
|
1711
|
+
}
|
|
1712
|
+
evaluateProviderAndHooks(flagKey, defaultValue, provider, hookContext, hookHints) {
|
|
1713
|
+
var _a;
|
|
1714
|
+
let evaluationDetails;
|
|
1715
|
+
try {
|
|
1716
|
+
this.hookExecutor.beforeHooks(provider.hooks, hookContext, hookHints);
|
|
1717
|
+
const resolutionDetails = this.callProviderResolve(
|
|
1718
|
+
provider,
|
|
1719
|
+
flagKey,
|
|
1720
|
+
defaultValue,
|
|
1721
|
+
hookContext.context
|
|
1722
|
+
);
|
|
1723
|
+
evaluationDetails = __spreadProps(__spreadValues({}, resolutionDetails), {
|
|
1724
|
+
flagMetadata: Object.freeze((_a = resolutionDetails.flagMetadata) != null ? _a : {}),
|
|
1725
|
+
flagKey
|
|
1726
|
+
});
|
|
1727
|
+
this.hookExecutor.afterHooks(provider.hooks, hookContext, evaluationDetails, hookHints);
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
this.hookExecutor.errorHooks(provider.hooks, hookContext, error, hookHints);
|
|
1730
|
+
evaluationDetails = this.getErrorEvaluationDetails(flagKey, defaultValue, error);
|
|
1731
|
+
}
|
|
1732
|
+
this.hookExecutor.finallyHooks(provider.hooks, hookContext, evaluationDetails, hookHints);
|
|
1733
|
+
return evaluationDetails;
|
|
1734
|
+
}
|
|
1735
|
+
callProviderResolve(provider, flagKey, defaultValue, context) {
|
|
1736
|
+
switch (typeof defaultValue) {
|
|
1737
|
+
case "string":
|
|
1738
|
+
return provider.resolveStringEvaluation(flagKey, defaultValue, context, this.logger);
|
|
1739
|
+
case "number":
|
|
1740
|
+
return provider.resolveNumberEvaluation(flagKey, defaultValue, context, this.logger);
|
|
1741
|
+
case "object":
|
|
1742
|
+
return provider.resolveObjectEvaluation(flagKey, defaultValue, context, this.logger);
|
|
1743
|
+
case "boolean":
|
|
1744
|
+
return provider.resolveBooleanEvaluation(flagKey, defaultValue, context, this.logger);
|
|
1745
|
+
default:
|
|
1746
|
+
throw new GeneralError("Invalid flag evaluation type");
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
get hooks() {
|
|
1750
|
+
return [
|
|
1751
|
+
{
|
|
1752
|
+
before: (hookContext, hints) => {
|
|
1753
|
+
this.hookContexts.set(hookContext.context, hookContext);
|
|
1754
|
+
this.hookHints.set(hookContext.context, hints != null ? hints : {});
|
|
1755
|
+
return hookContext.context;
|
|
1756
|
+
}
|
|
1757
|
+
}
|
|
1758
|
+
];
|
|
1759
|
+
}
|
|
1760
|
+
getErrorEvaluationDetails(flagKey, defaultValue, err, flagMetadata = {}) {
|
|
1761
|
+
const errorMessage = err == null ? void 0 : err.message;
|
|
1762
|
+
const errorCode = (err == null ? void 0 : err.code) || "GENERAL" /* GENERAL */;
|
|
1763
|
+
return {
|
|
1764
|
+
errorCode,
|
|
1765
|
+
errorMessage,
|
|
1766
|
+
value: defaultValue,
|
|
1767
|
+
reason: StandardResolutionReasons.ERROR,
|
|
1768
|
+
flagMetadata: Object.freeze(flagMetadata),
|
|
1769
|
+
flagKey
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
};
|
|
1773
|
+
|
|
1255
1774
|
// src/client/internal/open-feature-client.ts
|
|
1256
1775
|
var OpenFeatureClient = class {
|
|
1257
1776
|
constructor(providerAccessor, providerStatusAccessor, emitterAccessor, apiContextAccessor, apiHooksAccessor, globalLogger, options) {
|
|
@@ -1676,7 +2195,7 @@ var OpenFeature = (() => {
|
|
|
1676
2195
|
try {
|
|
1677
2196
|
if (typeof wrapper.provider.onContextChange === "function") {
|
|
1678
2197
|
const maybePromise = wrapper.provider.onContextChange(oldContext, newContext);
|
|
1679
|
-
if (typeof (maybePromise == null ? void 0 : maybePromise.then) === "function") {
|
|
2198
|
+
if (maybePromise && typeof (maybePromise == null ? void 0 : maybePromise.then) === "function") {
|
|
1680
2199
|
wrapper.incrementPendingContextChanges();
|
|
1681
2200
|
wrapper.status = this._statusEnumType.RECONCILING;
|
|
1682
2201
|
this.getAssociatedEventEmitters(domain).forEach((emitter) => {
|