@splitsoftware/splitio-commons 2.12.0 → 3.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +19 -1
- package/cjs/evaluator/condition/engineUtils.js +2 -2
- package/cjs/evaluator/fallbackConfigsCalculator/fallbackSanitizer/index.js +70 -0
- package/cjs/evaluator/fallbackConfigsCalculator/index.js +25 -0
- package/cjs/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -8
- package/cjs/evaluator/fallbackTreatmentsCalculator/index.js +21 -32
- package/cjs/evaluator/index.js +73 -48
- package/cjs/evaluator/matchers/whitelist.js +1 -1
- package/cjs/integrations/pluggable.js +1 -1
- package/cjs/listeners/browser.js +4 -3
- package/cjs/listeners/node.js +4 -4
- package/cjs/logger/constants.js +15 -21
- package/cjs/logger/index.js +1 -1
- package/cjs/logger/messages/debug.js +6 -7
- package/cjs/logger/messages/error.js +4 -4
- package/cjs/logger/messages/info.js +2 -4
- package/cjs/logger/messages/warn.js +9 -10
- package/cjs/platform/browser.js +15 -0
- package/cjs/platform/getEventSource/browser.js +8 -0
- package/cjs/platform/getEventSource/eventsource.js +480 -0
- package/cjs/platform/getEventSource/node.js +26 -0
- package/cjs/platform/getFetch/browser.js +9 -0
- package/cjs/platform/getFetch/node.js +27 -0
- package/cjs/platform/getOptions/node.js +25 -0
- package/cjs/platform/node.js +17 -0
- package/cjs/readiness/constants.js +5 -4
- package/cjs/readiness/readinessManager.js +22 -22
- package/cjs/readiness/sdkReadinessManager.js +0 -16
- package/cjs/sdkClient/client.js +9 -35
- package/cjs/sdkClient/clientAttributesDecoration.js +3 -3
- package/cjs/sdkClient/clientInputValidation.js +38 -81
- package/cjs/sdkClient/identity.js +1 -1
- package/cjs/sdkClient/sdkClient.js +4 -49
- package/cjs/sdkClient/sdkLifecycle.js +71 -0
- package/cjs/sdkClient/trackMethod.js +56 -0
- package/cjs/sdkFactory/index.js +13 -50
- package/cjs/sdkManager/index.js +5 -5
- package/cjs/services/authProvider.js +67 -0
- package/cjs/services/secureSplitHttpClient.js +39 -0
- package/cjs/services/splitApi.js +24 -14
- package/cjs/services/splitHttpClient.js +1 -1
- package/cjs/storages/AbstractDefinitionsCacheAsync.js +51 -0
- package/cjs/storages/AbstractDefinitionsCacheSync.js +76 -0
- package/cjs/storages/KeyBuilder.js +16 -16
- package/cjs/storages/KeyBuilderCS.js +17 -17
- package/cjs/storages/KeyBuilderSS.js +16 -16
- package/cjs/storages/getRolloutPlan.js +4 -4
- package/cjs/storages/inLocalStorage/DefinitionsCacheInLocal.js +194 -0
- package/cjs/storages/inLocalStorage/MySegmentsCacheInLocal.js +1 -1
- package/cjs/storages/inLocalStorage/RBSegmentsCacheInLocal.js +10 -10
- package/cjs/storages/inLocalStorage/index.js +6 -6
- package/cjs/storages/inLocalStorage/validateCache.js +3 -3
- package/cjs/storages/inMemory/DefinitionsCacheInMemory.js +121 -0
- package/cjs/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/InMemoryStorage.js +4 -4
- package/cjs/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/cjs/storages/inMemory/MySegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/RBSegmentsCacheInMemory.js +5 -5
- package/cjs/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/cjs/storages/inMemory/TelemetryCacheInMemory.js +5 -5
- package/cjs/storages/inRedis/DefinitionsCacheInRedis.js +243 -0
- package/cjs/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/ImpressionCountsCacheInRedis.js +5 -5
- package/cjs/storages/inRedis/ImpressionsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/RBSegmentsCacheInRedis.js +1 -1
- package/cjs/storages/inRedis/RedisAdapter.js +8 -8
- package/cjs/storages/inRedis/TelemetryCacheInRedis.js +7 -7
- package/cjs/storages/inRedis/UniqueKeysCacheInRedis.js +3 -3
- package/cjs/storages/inRedis/index.js +2 -2
- package/cjs/storages/pluggable/DefinitionsCachePluggable.js +212 -0
- package/cjs/storages/pluggable/EventsCachePluggable.js +1 -1
- package/cjs/storages/pluggable/ImpressionCountsCachePluggable.js +6 -6
- package/cjs/storages/pluggable/RBSegmentsCachePluggable.js +1 -1
- package/cjs/storages/pluggable/TelemetryCachePluggable.js +7 -7
- package/cjs/storages/pluggable/UniqueKeysCachePluggable.js +2 -2
- package/cjs/storages/pluggable/index.js +5 -5
- package/cjs/storages/pluggable/wrapperAdapter.js +1 -1
- package/cjs/storages/setRolloutPlan.js +6 -6
- package/cjs/storages/utils.js +6 -6
- package/cjs/sync/offline/splitsParser/parseCondition.js +1 -1
- package/cjs/sync/offline/syncTasks/fromObjectSyncTask.js +5 -5
- package/cjs/sync/polling/fetchers/segmentChangesFetcher.js +1 -1
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +24 -15
- package/cjs/sync/polling/pollingManagerCS.js +18 -18
- package/cjs/sync/polling/pollingManagerSS.js +13 -13
- package/cjs/sync/polling/syncTasks/definitionsSyncTask.js +12 -0
- package/cjs/sync/polling/updaters/{splitChangesUpdater.js → definitionChangesUpdater.js} +72 -64
- package/cjs/sync/polling/updaters/mySegmentsUpdater.js +3 -3
- package/cjs/sync/polling/updaters/segmentChangesUpdater.js +6 -6
- package/cjs/sync/streaming/SSEClient/index.js +2 -2
- package/cjs/sync/streaming/SSEHandler/NotificationKeeper.js +1 -1
- package/cjs/sync/streaming/SSEHandler/index.js +1 -1
- package/cjs/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +18 -18
- package/cjs/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +2 -2
- package/cjs/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -2
- package/cjs/sync/streaming/constants.js +3 -3
- package/cjs/sync/streaming/parseUtils.js +12 -12
- package/cjs/sync/streaming/pushManager.js +14 -14
- package/cjs/sync/submitters/impressionsSubmitter.js +6 -6
- package/cjs/sync/submitters/submitter.js +1 -1
- package/cjs/sync/submitters/telemetrySubmitter.js +1 -2
- package/cjs/sync/syncManagerOnline.js +9 -6
- package/cjs/trackers/eventTracker.js +10 -12
- package/cjs/trackers/impressionObserver/buildKey.js +1 -1
- package/cjs/trackers/impressionsTracker.js +37 -20
- package/cjs/trackers/uniqueKeysTracker.js +1 -1
- package/cjs/utils/Backoff.js +20 -0
- package/cjs/utils/EventEmitter.js +391 -0
- package/cjs/utils/filter/bloomFilter.js +37 -0
- package/cjs/utils/inputValidation/apiKey.js +1 -1
- package/cjs/utils/inputValidation/attribute.js +2 -2
- package/cjs/utils/inputValidation/{split.js → definition.js} +10 -10
- package/cjs/utils/inputValidation/{splitExistence.js → definitionExistence.js} +6 -6
- package/cjs/utils/inputValidation/definitions.js +25 -0
- package/cjs/utils/inputValidation/eventProperties.js +1 -1
- package/cjs/utils/inputValidation/index.js +7 -7
- package/cjs/utils/inputValidation/isOperational.js +4 -4
- package/cjs/utils/inputValidation/target.js +22 -0
- package/cjs/utils/labels/index.js +3 -3
- package/cjs/utils/lang/index.js +1 -1
- package/cjs/utils/lang/sets.js +4 -4
- package/cjs/utils/promise/timeout.js +1 -1
- package/cjs/utils/settingsValidation/splitFilters.js +4 -4
- package/cjs/utils/settingsValidation/url.js +6 -6
- package/esm/evaluator/condition/engineUtils.js +2 -2
- package/esm/evaluator/fallbackConfigsCalculator/fallbackSanitizer/index.js +64 -0
- package/esm/evaluator/fallbackConfigsCalculator/index.js +21 -0
- package/esm/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.js +5 -8
- package/esm/evaluator/fallbackTreatmentsCalculator/index.js +21 -33
- package/esm/evaluator/index.js +72 -48
- package/esm/evaluator/matchers/whitelist.js +1 -1
- package/esm/integrations/pluggable.js +1 -1
- package/esm/listeners/browser.js +4 -3
- package/esm/listeners/node.js +4 -4
- package/esm/logger/constants.js +12 -18
- package/esm/logger/index.js +1 -1
- package/esm/logger/messages/debug.js +5 -6
- package/esm/logger/messages/error.js +3 -3
- package/esm/logger/messages/info.js +1 -3
- package/esm/logger/messages/warn.js +8 -9
- package/esm/platform/browser.js +12 -0
- package/esm/platform/getEventSource/browser.js +4 -0
- package/esm/platform/getEventSource/eventsource.js +480 -0
- package/esm/platform/getEventSource/node.js +20 -0
- package/esm/platform/getFetch/browser.js +5 -0
- package/esm/platform/getFetch/node.js +22 -0
- package/esm/platform/getOptions/node.js +20 -0
- package/esm/platform/node.js +14 -0
- package/esm/readiness/constants.js +4 -3
- package/esm/readiness/readinessManager.js +23 -23
- package/esm/readiness/sdkReadinessManager.js +1 -17
- package/esm/sdkClient/client.js +10 -37
- package/esm/sdkClient/clientAttributesDecoration.js +3 -3
- package/esm/sdkClient/clientInputValidation.js +40 -83
- package/esm/sdkClient/identity.js +1 -1
- package/esm/sdkClient/sdkClient.js +4 -49
- package/esm/sdkClient/sdkLifecycle.js +67 -0
- package/esm/sdkClient/trackMethod.js +52 -0
- package/esm/sdkFactory/index.js +14 -51
- package/esm/sdkManager/index.js +6 -6
- package/esm/services/authProvider.js +63 -0
- package/esm/services/secureSplitHttpClient.js +35 -0
- package/esm/services/splitApi.js +24 -14
- package/esm/services/splitHttpClient.js +1 -1
- package/esm/storages/AbstractDefinitionsCacheAsync.js +48 -0
- package/esm/storages/AbstractDefinitionsCacheSync.js +71 -0
- package/esm/storages/KeyBuilder.js +16 -16
- package/esm/storages/KeyBuilderCS.js +16 -16
- package/esm/storages/KeyBuilderSS.js +15 -15
- package/esm/storages/getRolloutPlan.js +4 -4
- package/esm/storages/inLocalStorage/DefinitionsCacheInLocal.js +191 -0
- package/esm/storages/inLocalStorage/RBSegmentsCacheInLocal.js +7 -7
- package/esm/storages/inLocalStorage/index.js +6 -6
- package/esm/storages/inLocalStorage/validateCache.js +3 -3
- package/esm/storages/inMemory/DefinitionsCacheInMemory.js +118 -0
- package/esm/storages/inMemory/ImpressionCountsCacheInMemory.js +1 -1
- package/esm/storages/inMemory/InMemoryStorage.js +4 -4
- package/esm/storages/inMemory/InMemoryStorageCS.js +5 -5
- package/esm/storages/inMemory/RBSegmentsCacheInMemory.js +2 -2
- package/esm/storages/inMemory/SegmentsCacheInMemory.js +1 -1
- package/esm/storages/inMemory/TelemetryCacheInMemory.js +5 -5
- package/esm/storages/inRedis/DefinitionsCacheInRedis.js +240 -0
- package/esm/storages/inRedis/EventsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/ImpressionCountsCacheInRedis.js +4 -4
- package/esm/storages/inRedis/RBSegmentsCacheInRedis.js +1 -1
- package/esm/storages/inRedis/RedisAdapter.js +7 -7
- package/esm/storages/inRedis/TelemetryCacheInRedis.js +7 -7
- package/esm/storages/inRedis/UniqueKeysCacheInRedis.js +1 -1
- package/esm/storages/inRedis/index.js +2 -2
- package/esm/storages/pluggable/DefinitionsCachePluggable.js +209 -0
- package/esm/storages/pluggable/EventsCachePluggable.js +1 -1
- package/esm/storages/pluggable/ImpressionCountsCachePluggable.js +5 -5
- package/esm/storages/pluggable/RBSegmentsCachePluggable.js +1 -1
- package/esm/storages/pluggable/TelemetryCachePluggable.js +7 -7
- package/esm/storages/pluggable/UniqueKeysCachePluggable.js +1 -1
- package/esm/storages/pluggable/index.js +4 -4
- package/esm/storages/pluggable/wrapperAdapter.js +1 -1
- package/esm/storages/setRolloutPlan.js +6 -6
- package/esm/storages/utils.js +6 -6
- package/esm/sync/offline/splitsParser/parseCondition.js +1 -1
- package/esm/sync/offline/syncTasks/fromObjectSyncTask.js +6 -6
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +25 -16
- package/esm/sync/polling/pollingManagerCS.js +15 -15
- package/esm/sync/polling/pollingManagerSS.js +13 -13
- package/esm/sync/polling/syncTasks/definitionsSyncTask.js +8 -0
- package/esm/sync/polling/updaters/definitionChangesUpdater.js +200 -0
- package/esm/sync/polling/updaters/mySegmentsUpdater.js +3 -3
- package/esm/sync/polling/updaters/segmentChangesUpdater.js +7 -7
- package/esm/sync/streaming/SSEClient/index.js +2 -2
- package/esm/sync/streaming/SSEHandler/NotificationKeeper.js +2 -2
- package/esm/sync/streaming/SSEHandler/index.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.js → DefinitionsUpdateWorker.js} +17 -17
- package/esm/sync/streaming/UpdateWorkers/MySegmentsUpdateWorker.js +2 -2
- package/esm/sync/streaming/UpdateWorkers/SegmentsUpdateWorker.js +2 -2
- package/esm/sync/streaming/constants.js +2 -2
- package/esm/sync/streaming/parseUtils.js +10 -10
- package/esm/sync/streaming/pushManager.js +16 -16
- package/esm/sync/submitters/impressionsSubmitter.js +6 -6
- package/esm/sync/submitters/submitter.js +1 -1
- package/esm/sync/submitters/telemetrySubmitter.js +1 -2
- package/esm/sync/syncManagerOnline.js +8 -5
- package/esm/trackers/eventTracker.js +10 -12
- package/esm/trackers/impressionObserver/buildKey.js +1 -1
- package/esm/trackers/impressionsTracker.js +38 -21
- package/esm/trackers/uniqueKeysTracker.js +1 -1
- package/esm/utils/Backoff.js +20 -0
- package/esm/utils/EventEmitter.js +387 -0
- package/esm/utils/filter/bloomFilter.js +33 -0
- package/esm/utils/inputValidation/apiKey.js +1 -1
- package/esm/utils/inputValidation/attribute.js +2 -2
- package/esm/utils/inputValidation/{split.js → definition.js} +8 -8
- package/esm/utils/inputValidation/definitionExistence.js +15 -0
- package/esm/utils/inputValidation/definitions.js +21 -0
- package/esm/utils/inputValidation/index.js +3 -3
- package/esm/utils/inputValidation/isOperational.js +4 -4
- package/esm/utils/inputValidation/target.js +18 -0
- package/esm/utils/labels/index.js +2 -2
- package/esm/utils/lang/sets.js +4 -4
- package/esm/utils/promise/timeout.js +1 -1
- package/esm/utils/settingsValidation/splitFilters.js +4 -4
- package/esm/utils/settingsValidation/url.js +6 -6
- package/package.json +15 -6
- package/src/dtos/types.ts +65 -70
- package/src/evaluator/Engine.ts +4 -4
- package/src/evaluator/combiners/and.ts +3 -3
- package/src/evaluator/combiners/ifelseif.ts +2 -2
- package/src/evaluator/condition/engineUtils.ts +2 -2
- package/src/evaluator/condition/index.ts +3 -3
- package/src/evaluator/fallbackConfigsCalculator/fallbackSanitizer/index.ts +76 -0
- package/src/evaluator/fallbackConfigsCalculator/index.ts +24 -0
- package/src/evaluator/fallbackTreatmentsCalculator/fallbackSanitizer/index.ts +5 -7
- package/src/evaluator/fallbackTreatmentsCalculator/index.ts +27 -41
- package/src/evaluator/index.ts +86 -51
- package/src/evaluator/matchers/dependency.ts +2 -2
- package/src/evaluator/matchers/matcherTypes.ts +2 -2
- package/src/evaluator/matchers/prerequisites.ts +4 -4
- package/src/evaluator/matchers/rbsegment.ts +2 -2
- package/src/evaluator/matchers/whitelist.ts +2 -2
- package/src/evaluator/matchersTransform/index.ts +2 -2
- package/src/evaluator/matchersTransform/whitelist.ts +2 -2
- package/src/evaluator/parser/index.ts +4 -4
- package/src/evaluator/treatments/index.ts +2 -2
- package/src/evaluator/types.ts +6 -6
- package/src/evaluator/value/index.ts +1 -1
- package/src/integrations/pluggable.ts +1 -1
- package/src/listeners/browser.ts +11 -8
- package/src/listeners/node.ts +2 -5
- package/src/logger/constants.ts +12 -18
- package/src/logger/messages/debug.ts +5 -6
- package/src/logger/messages/error.ts +3 -3
- package/src/logger/messages/info.ts +1 -3
- package/src/logger/messages/warn.ts +8 -9
- package/src/platform/browser.ts +14 -0
- package/src/platform/getEventSource/browser.ts +4 -0
- package/src/platform/getEventSource/eventsource.js +519 -0
- package/src/platform/getEventSource/node.ts +23 -0
- package/src/platform/getFetch/browser.ts +5 -0
- package/src/platform/getFetch/node.ts +27 -0
- package/src/platform/getOptions/node.ts +24 -0
- package/src/platform/node.ts +16 -0
- package/src/readiness/constants.ts +4 -3
- package/src/readiness/readinessManager.ts +22 -22
- package/src/readiness/sdkReadinessManager.ts +1 -16
- package/src/readiness/types.ts +10 -16
- package/src/sdkClient/client.ts +11 -41
- package/src/sdkClient/clientInputValidation.ts +44 -84
- package/src/sdkClient/sdkClient.ts +5 -54
- package/src/sdkClient/sdkLifecycle.ts +79 -0
- package/src/sdkClient/trackMethod.ts +59 -0
- package/src/sdkFactory/index.ts +15 -59
- package/src/sdkFactory/types.ts +19 -16
- package/src/sdkManager/index.ts +15 -15
- package/src/services/authProvider.ts +86 -0
- package/src/services/secureSplitHttpClient.ts +44 -0
- package/src/services/splitApi.ts +14 -2
- package/src/services/types.ts +7 -2
- package/src/storages/AbstractDefinitionsCacheAsync.ts +63 -0
- package/src/storages/AbstractDefinitionsCacheSync.ts +95 -0
- package/src/storages/KeyBuilder.ts +6 -6
- package/src/storages/KeyBuilderCS.ts +6 -6
- package/src/storages/KeyBuilderSS.ts +2 -2
- package/src/storages/getRolloutPlan.ts +3 -3
- package/src/storages/inLocalStorage/DefinitionsCacheInLocal.ts +234 -0
- package/src/storages/inLocalStorage/RBSegmentsCacheInLocal.ts +8 -8
- package/src/storages/inLocalStorage/index.ts +6 -6
- package/src/storages/inLocalStorage/validateCache.ts +4 -4
- package/src/storages/inMemory/DefinitionsCacheInMemory.ts +130 -0
- package/src/storages/inMemory/InMemoryStorage.ts +4 -4
- package/src/storages/inMemory/InMemoryStorageCS.ts +5 -5
- package/src/storages/inMemory/RBSegmentsCacheInMemory.ts +3 -3
- package/src/storages/inMemory/SegmentsCacheInMemory.ts +1 -1
- package/src/storages/inMemory/TelemetryCacheInMemory.ts +5 -5
- package/src/storages/inRedis/{SplitsCacheInRedis.ts → DefinitionsCacheInRedis.ts} +69 -69
- package/src/storages/inRedis/RBSegmentsCacheInRedis.ts +2 -2
- package/src/storages/inRedis/index.ts +2 -2
- package/src/storages/pluggable/DefinitionsCachePluggable.ts +230 -0
- package/src/storages/pluggable/RBSegmentsCachePluggable.ts +2 -2
- package/src/storages/pluggable/index.ts +2 -2
- package/src/storages/setRolloutPlan.ts +7 -7
- package/src/storages/types.ts +34 -29
- package/src/sync/offline/splitsParser/parseCondition.ts +2 -2
- package/src/sync/offline/splitsParser/splitsParserFromSettings.ts +3 -3
- package/src/sync/offline/splitsParser/types.ts +6 -3
- package/src/sync/offline/syncManagerOffline.ts +2 -2
- package/src/sync/offline/syncTasks/fromObjectSyncTask.ts +14 -14
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +58 -23
- package/src/sync/polling/fetchers/types.ts +5 -3
- package/src/sync/polling/pollingManagerCS.ts +16 -14
- package/src/sync/polling/pollingManagerSS.ts +15 -13
- package/src/sync/polling/syncTasks/{splitsSyncTask.ts → definitionsSyncTask.ts} +11 -12
- package/src/sync/polling/types.ts +3 -3
- package/src/sync/polling/updaters/definitionChangesUpdater.ts +239 -0
- package/src/sync/polling/updaters/mySegmentsUpdater.ts +3 -3
- package/src/sync/polling/updaters/segmentChangesUpdater.ts +7 -7
- package/src/sync/streaming/AuthClient/index.ts +2 -2
- package/src/sync/streaming/AuthClient/types.ts +14 -11
- package/src/sync/streaming/SSEClient/index.ts +2 -2
- package/src/sync/streaming/SSEClient/types.ts +2 -2
- package/src/sync/streaming/SSEHandler/NotificationKeeper.ts +2 -2
- package/src/sync/streaming/SSEHandler/index.ts +2 -2
- package/src/sync/streaming/UpdateWorkers/{SplitsUpdateWorker.ts → DefinitionsUpdateWorker.ts} +21 -21
- package/src/sync/streaming/constants.ts +2 -2
- package/src/sync/streaming/parseUtils.ts +12 -12
- package/src/sync/streaming/pushManager.ts +18 -18
- package/src/sync/streaming/types.ts +2 -2
- package/src/sync/submitters/impressionsSubmitter.ts +9 -9
- package/src/sync/submitters/telemetrySubmitter.ts +1 -2
- package/src/sync/submitters/types.ts +3 -0
- package/src/sync/syncManagerOnline.ts +9 -5
- package/src/trackers/eventTracker.ts +7 -10
- package/src/trackers/impressionsTracker.ts +42 -26
- package/src/trackers/types.ts +3 -1
- package/src/utils/Backoff.ts +21 -0
- package/src/utils/EventEmitter.js +444 -0
- package/src/utils/filter/bloomFilter.ts +38 -0
- package/src/utils/inputValidation/definition.ts +27 -0
- package/src/utils/inputValidation/definitionExistence.ts +19 -0
- package/src/utils/inputValidation/definitions.ts +21 -0
- package/src/utils/inputValidation/index.ts +3 -3
- package/src/utils/inputValidation/isOperational.ts +4 -4
- package/src/utils/inputValidation/target.ts +21 -0
- package/src/utils/inputValidation/trafficTypeExistence.ts +2 -2
- package/src/utils/jwt/types.ts +1 -1
- package/src/utils/labels/index.ts +2 -2
- package/src/utils/lang/sets.ts +4 -2
- package/src/utils/settingsValidation/splitFilters.ts +3 -3
- package/src/utils/settingsValidation/url.ts +1 -1
- package/types/splitio.d.ts +222 -11
- package/cjs/storages/AbstractSplitsCacheAsync.js +0 -51
- package/cjs/storages/AbstractSplitsCacheSync.js +0 -76
- package/cjs/storages/inLocalStorage/SplitsCacheInLocal.js +0 -194
- package/cjs/storages/inMemory/SplitsCacheInMemory.js +0 -121
- package/cjs/storages/inRedis/SplitsCacheInRedis.js +0 -243
- package/cjs/storages/pluggable/SplitsCachePluggable.js +0 -212
- package/cjs/sync/polling/syncTasks/splitsSyncTask.js +0 -13
- package/cjs/utils/inputValidation/splits.js +0 -25
- package/esm/storages/AbstractSplitsCacheAsync.js +0 -48
- package/esm/storages/AbstractSplitsCacheSync.js +0 -71
- package/esm/storages/inLocalStorage/SplitsCacheInLocal.js +0 -191
- package/esm/storages/inMemory/SplitsCacheInMemory.js +0 -118
- package/esm/storages/inRedis/SplitsCacheInRedis.js +0 -240
- package/esm/storages/pluggable/SplitsCachePluggable.js +0 -209
- package/esm/sync/polling/syncTasks/splitsSyncTask.js +0 -9
- package/esm/sync/polling/updaters/splitChangesUpdater.js +0 -192
- package/esm/utils/inputValidation/splitExistence.js +0 -15
- package/esm/utils/inputValidation/splits.js +0 -21
- package/src/storages/AbstractSplitsCacheAsync.ts +0 -63
- package/src/storages/AbstractSplitsCacheSync.ts +0 -95
- package/src/storages/inLocalStorage/SplitsCacheInLocal.ts +0 -234
- package/src/storages/inMemory/SplitsCacheInMemory.ts +0 -130
- package/src/storages/pluggable/SplitsCachePluggable.ts +0 -230
- package/src/sync/polling/updaters/splitChangesUpdater.ts +0 -230
- package/src/utils/inputValidation/split.ts +0 -27
- package/src/utils/inputValidation/splitExistence.ts +0 -19
- package/src/utils/inputValidation/splits.ts +0 -21
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { releaseApiKey, validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
|
|
2
|
+
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
3
|
+
|
|
4
|
+
const COOLDOWN_TIME_IN_MILLIS = 1000;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates an Sdk client, i.e., a base client with status, init, flush and destroy interface
|
|
8
|
+
*/
|
|
9
|
+
export function sdkLifecycleFactory(params: ISdkFactoryContext, isSharedClient?: boolean): { init(): void; flush(): Promise<void>; destroy(): Promise<void> } {
|
|
10
|
+
const { sdkReadinessManager, syncManager, storage, settings, telemetryTracker, impressionsTracker, platform, splitApi } = params;
|
|
11
|
+
|
|
12
|
+
let hasInit = false;
|
|
13
|
+
let lastActionTime = 0;
|
|
14
|
+
|
|
15
|
+
const signalListener = platform.SignalListener && new platform.SignalListener(params);
|
|
16
|
+
|
|
17
|
+
function __cooldown(func: Function, time: number) {
|
|
18
|
+
const now = Date.now();
|
|
19
|
+
//get the actual time elapsed in ms
|
|
20
|
+
const timeElapsed = now - lastActionTime;
|
|
21
|
+
//check if the time elapsed is less than desired cooldown
|
|
22
|
+
if (timeElapsed < time) {
|
|
23
|
+
//if yes, return message with remaining time in seconds
|
|
24
|
+
settings.log.warn(`Flush cooldown, remaining time ${(time - timeElapsed) / 1000} seconds`);
|
|
25
|
+
return Promise.resolve();
|
|
26
|
+
} else {
|
|
27
|
+
//Do the requested action and re-assign the lastActionTime
|
|
28
|
+
lastActionTime = now;
|
|
29
|
+
return func();
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function __flush() {
|
|
34
|
+
return syncManager ? syncManager.flush() : Promise.resolve();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
init() {
|
|
39
|
+
if (hasInit) return;
|
|
40
|
+
hasInit = true;
|
|
41
|
+
|
|
42
|
+
if (!isSharedClient) {
|
|
43
|
+
validateAndTrackApiKey(settings.log, settings.core.authorizationKey);
|
|
44
|
+
sdkReadinessManager.readinessManager.init();
|
|
45
|
+
impressionsTracker.start();
|
|
46
|
+
syncManager && syncManager.start();
|
|
47
|
+
signalListener && signalListener.start();
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
flush() {
|
|
52
|
+
// @TODO define cooldown time
|
|
53
|
+
return __cooldown(__flush, COOLDOWN_TIME_IN_MILLIS);
|
|
54
|
+
},
|
|
55
|
+
|
|
56
|
+
destroy() {
|
|
57
|
+
hasInit = false;
|
|
58
|
+
// Mark the SDK as destroyed immediately
|
|
59
|
+
sdkReadinessManager.readinessManager.destroy();
|
|
60
|
+
|
|
61
|
+
// For main client, cleanup the SDK Key, listeners and scheduled jobs, and record stat before flushing data
|
|
62
|
+
if (!isSharedClient) {
|
|
63
|
+
releaseApiKey(settings.core.authorizationKey);
|
|
64
|
+
telemetryTracker.sessionLength();
|
|
65
|
+
signalListener && signalListener.stop();
|
|
66
|
+
impressionsTracker.stop();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Stop background jobs
|
|
70
|
+
syncManager && syncManager.stop();
|
|
71
|
+
splitApi && splitApi.stop();
|
|
72
|
+
|
|
73
|
+
return __flush().then(() => {
|
|
74
|
+
// Cleanup storage
|
|
75
|
+
return storage.destroy();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { thenable } from '../utils/promise/thenable';
|
|
2
|
+
import { getMatching } from '../utils/key';
|
|
3
|
+
import { validateTrafficTypeExistence } from '../utils/inputValidation/trafficTypeExistence';
|
|
4
|
+
import { validateKey, validateTrafficType, validateEvent, validateEventValue, validateEventProperties, validateIfNotDestroyed } from '../utils/inputValidation';
|
|
5
|
+
import { TRACK, TRACK_FN_LABEL } from '../utils/constants';
|
|
6
|
+
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
7
|
+
import SplitIO from '../../types/splitio';
|
|
8
|
+
import { ISdkFactoryContext } from '../sdkFactory/types';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a standalone `track` function with input validation.
|
|
12
|
+
* Reusable by FF SDK client, Configs SDK, and thin-client SDK.
|
|
13
|
+
*/
|
|
14
|
+
export function trackMethodFactory(params: Pick<ISdkFactoryContext, 'settings' | 'eventTracker' | 'telemetryTracker' | 'storage' | 'sdkReadinessManager'>, warnTTExistence = true) {
|
|
15
|
+
const { settings, storage: { definitions }, telemetryTracker, eventTracker, sdkReadinessManager: { readinessManager } } = params;
|
|
16
|
+
const { log, mode } = settings;
|
|
17
|
+
const isAsync = isConsumerMode(mode);
|
|
18
|
+
|
|
19
|
+
return function track(maybeKey: SplitIO.SplitKey, maybeTT: string, maybeEvent: string, maybeEventValue?: number, maybeProperties?: SplitIO.Properties) {
|
|
20
|
+
// Input validation
|
|
21
|
+
const key = validateKey(log, maybeKey, TRACK_FN_LABEL);
|
|
22
|
+
const trafficTypeName = validateTrafficType(log, maybeTT, TRACK_FN_LABEL);
|
|
23
|
+
const eventTypeId = validateEvent(log, maybeEvent, TRACK_FN_LABEL);
|
|
24
|
+
const value = validateEventValue(log, maybeEventValue, TRACK_FN_LABEL);
|
|
25
|
+
const { properties, size } = validateEventProperties(log, maybeProperties, TRACK_FN_LABEL);
|
|
26
|
+
const isNotDestroyed = validateIfNotDestroyed(log, readinessManager, TRACK_FN_LABEL);
|
|
27
|
+
|
|
28
|
+
if (!(isNotDestroyed && key && trafficTypeName && eventTypeId && value !== false && properties !== false)) {
|
|
29
|
+
return isAsync ? Promise.resolve(false) : false;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Core logic
|
|
33
|
+
const stopTelemetryTracker = telemetryTracker.trackEval(TRACK);
|
|
34
|
+
|
|
35
|
+
const eventData: SplitIO.EventData = {
|
|
36
|
+
eventTypeId,
|
|
37
|
+
trafficTypeName,
|
|
38
|
+
value,
|
|
39
|
+
timestamp: Date.now(),
|
|
40
|
+
key: getMatching(key),
|
|
41
|
+
properties: properties as SplitIO.Properties | undefined
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// This may be async but we only warn, we don't actually care if it is valid or not in terms of queueing the event.
|
|
45
|
+
if (warnTTExistence) validateTrafficTypeExistence(log, readinessManager, definitions, mode, trafficTypeName, TRACK_FN_LABEL);
|
|
46
|
+
|
|
47
|
+
const result = eventTracker.track(eventData, size);
|
|
48
|
+
|
|
49
|
+
if (thenable(result)) {
|
|
50
|
+
return result.then((result) => {
|
|
51
|
+
stopTelemetryTracker();
|
|
52
|
+
return result;
|
|
53
|
+
});
|
|
54
|
+
} else {
|
|
55
|
+
stopTelemetryTracker();
|
|
56
|
+
return result;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
package/src/sdkFactory/index.ts
CHANGED
|
@@ -4,16 +4,10 @@ import { impressionsTrackerFactory } from '../trackers/impressionsTracker';
|
|
|
4
4
|
import { eventTrackerFactory } from '../trackers/eventTracker';
|
|
5
5
|
import { telemetryTrackerFactory } from '../trackers/telemetryTracker';
|
|
6
6
|
import SplitIO from '../../types/splitio';
|
|
7
|
-
import { validateAndTrackApiKey } from '../utils/inputValidation/apiKey';
|
|
8
7
|
import { createLoggerAPI } from '../logger/sdkLogger';
|
|
9
8
|
import { NEW_FACTORY, RETRIEVE_MANAGER } from '../logger/constants';
|
|
10
|
-
import {
|
|
9
|
+
import { SDK_DEFINITIONS_ARRIVED, SDK_SEGMENTS_ARRIVED, SDK_DEFINITIONS_CACHE_LOADED } from '../readiness/constants';
|
|
11
10
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
12
|
-
import { strategyDebugFactory } from '../trackers/strategy/strategyDebug';
|
|
13
|
-
import { strategyOptimizedFactory } from '../trackers/strategy/strategyOptimized';
|
|
14
|
-
import { strategyNoneFactory } from '../trackers/strategy/strategyNone';
|
|
15
|
-
import { uniqueKeysTrackerFactory } from '../trackers/uniqueKeysTracker';
|
|
16
|
-
import { DEBUG, OPTIMIZED } from '../utils/constants';
|
|
17
11
|
import { setRolloutPlan } from '../storages/setRolloutPlan';
|
|
18
12
|
import { IStorageSync } from '../storages/types';
|
|
19
13
|
import { getMatching } from '../utils/key';
|
|
@@ -24,24 +18,13 @@ import { FallbackTreatmentsCalculator } from '../evaluator/fallbackTreatmentsCal
|
|
|
24
18
|
*/
|
|
25
19
|
export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IAsyncSDK | SplitIO.IBrowserSDK | SplitIO.IBrowserAsyncSDK {
|
|
26
20
|
|
|
27
|
-
const { settings, platform, storageFactory, splitApiFactory, extraProps,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
filterAdapterFactory, lazyInit } = params;
|
|
31
|
-
const { log, sync: { impressionsMode }, initialRolloutPlan, core: { key } } = settings;
|
|
21
|
+
const { settings, platform, storageFactory, splitApiFactory, extraProps, syncManagerFactory,
|
|
22
|
+
integrationsManagerFactory, sdkManagerFactory, sdkClientMethodFactory, lazyInit } = params;
|
|
23
|
+
const { log, initialRolloutPlan, core: { key } } = settings;
|
|
32
24
|
|
|
33
25
|
// @TODO handle non-recoverable errors, such as, global `fetch` not available, invalid SDK Key, etc.
|
|
34
26
|
// On non-recoverable errors, we should mark the SDK as destroyed and not start synchronization.
|
|
35
27
|
|
|
36
|
-
// initialization
|
|
37
|
-
let hasInit = false;
|
|
38
|
-
const initCallbacks: (() => void)[] = [];
|
|
39
|
-
|
|
40
|
-
function whenInit(cb: () => void) {
|
|
41
|
-
if (hasInit) cb();
|
|
42
|
-
else initCallbacks.push(cb);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
28
|
const sdkReadinessManager = sdkReadinessManagerFactory(platform.EventEmitter, settings);
|
|
46
29
|
const readiness = sdkReadinessManager.readinessManager;
|
|
47
30
|
|
|
@@ -53,67 +36,41 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
53
36
|
readiness.timeout();
|
|
54
37
|
return;
|
|
55
38
|
}
|
|
56
|
-
readiness.
|
|
39
|
+
readiness.definitions.emit(SDK_DEFINITIONS_ARRIVED);
|
|
57
40
|
readiness.segments.emit(SDK_SEGMENTS_ARRIVED);
|
|
58
41
|
},
|
|
59
42
|
onReadyFromCacheCb() {
|
|
60
|
-
readiness.
|
|
43
|
+
readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED);
|
|
61
44
|
}
|
|
62
45
|
});
|
|
63
46
|
|
|
64
|
-
const
|
|
47
|
+
const fallbackCalculator = FallbackTreatmentsCalculator(settings.fallbackTreatments);
|
|
65
48
|
|
|
66
49
|
if (initialRolloutPlan) {
|
|
67
50
|
setRolloutPlan(log, initialRolloutPlan, storage as IStorageSync, key && getMatching(key));
|
|
68
|
-
if ((storage as IStorageSync).
|
|
51
|
+
if ((storage as IStorageSync).definitions.getChangeNumber() > -1) readiness.definitions.emit(SDK_DEFINITIONS_CACHE_LOADED, { initialCacheLoad: false /* Not an initial load, cache exists */ });
|
|
69
52
|
}
|
|
70
53
|
|
|
71
|
-
const clients: Record<string, SplitIO.IBasicClient> = {};
|
|
54
|
+
const clients: Record<string, SplitIO.IBasicClient & { init: () => void }> = {};
|
|
72
55
|
const telemetryTracker = telemetryTrackerFactory(storage.telemetry, platform.now);
|
|
73
56
|
const integrationsManager = integrationsManagerFactory && integrationsManagerFactory({ settings, storage, telemetryTracker });
|
|
74
57
|
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const noneStrategy = strategyNoneFactory(storage.impressionCounts, uniqueKeysTracker);
|
|
79
|
-
const strategy = impressionsMode === OPTIMIZED ?
|
|
80
|
-
strategyOptimizedFactory(observer, storage.impressionCounts) :
|
|
81
|
-
impressionsMode === DEBUG ?
|
|
82
|
-
strategyDebugFactory(observer) :
|
|
83
|
-
noneStrategy;
|
|
84
|
-
|
|
85
|
-
const impressionsTracker = impressionsTrackerFactory(settings, storage.impressions, noneStrategy, strategy, whenInit, integrationsManager, storage.telemetry);
|
|
86
|
-
const eventTracker = eventTrackerFactory(settings, storage.events, whenInit, integrationsManager, storage.telemetry);
|
|
58
|
+
const impressionsTracker = impressionsTrackerFactory(params, storage, integrationsManager);
|
|
59
|
+
const eventTracker = eventTrackerFactory(settings, storage.events, integrationsManager, storage.telemetry);
|
|
87
60
|
|
|
88
61
|
// splitApi is used by SyncManager and Browser signal listener
|
|
89
62
|
const splitApi = splitApiFactory && splitApiFactory(settings, platform, telemetryTracker);
|
|
90
63
|
|
|
91
|
-
const ctx: ISdkFactoryContext = { clients, splitApi, eventTracker, impressionsTracker, telemetryTracker,
|
|
92
|
-
|
|
93
|
-
const syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
|
|
94
|
-
ctx.syncManager = syncManager;
|
|
95
|
-
|
|
96
|
-
const signalListener = SignalListener && new SignalListener(syncManager, settings, storage, splitApi);
|
|
97
|
-
ctx.signalListener = signalListener;
|
|
64
|
+
const ctx: ISdkFactoryContext = { clients, splitApi, eventTracker, impressionsTracker, telemetryTracker, sdkReadinessManager, readiness, settings, storage, platform, fallbackCalculator };
|
|
65
|
+
ctx.syncManager = syncManagerFactory && syncManagerFactory(ctx as ISdkFactoryContextSync);
|
|
98
66
|
|
|
99
67
|
// SDK client and manager
|
|
100
68
|
const clientMethod = sdkClientMethodFactory(ctx);
|
|
101
|
-
const managerInstance = sdkManagerFactory(settings, storage.
|
|
69
|
+
const managerInstance = sdkManagerFactory(settings, storage.definitions, sdkReadinessManager);
|
|
102
70
|
|
|
103
71
|
|
|
104
72
|
function init() {
|
|
105
|
-
|
|
106
|
-
hasInit = true;
|
|
107
|
-
|
|
108
|
-
// We will just log and allow for the SDK to end up throwing an SDK_TIMEOUT event for devs to handle.
|
|
109
|
-
validateAndTrackApiKey(log, settings.core.authorizationKey);
|
|
110
|
-
readiness.init();
|
|
111
|
-
uniqueKeysTracker.start();
|
|
112
|
-
syncManager && syncManager.start();
|
|
113
|
-
signalListener && signalListener.start();
|
|
114
|
-
|
|
115
|
-
initCallbacks.forEach((cb) => cb());
|
|
116
|
-
initCallbacks.length = 0;
|
|
73
|
+
Object.keys(clients).map(key => clients[key].init());
|
|
117
74
|
}
|
|
118
75
|
|
|
119
76
|
log.info(NEW_FACTORY, [settings.version]);
|
|
@@ -135,7 +92,6 @@ export function sdkFactory(params: ISdkFactoryParams): SplitIO.ISDK | SplitIO.IA
|
|
|
135
92
|
settings,
|
|
136
93
|
|
|
137
94
|
destroy() {
|
|
138
|
-
hasInit = false;
|
|
139
95
|
return Promise.all(Object.keys(clients).map(key => clients[key].destroy())).then(() => { });
|
|
140
96
|
}
|
|
141
97
|
}, extraProps && extraProps(ctx), lazyInit ? { init } : init());
|
package/src/sdkFactory/types.ts
CHANGED
|
@@ -3,12 +3,12 @@ import { ISignalListener } from '../listeners/types';
|
|
|
3
3
|
import { IReadinessManager, ISdkReadinessManager } from '../readiness/types';
|
|
4
4
|
import type { sdkManagerFactory } from '../sdkManager';
|
|
5
5
|
import type { splitApiFactory } from '../services/splitApi';
|
|
6
|
-
import type {
|
|
6
|
+
import type { IFallbackCalculator } from '../evaluator/fallbackTreatmentsCalculator';
|
|
7
7
|
import { IFetch, ISplitApi, IEventSourceConstructor } from '../services/types';
|
|
8
8
|
import { IStorageAsync, IStorageSync, IStorageFactoryParams } from '../storages/types';
|
|
9
9
|
import { ISyncManager } from '../sync/types';
|
|
10
10
|
import { IImpressionObserver } from '../trackers/impressionObserver/types';
|
|
11
|
-
import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter
|
|
11
|
+
import { IImpressionsTracker, IEventTracker, ITelemetryTracker, IFilterAdapter } from '../trackers/types';
|
|
12
12
|
import { ISettings } from '../types';
|
|
13
13
|
import SplitIO from '../../types/splitio';
|
|
14
14
|
|
|
@@ -35,9 +35,17 @@ export interface IPlatform {
|
|
|
35
35
|
/**
|
|
36
36
|
* Function used to track latencies for telemetry.
|
|
37
37
|
*/
|
|
38
|
-
now?: () => number
|
|
38
|
+
now?: () => number,
|
|
39
|
+
/**
|
|
40
|
+
* Optional signal listener constructor. Used to listen and handle runtime environment states, like server shutdown, app paused or resumed.
|
|
41
|
+
*/
|
|
42
|
+
// eslint-disable-next-line no-use-before-define
|
|
43
|
+
SignalListener?: new (params: ISdkFactoryContext) => ISignalListener, // Used by BrowserSignalListener
|
|
39
44
|
}
|
|
40
45
|
|
|
46
|
+
// Definition type
|
|
47
|
+
export type EntityType = 'config' | 'flag';
|
|
48
|
+
|
|
41
49
|
export interface ISdkFactoryContext {
|
|
42
50
|
platform: IPlatform,
|
|
43
51
|
sdkReadinessManager: ISdkReadinessManager,
|
|
@@ -47,12 +55,11 @@ export interface ISdkFactoryContext {
|
|
|
47
55
|
eventTracker: IEventTracker,
|
|
48
56
|
telemetryTracker: ITelemetryTracker,
|
|
49
57
|
storage: IStorageSync | IStorageAsync,
|
|
50
|
-
|
|
51
|
-
signalListener?: ISignalListener
|
|
52
|
-
splitApi?: ISplitApi
|
|
58
|
+
splitApi?: ISplitApi,
|
|
53
59
|
syncManager?: ISyncManager,
|
|
54
60
|
clients: Record<string, SplitIO.IBasicClient>,
|
|
55
|
-
|
|
61
|
+
fallbackCalculator: IFallbackCalculator,
|
|
62
|
+
entityType?: EntityType
|
|
56
63
|
}
|
|
57
64
|
|
|
58
65
|
export interface ISdkFactoryContextSync extends ISdkFactoryContext {
|
|
@@ -98,21 +105,17 @@ export interface ISdkFactoryParams {
|
|
|
98
105
|
|
|
99
106
|
// Sdk client method factory.
|
|
100
107
|
// It Allows to distinguish SDK clients with the client-side API (`IBrowserSDK` and `IBrowserAsyncSDK`) or server-side API (`ISDK` and `IAsyncSDK`).
|
|
101
|
-
sdkClientMethodFactory: (params: ISdkFactoryContext) => (
|
|
108
|
+
sdkClientMethodFactory: (params: ISdkFactoryContext) => (
|
|
109
|
+
{ (): SplitIO.IBrowserClient & { init(): void }; (key: SplitIO.SplitKey): SplitIO.IBrowserClient & { init(): void }; } |
|
|
110
|
+
(() => SplitIO.IClient & { init(): void }) |
|
|
111
|
+
(() => SplitIO.IAsyncClient & { init(): void })
|
|
112
|
+
)
|
|
102
113
|
|
|
103
114
|
// Impression observer factory.
|
|
104
115
|
impressionsObserverFactory: () => IImpressionObserver
|
|
105
116
|
|
|
106
117
|
filterAdapterFactory?: () => IFilterAdapter
|
|
107
118
|
|
|
108
|
-
// Optional signal listener constructor. Used to handle special app states, like shutdown, app paused or resumed.
|
|
109
|
-
// Pass only if `syncManager` (used by NodeSignalListener) and `splitApi` (used by Browser listener) are passed.
|
|
110
|
-
SignalListener?: new (
|
|
111
|
-
syncManager: ISyncManager | undefined, // Used by NodeSignalListener to flush data, and by BrowserSignalListener to close streaming connection.
|
|
112
|
-
settings: ISettings, // Used by BrowserSignalListener
|
|
113
|
-
storage: IStorageSync | IStorageAsync, // Used by BrowserSignalListener
|
|
114
|
-
serviceApi: ISplitApi | undefined) => ISignalListener, // Used by BrowserSignalListener
|
|
115
|
-
|
|
116
119
|
// @TODO review impressionListener and integrations interfaces. What about handling impressionListener as an integration ?
|
|
117
120
|
integrationsManagerFactory?: (params: IIntegrationFactoryParams) => IIntegrationManager | undefined,
|
|
118
121
|
|
package/src/sdkManager/index.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
2
2
|
import { thenable } from '../utils/promise/thenable';
|
|
3
3
|
import { find } from '../utils/lang';
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { validateDefinition, validateDefinitionExistence, validateIfOperational } from '../utils/inputValidation';
|
|
5
|
+
import { IDefinitionsCacheAsync, IDefinitionsCacheSync } from '../storages/types';
|
|
6
6
|
import { ISdkReadinessManager } from '../readiness/types';
|
|
7
|
-
import {
|
|
7
|
+
import { IDefinition } from '../dtos/types';
|
|
8
8
|
import { ISettings } from '../types';
|
|
9
9
|
import SplitIO from '../../types/splitio';
|
|
10
10
|
import { isConsumerMode } from '../utils/settingsValidation/mode';
|
|
11
11
|
import { SPLIT_FN_LABEL, SPLITS_FN_LABEL, NAMES_FN_LABEL } from '../utils/constants';
|
|
12
12
|
|
|
13
|
-
function collectTreatments(splitObject:
|
|
13
|
+
function collectTreatments(splitObject: IDefinition) {
|
|
14
14
|
const conditions = splitObject.conditions;
|
|
15
15
|
// Rollout conditions are supposed to have the entire partitions list, so we find the first one.
|
|
16
16
|
let allTreatmentsCondition = find(conditions, (cond) => cond.conditionType === 'ROLLOUT');
|
|
@@ -20,7 +20,7 @@ function collectTreatments(splitObject: ISplit) {
|
|
|
20
20
|
return allTreatmentsCondition ? allTreatmentsCondition.partitions!.map(v => v.treatment) : [];
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
function objectToView(splitObject:
|
|
23
|
+
function objectToView(splitObject: IDefinition | null): SplitIO.SplitView | null {
|
|
24
24
|
if (!splitObject) return null;
|
|
25
25
|
|
|
26
26
|
return {
|
|
@@ -29,7 +29,7 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
|
|
|
29
29
|
killed: splitObject.killed,
|
|
30
30
|
changeNumber: splitObject.changeNumber || 0,
|
|
31
31
|
treatments: collectTreatments(splitObject),
|
|
32
|
-
configs: splitObject.configurations || {},
|
|
32
|
+
configs: splitObject.configurations as SplitIO.SplitView['configs'] || {},
|
|
33
33
|
sets: splitObject.sets || [],
|
|
34
34
|
defaultTreatment: splitObject.defaultTreatment,
|
|
35
35
|
impressionsDisabled: splitObject.impressionsDisabled === true,
|
|
@@ -37,7 +37,7 @@ function objectToView(splitObject: ISplit | null): SplitIO.SplitView | null {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function objectsToViews(splitObjects:
|
|
40
|
+
function objectsToViews(splitObjects: IDefinition[]) {
|
|
41
41
|
let views: SplitIO.SplitView[] = [];
|
|
42
42
|
|
|
43
43
|
splitObjects.forEach(split => {
|
|
@@ -48,11 +48,11 @@ function objectsToViews(splitObjects: ISplit[]) {
|
|
|
48
48
|
return views;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export function sdkManagerFactory<
|
|
51
|
+
export function sdkManagerFactory<TDefinitionsCache extends IDefinitionsCacheSync | IDefinitionsCacheAsync>(
|
|
52
52
|
settings: Pick<ISettings, 'log' | 'mode'>,
|
|
53
|
-
splits:
|
|
53
|
+
splits: TDefinitionsCache,
|
|
54
54
|
{ readinessManager, sdkStatus }: ISdkReadinessManager,
|
|
55
|
-
):
|
|
55
|
+
): TDefinitionsCache extends IDefinitionsCacheAsync ? SplitIO.IAsyncManager : SplitIO.IManager {
|
|
56
56
|
|
|
57
57
|
const { log, mode } = settings;
|
|
58
58
|
const isAsync = isConsumerMode(mode);
|
|
@@ -65,21 +65,21 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
|
|
|
65
65
|
* Get the feature flag object corresponding to the given feature flag name if valid
|
|
66
66
|
*/
|
|
67
67
|
split(featureFlagName: string) {
|
|
68
|
-
const splitName =
|
|
68
|
+
const splitName = validateDefinition(log, featureFlagName, SPLIT_FN_LABEL);
|
|
69
69
|
if (!validateIfOperational(log, readinessManager, SPLIT_FN_LABEL) || !splitName) {
|
|
70
70
|
return isAsync ? Promise.resolve(null) : null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const split = splits.
|
|
73
|
+
const split = splits.get(splitName);
|
|
74
74
|
|
|
75
75
|
if (thenable(split)) {
|
|
76
76
|
return split.catch(() => null).then(result => { // handle possible rejections when using pluggable storage
|
|
77
|
-
|
|
77
|
+
validateDefinitionExistence(log, readinessManager, splitName, result, SPLIT_FN_LABEL);
|
|
78
78
|
return objectToView(result);
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
validateDefinitionExistence(log, readinessManager, splitName, split, SPLIT_FN_LABEL);
|
|
83
83
|
|
|
84
84
|
return objectToView(split);
|
|
85
85
|
},
|
|
@@ -103,7 +103,7 @@ export function sdkManagerFactory<TSplitCache extends ISplitsCacheSync | ISplits
|
|
|
103
103
|
if (!validateIfOperational(log, readinessManager, NAMES_FN_LABEL)) {
|
|
104
104
|
return isAsync ? Promise.resolve([]) : [];
|
|
105
105
|
}
|
|
106
|
-
const splitNames = splits.
|
|
106
|
+
const splitNames = splits.getNames();
|
|
107
107
|
|
|
108
108
|
return thenable(splitNames) ?
|
|
109
109
|
splitNames.catch(() => []) : // handle possible rejections when using pluggable storage
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { ISplitHttpClient, NetworkError } from './types';
|
|
2
|
+
import { IJwtCredentialV3 } from '../sync/streaming/AuthClient/types';
|
|
3
|
+
import { authenticateFactory } from '../sync/streaming/AuthClient';
|
|
4
|
+
import { Backoff } from '../utils/Backoff';
|
|
5
|
+
import { LOG_PREFIX_SYNC_AUTH } from '../logger/constants';
|
|
6
|
+
import { ISettings } from '../types';
|
|
7
|
+
import { TOKEN } from '../utils/constants';
|
|
8
|
+
import { ITelemetryTracker } from '../trackers/types';
|
|
9
|
+
|
|
10
|
+
const SKEW_SECONDS = 30;
|
|
11
|
+
|
|
12
|
+
function isExpired(credential: IJwtCredentialV3): boolean {
|
|
13
|
+
return Date.now() / 1000 + SKEW_SECONDS >= credential.decodedToken.exp;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface IAuthProvider {
|
|
17
|
+
credential(): Promise<IJwtCredentialV3>;
|
|
18
|
+
invalidate(): void;
|
|
19
|
+
stop(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Factory of AuthProvider, which provides JWT credentials for authenticated HTTP requests.
|
|
24
|
+
* Credentials are fetched lazily on demand, cached in memory, and retried with backoff on failure.
|
|
25
|
+
*/
|
|
26
|
+
export function authProviderFactory(settings: ISettings, splitHttpClient: ISplitHttpClient, telemetryTracker: ITelemetryTracker): IAuthProvider {
|
|
27
|
+
|
|
28
|
+
const { urls, log } = settings;
|
|
29
|
+
|
|
30
|
+
function fetchAuth() {
|
|
31
|
+
let url = `${urls.auth}/v3/auth?capabilities=config`;
|
|
32
|
+
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const authenticate = authenticateFactory(fetchAuth);
|
|
36
|
+
const backoff = new Backoff(fetchCredential);
|
|
37
|
+
|
|
38
|
+
let cachedCredential: IJwtCredentialV3 | undefined;
|
|
39
|
+
let inFlightPromise: Promise<IJwtCredentialV3> | undefined;
|
|
40
|
+
let stopped = false;
|
|
41
|
+
|
|
42
|
+
function fetchCredential(): Promise<IJwtCredentialV3> {
|
|
43
|
+
return authenticate().then((credential: IJwtCredentialV3) => {
|
|
44
|
+
log.info(LOG_PREFIX_SYNC_AUTH + 'credential fetched successfully');
|
|
45
|
+
cachedCredential = credential;
|
|
46
|
+
inFlightPromise = undefined;
|
|
47
|
+
backoff.reset();
|
|
48
|
+
return credential;
|
|
49
|
+
}).catch((error: NetworkError) => {
|
|
50
|
+
// Avoid rejected promises and unnecessary retries after stop()
|
|
51
|
+
if (stopped) return cachedCredential!;
|
|
52
|
+
|
|
53
|
+
if (error.statusCode && error.statusCode >= 400 && error.statusCode < 500) {
|
|
54
|
+
log.error(LOG_PREFIX_SYNC_AUTH + 'non-retryable error fetching credential (status ' + error.statusCode + '): ' + error.message);
|
|
55
|
+
inFlightPromise = undefined;
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
log.warn(LOG_PREFIX_SYNC_AUTH + 'credential fetch failed (attempt ' + (backoff.attempts + 1) + '). Error: ' + error.message);
|
|
60
|
+
return backoff.scheduleCallAsync();
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
credential(): Promise<IJwtCredentialV3> {
|
|
66
|
+
if (cachedCredential && !isExpired(cachedCredential)) {
|
|
67
|
+
return Promise.resolve(cachedCredential);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (cachedCredential) log.debug(LOG_PREFIX_SYNC_AUTH + 'cached credential expired');
|
|
71
|
+
|
|
72
|
+
return inFlightPromise || (inFlightPromise = fetchCredential());
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
invalidate() {
|
|
76
|
+
cachedCredential = undefined;
|
|
77
|
+
},
|
|
78
|
+
|
|
79
|
+
stop() {
|
|
80
|
+
stopped = true;
|
|
81
|
+
cachedCredential = undefined;
|
|
82
|
+
inFlightPromise = undefined;
|
|
83
|
+
backoff.reset();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { IRequestOptions, IResponse, ISecureSplitHttpClient, NetworkError } from './types';
|
|
2
|
+
import { ISettings } from '../types';
|
|
3
|
+
import { IPlatform } from '../sdkFactory/types';
|
|
4
|
+
import { splitHttpClientFactory } from './splitHttpClient';
|
|
5
|
+
import { authProviderFactory } from './authProvider';
|
|
6
|
+
import { ITelemetryTracker } from '../trackers/types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Factory of Secure HTTP client, which authenticates requests using a JWT token.
|
|
10
|
+
* On 401 responses, invalidates the cached credential and retries once with a fresh token.
|
|
11
|
+
*
|
|
12
|
+
* @param settings - SDK settings
|
|
13
|
+
* @param platform - object containing environment-specific dependencies
|
|
14
|
+
* @param fetchAuth - function to fetch auth credentials from the /v2/auth endpoint
|
|
15
|
+
*/
|
|
16
|
+
export function secureSplitHttpClientFactory(settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker): ISecureSplitHttpClient {
|
|
17
|
+
|
|
18
|
+
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
19
|
+
const authProvider = authProviderFactory(settings, splitHttpClient, telemetryTracker);
|
|
20
|
+
|
|
21
|
+
function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker: ((error?: NetworkError) => void) | undefined, logErrorsAsInfo: boolean | undefined, token: string): Promise<IResponse> {
|
|
22
|
+
return splitHttpClient(url, { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } }, latencyTracker, logErrorsAsInfo);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean): Promise<IResponse> {
|
|
26
|
+
return authProvider.credential().then(credential => {
|
|
27
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, credential.token)
|
|
28
|
+
.catch((error: NetworkError) => {
|
|
29
|
+
if (error.statusCode === 401) {
|
|
30
|
+
// retry once for 401, in case the token has just expired
|
|
31
|
+
authProvider.invalidate();
|
|
32
|
+
return authProvider.credential().then(newCredential => {
|
|
33
|
+
return makeRequest(url, options, latencyTracker, logErrorsAsInfo, newCredential.token);
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
throw error;
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
httpClient.stop = () => authProvider.stop();
|
|
42
|
+
|
|
43
|
+
return httpClient;
|
|
44
|
+
}
|
package/src/services/splitApi.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IPlatform } from '../sdkFactory/types';
|
|
2
2
|
import { ISettings } from '../types';
|
|
3
3
|
import { splitHttpClientFactory } from './splitHttpClient';
|
|
4
|
-
import { ISplitApi } from './types';
|
|
4
|
+
import { ISecureSplitHttpClient, ISplitApi } from './types';
|
|
5
5
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
6
6
|
import { ITelemetryTracker } from '../trackers/types';
|
|
7
7
|
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
@@ -23,13 +23,15 @@ function userKeyToQueryParam(userKey: string) {
|
|
|
23
23
|
export function splitApiFactory(
|
|
24
24
|
settings: ISettings,
|
|
25
25
|
platform: Pick<IPlatform, 'getOptions' | 'getFetch'>,
|
|
26
|
-
telemetryTracker: ITelemetryTracker
|
|
26
|
+
telemetryTracker: ITelemetryTracker,
|
|
27
|
+
secureSplitHttpClientFactory?: (settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker) => ISecureSplitHttpClient,
|
|
27
28
|
): ISplitApi {
|
|
28
29
|
|
|
29
30
|
const urls = settings.urls;
|
|
30
31
|
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
31
32
|
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
32
33
|
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
34
|
+
const secureSplitHttpClient = secureSplitHttpClientFactory ? secureSplitHttpClientFactory(settings, platform, telemetryTracker) : undefined;
|
|
33
35
|
|
|
34
36
|
return {
|
|
35
37
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
@@ -61,6 +63,12 @@ export function splitApiFactory(
|
|
|
61
63
|
});
|
|
62
64
|
},
|
|
63
65
|
|
|
66
|
+
// @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
|
|
67
|
+
fetchConfigs(since: number, noCache?: boolean, till?: number) {
|
|
68
|
+
const url = `${urls.sdk}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
69
|
+
return (secureSplitHttpClient || splitHttpClient)(url, noCache ? noCacheHeaderOptions : undefined);
|
|
70
|
+
},
|
|
71
|
+
|
|
64
72
|
fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
|
|
65
73
|
const url = `${urls.sdk}/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
|
|
66
74
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
|
|
@@ -143,6 +151,10 @@ export function splitApiFactory(
|
|
|
143
151
|
postMetricsUsage(body: string, headers?: Record<string, string>) {
|
|
144
152
|
const url = `${urls.telemetry}/v1/metrics/usage`;
|
|
145
153
|
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
stop() {
|
|
157
|
+
if (secureSplitHttpClient) secureSplitHttpClient.stop();
|
|
146
158
|
}
|
|
147
159
|
};
|
|
148
160
|
}
|
package/src/services/types.ts
CHANGED
|
@@ -33,9 +33,11 @@ export type IHealthCheckAPI = () => Promise<boolean>
|
|
|
33
33
|
|
|
34
34
|
export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean) => Promise<IResponse>
|
|
35
35
|
|
|
36
|
+
export type ISecureSplitHttpClient = ISplitHttpClient & { stop(): void }
|
|
37
|
+
|
|
36
38
|
export type IFetchAuth = (userKeys?: string[]) => Promise<IResponse>
|
|
37
39
|
|
|
38
|
-
export type
|
|
40
|
+
export type IFetchDefinitionChanges = (since: number, noCache?: boolean, till?: number, rbSince?: number) => Promise<IResponse>
|
|
39
41
|
|
|
40
42
|
export type IFetchSegmentChanges = (since: number, segmentName: string, noCache?: boolean, till?: number) => Promise<IResponse>
|
|
41
43
|
|
|
@@ -59,7 +61,8 @@ export interface ISplitApi {
|
|
|
59
61
|
getSdkAPIHealthCheck: IHealthCheckAPI
|
|
60
62
|
getEventsAPIHealthCheck: IHealthCheckAPI
|
|
61
63
|
fetchAuth: IFetchAuth
|
|
62
|
-
fetchSplitChanges:
|
|
64
|
+
fetchSplitChanges: IFetchDefinitionChanges
|
|
65
|
+
fetchConfigs: IFetchDefinitionChanges
|
|
63
66
|
fetchSegmentChanges: IFetchSegmentChanges
|
|
64
67
|
fetchMemberships: IFetchMemberships
|
|
65
68
|
postEventsBulk: IPostEventsBulk
|
|
@@ -69,6 +72,8 @@ export interface ISplitApi {
|
|
|
69
72
|
postTestImpressionsCount: IPostTestImpressionsCount
|
|
70
73
|
postMetricsConfig: IPostMetricsConfig
|
|
71
74
|
postMetricsUsage: IPostMetricsUsage
|
|
75
|
+
// lifecycle
|
|
76
|
+
stop(): void
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
// Minimal version of EventSource API used by the SDK
|