@launchdarkly/js-sdk-common 2.9.0 → 2.10.1-beta.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/CHANGELOG.md +9 -0
- package/dist/{AttributeReference.d.ts → cjs/AttributeReference.d.ts} +2 -2
- package/dist/{AttributeReference.d.ts.map → cjs/AttributeReference.d.ts.map} +1 -1
- package/dist/{Context.d.ts → cjs/Context.d.ts} +13 -13
- package/dist/cjs/Context.d.ts.map +1 -0
- package/dist/cjs/ContextFilter.d.ts +11 -0
- package/dist/cjs/ContextFilter.d.ts.map +1 -0
- package/dist/{api → cjs/api}/platform/Requests.d.ts +5 -0
- package/dist/{api → cjs/api}/platform/Requests.d.ts.map +1 -1
- package/dist/cjs/datasource/DataSourceErrorKinds.d.ts +7 -0
- package/dist/cjs/datasource/DataSourceErrorKinds.d.ts.map +1 -0
- package/dist/cjs/datasource/errors.d.ts +17 -0
- package/dist/cjs/datasource/errors.d.ts.map +1 -0
- package/dist/cjs/datasource/index.d.ts +4 -0
- package/dist/cjs/datasource/index.d.ts.map +1 -0
- package/dist/{errors.d.ts → cjs/errors.d.ts} +0 -11
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/index.cjs +2479 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/{index.d.ts → cjs/index.d.ts} +2 -1
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/{internal → cjs/internal}/diagnostics/DiagnosticsManager.d.ts +7 -7
- package/dist/{internal → cjs/internal}/diagnostics/DiagnosticsManager.d.ts.map +1 -1
- package/dist/{internal → cjs/internal}/evaluation/EventFactoryBase.d.ts +2 -2
- package/dist/{internal → cjs/internal}/evaluation/EventFactoryBase.d.ts.map +1 -1
- package/dist/{internal → cjs/internal}/events/ClientMessages.d.ts +1 -1
- package/dist/cjs/internal/events/EventProcessor.d.ts +44 -0
- package/dist/cjs/internal/events/EventProcessor.d.ts.map +1 -0
- package/dist/{internal → cjs/internal}/events/EventSender.d.ts +6 -6
- package/dist/{internal → cjs/internal}/events/EventSender.d.ts.map +1 -1
- package/dist/{internal → cjs/internal}/index.d.ts +1 -1
- package/dist/cjs/internal/index.d.ts.map +1 -0
- package/dist/{internal → cjs/internal}/stream/StreamingProcessor.d.ts +14 -14
- package/dist/cjs/internal/stream/StreamingProcessor.d.ts.map +1 -0
- package/dist/cjs/internal/stream/types.d.ts +3 -0
- package/dist/{internal → cjs/internal}/stream/types.d.ts.map +1 -1
- package/dist/{logging → cjs/logging}/BasicLogger.d.ts +7 -7
- package/dist/cjs/logging/BasicLogger.d.ts.map +1 -0
- package/dist/{logging → cjs/logging}/SafeLogger.d.ts +3 -3
- package/dist/{logging → cjs/logging}/SafeLogger.d.ts.map +1 -1
- package/dist/{options → cjs/options}/ServiceEndpoints.d.ts.map +1 -1
- package/dist/cjs/package.json +1 -0
- package/dist/{validators.d.ts → cjs/validators.d.ts} +2 -2
- package/dist/{validators.d.ts.map → cjs/validators.d.ts.map} +1 -1
- package/dist/esm/AttributeReference.d.ts +35 -0
- package/dist/esm/AttributeReference.d.ts.map +1 -0
- package/dist/esm/Context.d.ts +95 -0
- package/dist/esm/Context.d.ts.map +1 -0
- package/dist/esm/ContextFilter.d.ts +11 -0
- package/dist/esm/ContextFilter.d.ts.map +1 -0
- package/dist/esm/api/context/LDContext.d.ts +8 -0
- package/dist/esm/api/context/LDContext.d.ts.map +1 -0
- package/dist/esm/api/context/LDContextCommon.d.ts +29 -0
- package/dist/esm/api/context/LDContextCommon.d.ts.map +1 -0
- package/dist/esm/api/context/LDContextMeta.d.ts +52 -0
- package/dist/esm/api/context/LDContextMeta.d.ts.map +1 -0
- package/dist/esm/api/context/LDMultiKindContext.d.ts +46 -0
- package/dist/esm/api/context/LDMultiKindContext.d.ts.map +1 -0
- package/dist/esm/api/context/LDSingleKindContext.d.ts +24 -0
- package/dist/esm/api/context/LDSingleKindContext.d.ts.map +1 -0
- package/dist/esm/api/context/LDUser.d.ts +66 -0
- package/dist/esm/api/context/LDUser.d.ts.map +1 -0
- package/dist/esm/api/context/index.d.ts +7 -0
- package/dist/esm/api/context/index.d.ts.map +1 -0
- package/dist/esm/api/data/LDEvaluationDetail.d.ts +43 -0
- package/dist/esm/api/data/LDEvaluationDetail.d.ts.map +1 -0
- package/dist/esm/api/data/LDEvaluationReason.d.ts +57 -0
- package/dist/esm/api/data/LDEvaluationReason.d.ts.map +1 -0
- package/dist/esm/api/data/LDFlagSet.d.ts +8 -0
- package/dist/esm/api/data/LDFlagSet.d.ts.map +1 -0
- package/dist/esm/api/data/LDFlagValue.d.ts +7 -0
- package/dist/esm/api/data/LDFlagValue.d.ts.map +1 -0
- package/dist/esm/api/data/index.d.ts +5 -0
- package/dist/esm/api/data/index.d.ts.map +1 -0
- package/dist/esm/api/index.d.ts +7 -0
- package/dist/esm/api/index.d.ts.map +1 -0
- package/dist/esm/api/logging/BasicLoggerOptions.d.ts +43 -0
- package/dist/esm/api/logging/BasicLoggerOptions.d.ts.map +1 -0
- package/dist/esm/api/logging/LDLogLevel.d.ts +10 -0
- package/dist/esm/api/logging/LDLogLevel.d.ts.map +1 -0
- package/dist/esm/api/logging/LDLogger.d.ts +47 -0
- package/dist/esm/api/logging/LDLogger.d.ts.map +1 -0
- package/dist/esm/api/logging/index.d.ts +4 -0
- package/dist/esm/api/logging/index.d.ts.map +1 -0
- package/dist/esm/api/options/LDClientContext.d.ts +46 -0
- package/dist/esm/api/options/LDClientContext.d.ts.map +1 -0
- package/dist/esm/api/options/index.d.ts +3 -0
- package/dist/esm/api/options/index.d.ts.map +1 -0
- package/dist/esm/api/platform/AutoEnv.d.ts +50 -0
- package/dist/esm/api/platform/AutoEnv.d.ts.map +1 -0
- package/dist/esm/api/platform/Crypto.d.ts +45 -0
- package/dist/esm/api/platform/Crypto.d.ts.map +1 -0
- package/dist/esm/api/platform/Encoding.d.ts +4 -0
- package/dist/esm/api/platform/Encoding.d.ts.map +1 -0
- package/dist/esm/api/platform/EventSource.d.ts +31 -0
- package/dist/esm/api/platform/EventSource.d.ts.map +1 -0
- package/dist/esm/api/platform/Filesystem.d.ts +40 -0
- package/dist/esm/api/platform/Filesystem.d.ts.map +1 -0
- package/dist/esm/api/platform/Info.d.ts +80 -0
- package/dist/esm/api/platform/Info.d.ts.map +1 -0
- package/dist/esm/api/platform/Platform.d.ts +36 -0
- package/dist/esm/api/platform/Platform.d.ts.map +1 -0
- package/dist/esm/api/platform/Requests.d.ts +107 -0
- package/dist/esm/api/platform/Requests.d.ts.map +1 -0
- package/dist/esm/api/platform/Storage.d.ts +28 -0
- package/dist/esm/api/platform/Storage.d.ts.map +1 -0
- package/dist/esm/api/platform/index.d.ts +10 -0
- package/dist/esm/api/platform/index.d.ts.map +1 -0
- package/dist/esm/api/subsystem/LDContextDeduplicator.d.ts +24 -0
- package/dist/esm/api/subsystem/LDContextDeduplicator.d.ts.map +1 -0
- package/dist/esm/api/subsystem/LDEventProcessor.d.ts +7 -0
- package/dist/esm/api/subsystem/LDEventProcessor.d.ts.map +1 -0
- package/dist/esm/api/subsystem/LDEventSender.d.ts +18 -0
- package/dist/esm/api/subsystem/LDEventSender.d.ts.map +1 -0
- package/dist/esm/api/subsystem/LDStreamProcessor.d.ts +13 -0
- package/dist/esm/api/subsystem/LDStreamProcessor.d.ts.map +1 -0
- package/dist/esm/api/subsystem/index.d.ts +6 -0
- package/dist/esm/api/subsystem/index.d.ts.map +1 -0
- package/dist/esm/datasource/DataSourceErrorKinds.d.ts +7 -0
- package/dist/esm/datasource/DataSourceErrorKinds.d.ts.map +1 -0
- package/dist/esm/datasource/errors.d.ts +17 -0
- package/dist/esm/datasource/errors.d.ts.map +1 -0
- package/dist/esm/datasource/index.d.ts +4 -0
- package/dist/esm/datasource/index.d.ts.map +1 -0
- package/dist/esm/errors.d.ts +25 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/index.d.ts +13 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.mjs +2432 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/internal/context/index.d.ts +24 -0
- package/dist/esm/internal/context/index.d.ts.map +1 -0
- package/dist/esm/internal/diagnostics/DiagnosticsManager.d.ts +35 -0
- package/dist/esm/internal/diagnostics/DiagnosticsManager.d.ts.map +1 -0
- package/dist/esm/internal/diagnostics/index.d.ts +3 -0
- package/dist/esm/internal/diagnostics/index.d.ts.map +1 -0
- package/dist/esm/internal/diagnostics/types.d.ts +65 -0
- package/dist/esm/internal/diagnostics/types.d.ts.map +1 -0
- package/dist/esm/internal/evaluation/ErrorKinds.d.ts +12 -0
- package/dist/esm/internal/evaluation/ErrorKinds.d.ts.map +1 -0
- package/dist/esm/internal/evaluation/EventFactoryBase.d.ts +27 -0
- package/dist/esm/internal/evaluation/EventFactoryBase.d.ts.map +1 -0
- package/dist/esm/internal/evaluation/index.d.ts +4 -0
- package/dist/esm/internal/evaluation/index.d.ts.map +1 -0
- package/dist/esm/internal/events/ClientMessages.d.ts +8 -0
- package/dist/esm/internal/events/ClientMessages.d.ts.map +1 -0
- package/dist/esm/internal/events/EventProcessor.d.ts +44 -0
- package/dist/esm/internal/events/EventProcessor.d.ts.map +1 -0
- package/dist/esm/internal/events/EventSender.d.ts +14 -0
- package/dist/esm/internal/events/EventSender.d.ts.map +1 -0
- package/dist/esm/internal/events/EventSummarizer.d.ts +2 -0
- package/dist/esm/internal/events/EventSummarizer.d.ts.map +1 -0
- package/dist/esm/internal/events/InputClickEvent.d.ts +11 -0
- package/dist/esm/internal/events/InputClickEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/InputCustomEvent.d.ts +13 -0
- package/dist/esm/internal/events/InputCustomEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/InputEvalEvent.d.ts +22 -0
- package/dist/esm/internal/events/InputEvalEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/InputEvent.d.ts +9 -0
- package/dist/esm/internal/events/InputEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/InputEventBase.d.ts +8 -0
- package/dist/esm/internal/events/InputEventBase.d.ts.map +1 -0
- package/dist/esm/internal/events/InputIdentifyEvent.d.ts +9 -0
- package/dist/esm/internal/events/InputIdentifyEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/InputMigrationEvent.d.ts +10 -0
- package/dist/esm/internal/events/InputMigrationEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/InputPageViewEvent.d.ts +10 -0
- package/dist/esm/internal/events/InputPageViewEvent.d.ts.map +1 -0
- package/dist/esm/internal/events/LDInternalOptions.d.ts +25 -0
- package/dist/esm/internal/events/LDInternalOptions.d.ts.map +1 -0
- package/dist/esm/internal/events/LDInvalidSDKKeyError.d.ts +4 -0
- package/dist/esm/internal/events/LDInvalidSDKKeyError.d.ts.map +1 -0
- package/dist/esm/internal/events/NullEventProcessor.d.ts +7 -0
- package/dist/esm/internal/events/NullEventProcessor.d.ts.map +1 -0
- package/dist/esm/internal/events/SummaryCounter.d.ts +2 -0
- package/dist/esm/internal/events/SummaryCounter.d.ts.map +1 -0
- package/dist/esm/internal/events/guards.d.ts +9 -0
- package/dist/esm/internal/events/guards.d.ts.map +1 -0
- package/dist/esm/internal/events/index.d.ts +12 -0
- package/dist/esm/internal/events/index.d.ts.map +1 -0
- package/dist/esm/internal/events/sampling.d.ts +6 -0
- package/dist/esm/internal/events/sampling.d.ts.map +1 -0
- package/dist/esm/internal/index.d.ts +6 -0
- package/dist/esm/internal/index.d.ts.map +1 -0
- package/dist/esm/internal/stream/StreamingProcessor.d.ts +39 -0
- package/dist/esm/internal/stream/StreamingProcessor.d.ts.map +1 -0
- package/dist/esm/internal/stream/index.d.ts +5 -0
- package/dist/esm/internal/stream/index.d.ts.map +1 -0
- package/dist/esm/internal/stream/types.d.ts +3 -0
- package/dist/esm/internal/stream/types.d.ts.map +1 -0
- package/dist/esm/logging/BasicLogger.d.ts +33 -0
- package/dist/esm/logging/BasicLogger.d.ts.map +1 -0
- package/dist/esm/logging/SafeLogger.d.ts +28 -0
- package/dist/esm/logging/SafeLogger.d.ts.map +1 -0
- package/dist/esm/logging/createSafeLogger.d.ts +6 -0
- package/dist/esm/logging/createSafeLogger.d.ts.map +1 -0
- package/dist/esm/logging/format.d.ts +2 -0
- package/dist/esm/logging/format.d.ts.map +1 -0
- package/dist/esm/logging/index.d.ts +5 -0
- package/dist/esm/logging/index.d.ts.map +1 -0
- package/dist/esm/options/ApplicationTags.d.ts +17 -0
- package/dist/esm/options/ApplicationTags.d.ts.map +1 -0
- package/dist/esm/options/ClientContext.d.ts +43 -0
- package/dist/esm/options/ClientContext.d.ts.map +1 -0
- package/dist/esm/options/OptionMessages.d.ts +14 -0
- package/dist/esm/options/OptionMessages.d.ts.map +1 -0
- package/dist/esm/options/ServiceEndpoints.d.ts +58 -0
- package/dist/esm/options/ServiceEndpoints.d.ts.map +1 -0
- package/dist/esm/options/index.d.ts +6 -0
- package/dist/esm/options/index.d.ts.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/utils/VoidFunction.d.ts +2 -0
- package/dist/esm/utils/VoidFunction.d.ts.map +1 -0
- package/dist/esm/utils/cancelableTimedPromise.d.ts +20 -0
- package/dist/esm/utils/cancelableTimedPromise.d.ts.map +1 -0
- package/dist/esm/utils/clone.d.ts +2 -0
- package/dist/esm/utils/clone.d.ts.map +1 -0
- package/dist/esm/utils/date.d.ts +2 -0
- package/dist/esm/utils/date.d.ts.map +1 -0
- package/dist/{utils/debounce.js → esm/utils/debounce.d.ts} +3 -13
- package/dist/esm/utils/debounce.d.ts.map +1 -0
- package/dist/esm/utils/deepCompact.d.ts +11 -0
- package/dist/esm/utils/deepCompact.d.ts.map +1 -0
- package/dist/esm/utils/fast-deep-equal/index.d.ts +2 -0
- package/dist/esm/utils/fast-deep-equal/index.d.ts.map +1 -0
- package/dist/esm/utils/http.d.ts +23 -0
- package/dist/esm/utils/http.d.ts.map +1 -0
- package/dist/esm/utils/index.d.ts +13 -0
- package/dist/esm/utils/index.d.ts.map +1 -0
- package/dist/esm/utils/isEmptyObject.d.ts +3 -0
- package/dist/esm/utils/isEmptyObject.d.ts.map +1 -0
- package/dist/esm/utils/noop.d.ts +3 -0
- package/dist/esm/utils/noop.d.ts.map +1 -0
- package/dist/esm/utils/sleep.d.ts +3 -0
- package/dist/esm/utils/sleep.d.ts.map +1 -0
- package/dist/esm/utils/timedPromise.d.ts +9 -0
- package/dist/esm/utils/timedPromise.d.ts.map +1 -0
- package/dist/esm/validators.d.ts +98 -0
- package/dist/esm/validators.d.ts.map +1 -0
- package/package.json +27 -8
- package/dist/AttributeReference.js +0 -126
- package/dist/AttributeReference.js.map +0 -1
- package/dist/Context.d.ts.map +0 -1
- package/dist/Context.js +0 -382
- package/dist/Context.js.map +0 -1
- package/dist/ContextFilter.d.ts +0 -11
- package/dist/ContextFilter.d.ts.map +0 -1
- package/dist/ContextFilter.js +0 -124
- package/dist/ContextFilter.js.map +0 -1
- package/dist/api/context/LDContext.js +0 -3
- package/dist/api/context/LDContext.js.map +0 -1
- package/dist/api/context/LDContextCommon.js +0 -3
- package/dist/api/context/LDContextCommon.js.map +0 -1
- package/dist/api/context/LDContextMeta.js +0 -3
- package/dist/api/context/LDContextMeta.js.map +0 -1
- package/dist/api/context/LDMultiKindContext.js +0 -3
- package/dist/api/context/LDMultiKindContext.js.map +0 -1
- package/dist/api/context/LDSingleKindContext.js +0 -3
- package/dist/api/context/LDSingleKindContext.js.map +0 -1
- package/dist/api/context/LDUser.js +0 -3
- package/dist/api/context/LDUser.js.map +0 -1
- package/dist/api/context/index.js +0 -23
- package/dist/api/context/index.js.map +0 -1
- package/dist/api/data/LDEvaluationDetail.js +0 -3
- package/dist/api/data/LDEvaluationDetail.js.map +0 -1
- package/dist/api/data/LDEvaluationReason.js +0 -3
- package/dist/api/data/LDEvaluationReason.js.map +0 -1
- package/dist/api/data/LDFlagSet.js +0 -3
- package/dist/api/data/LDFlagSet.js.map +0 -1
- package/dist/api/data/LDFlagValue.js +0 -3
- package/dist/api/data/LDFlagValue.js.map +0 -1
- package/dist/api/data/index.js +0 -21
- package/dist/api/data/index.js.map +0 -1
- package/dist/api/index.js +0 -24
- package/dist/api/index.js.map +0 -1
- package/dist/api/logging/BasicLoggerOptions.js +0 -3
- package/dist/api/logging/BasicLoggerOptions.js.map +0 -1
- package/dist/api/logging/LDLogLevel.js +0 -3
- package/dist/api/logging/LDLogLevel.js.map +0 -1
- package/dist/api/logging/LDLogger.js +0 -3
- package/dist/api/logging/LDLogger.js.map +0 -1
- package/dist/api/logging/index.js +0 -20
- package/dist/api/logging/index.js.map +0 -1
- package/dist/api/options/LDClientContext.js +0 -3
- package/dist/api/options/LDClientContext.js.map +0 -1
- package/dist/api/options/index.js +0 -4
- package/dist/api/options/index.js.map +0 -1
- package/dist/api/platform/AutoEnv.js +0 -20
- package/dist/api/platform/AutoEnv.js.map +0 -1
- package/dist/api/platform/Crypto.js +0 -3
- package/dist/api/platform/Crypto.js.map +0 -1
- package/dist/api/platform/Encoding.js +0 -3
- package/dist/api/platform/Encoding.js.map +0 -1
- package/dist/api/platform/EventSource.js +0 -3
- package/dist/api/platform/EventSource.js.map +0 -1
- package/dist/api/platform/Filesystem.js +0 -3
- package/dist/api/platform/Filesystem.js.map +0 -1
- package/dist/api/platform/Info.js +0 -3
- package/dist/api/platform/Info.js.map +0 -1
- package/dist/api/platform/Platform.js +0 -3
- package/dist/api/platform/Platform.js.map +0 -1
- package/dist/api/platform/Requests.js +0 -3
- package/dist/api/platform/Requests.js.map +0 -1
- package/dist/api/platform/Storage.js +0 -3
- package/dist/api/platform/Storage.js.map +0 -1
- package/dist/api/platform/index.js +0 -26
- package/dist/api/platform/index.js.map +0 -1
- package/dist/api/subsystem/LDContextDeduplicator.js +0 -3
- package/dist/api/subsystem/LDContextDeduplicator.js.map +0 -1
- package/dist/api/subsystem/LDEventProcessor.js +0 -3
- package/dist/api/subsystem/LDEventProcessor.js.map +0 -1
- package/dist/api/subsystem/LDEventSender.js +0 -15
- package/dist/api/subsystem/LDEventSender.js.map +0 -1
- package/dist/api/subsystem/LDStreamProcessor.js +0 -3
- package/dist/api/subsystem/LDStreamProcessor.js.map +0 -1
- package/dist/api/subsystem/index.js +0 -7
- package/dist/api/subsystem/index.js.map +0 -1
- package/dist/errors.d.ts.map +0 -1
- package/dist/errors.js +0 -78
- package/dist/errors.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -31
- package/dist/index.js.map +0 -1
- package/dist/internal/context/index.js +0 -38
- package/dist/internal/context/index.js.map +0 -1
- package/dist/internal/diagnostics/DiagnosticsManager.js +0 -70
- package/dist/internal/diagnostics/DiagnosticsManager.js.map +0 -1
- package/dist/internal/diagnostics/index.js +0 -6
- package/dist/internal/diagnostics/index.js.map +0 -1
- package/dist/internal/diagnostics/types.js +0 -3
- package/dist/internal/diagnostics/types.js.map +0 -1
- package/dist/internal/evaluation/ErrorKinds.js +0 -15
- package/dist/internal/evaluation/ErrorKinds.js.map +0 -1
- package/dist/internal/evaluation/EventFactoryBase.js +0 -38
- package/dist/internal/evaluation/EventFactoryBase.js.map +0 -1
- package/dist/internal/evaluation/index.js +0 -8
- package/dist/internal/evaluation/index.js.map +0 -1
- package/dist/internal/events/ClientMessages.js +0 -14
- package/dist/internal/events/ClientMessages.js.map +0 -1
- package/dist/internal/events/EventProcessor.d.ts +0 -44
- package/dist/internal/events/EventProcessor.d.ts.map +0 -1
- package/dist/internal/events/EventProcessor.js +0 -263
- package/dist/internal/events/EventProcessor.js.map +0 -1
- package/dist/internal/events/EventSender.js +0 -77
- package/dist/internal/events/EventSender.js.map +0 -1
- package/dist/internal/events/EventSummarizer.js +0 -84
- package/dist/internal/events/EventSummarizer.js.map +0 -1
- package/dist/internal/events/InputClickEvent.js +0 -3
- package/dist/internal/events/InputClickEvent.js.map +0 -1
- package/dist/internal/events/InputCustomEvent.js +0 -22
- package/dist/internal/events/InputCustomEvent.js.map +0 -1
- package/dist/internal/events/InputEvalEvent.js +0 -38
- package/dist/internal/events/InputEvalEvent.js.map +0 -1
- package/dist/internal/events/InputEvent.js +0 -3
- package/dist/internal/events/InputEvent.js.map +0 -1
- package/dist/internal/events/InputEventBase.js +0 -11
- package/dist/internal/events/InputEventBase.js.map +0 -1
- package/dist/internal/events/InputIdentifyEvent.js +0 -12
- package/dist/internal/events/InputIdentifyEvent.js.map +0 -1
- package/dist/internal/events/InputMigrationEvent.js +0 -7
- package/dist/internal/events/InputMigrationEvent.js.map +0 -1
- package/dist/internal/events/InputPageViewEvent.js +0 -3
- package/dist/internal/events/InputPageViewEvent.js.map +0 -1
- package/dist/internal/events/LDInternalOptions.js +0 -3
- package/dist/internal/events/LDInternalOptions.js.map +0 -1
- package/dist/internal/events/LDInvalidSDKKeyError.js +0 -10
- package/dist/internal/events/LDInvalidSDKKeyError.js.map +0 -1
- package/dist/internal/events/NullEventProcessor.js +0 -11
- package/dist/internal/events/NullEventProcessor.js.map +0 -1
- package/dist/internal/events/SummaryCounter.js +0 -20
- package/dist/internal/events/SummaryCounter.js.map +0 -1
- package/dist/internal/events/guards.js +0 -20
- package/dist/internal/events/guards.js.map +0 -1
- package/dist/internal/events/index.js +0 -18
- package/dist/internal/events/index.js.map +0 -1
- package/dist/internal/events/sampling.js +0 -23
- package/dist/internal/events/sampling.js.map +0 -1
- package/dist/internal/index.d.ts.map +0 -1
- package/dist/internal/index.js +0 -22
- package/dist/internal/index.js.map +0 -1
- package/dist/internal/stream/StreamingProcessor.d.ts.map +0 -1
- package/dist/internal/stream/StreamingProcessor.js +0 -113
- package/dist/internal/stream/StreamingProcessor.js.map +0 -1
- package/dist/internal/stream/index.js +0 -6
- package/dist/internal/stream/index.js.map +0 -1
- package/dist/internal/stream/types.d.ts +0 -3
- package/dist/internal/stream/types.js +0 -3
- package/dist/internal/stream/types.js.map +0 -1
- package/dist/logging/BasicLogger.d.ts.map +0 -1
- package/dist/logging/BasicLogger.js +0 -97
- package/dist/logging/BasicLogger.js.map +0 -1
- package/dist/logging/SafeLogger.js +0 -63
- package/dist/logging/SafeLogger.js.map +0 -1
- package/dist/logging/createSafeLogger.js +0 -16
- package/dist/logging/createSafeLogger.js.map +0 -1
- package/dist/logging/format.js +0 -156
- package/dist/logging/format.js.map +0 -1
- package/dist/logging/index.js +0 -10
- package/dist/logging/index.js.map +0 -1
- package/dist/options/ApplicationTags.js +0 -55
- package/dist/options/ApplicationTags.js.map +0 -1
- package/dist/options/ClientContext.js +0 -20
- package/dist/options/ClientContext.js.map +0 -1
- package/dist/options/OptionMessages.js +0 -33
- package/dist/options/OptionMessages.js.map +0 -1
- package/dist/options/ServiceEndpoints.js +0 -77
- package/dist/options/ServiceEndpoints.js.map +0 -1
- package/dist/options/index.js +0 -15
- package/dist/options/index.js.map +0 -1
- package/dist/utils/VoidFunction.js +0 -3
- package/dist/utils/VoidFunction.js.map +0 -1
- package/dist/utils/cancelableTimedPromise.js +0 -30
- package/dist/utils/cancelableTimedPromise.js.map +0 -1
- package/dist/utils/clone.js +0 -10
- package/dist/utils/clone.js.map +0 -1
- package/dist/utils/date.js +0 -9
- package/dist/utils/date.js.map +0 -1
- package/dist/utils/debounce.js.map +0 -1
- package/dist/utils/deepCompact.js +0 -24
- package/dist/utils/deepCompact.js.map +0 -1
- package/dist/utils/fast-deep-equal/index.js +0 -90
- package/dist/utils/fast-deep-equal/index.js.map +0 -1
- package/dist/utils/http.js +0 -53
- package/dist/utils/http.js.map +0 -1
- package/dist/utils/index.js +0 -27
- package/dist/utils/index.js.map +0 -1
- package/dist/utils/isEmptyObject.js +0 -5
- package/dist/utils/isEmptyObject.js.map +0 -1
- package/dist/utils/noop.js +0 -4
- package/dist/utils/noop.js.map +0 -1
- package/dist/utils/sleep.js +0 -7
- package/dist/utils/sleep.js.map +0 -1
- package/dist/utils/timedPromise.js +0 -17
- package/dist/utils/timedPromise.js.map +0 -1
- package/dist/validators.js +0 -175
- package/dist/validators.js.map +0 -1
- /package/dist/{api → cjs/api}/context/LDContext.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/LDContext.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/context/LDContextCommon.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/LDContextCommon.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/context/LDContextMeta.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/LDContextMeta.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/context/LDMultiKindContext.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/LDMultiKindContext.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/context/LDSingleKindContext.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/LDSingleKindContext.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/context/LDUser.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/LDUser.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/context/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/context/index.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/data/LDEvaluationDetail.d.ts +0 -0
- /package/dist/{api → cjs/api}/data/LDEvaluationDetail.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/data/LDEvaluationReason.d.ts +0 -0
- /package/dist/{api → cjs/api}/data/LDEvaluationReason.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/data/LDFlagSet.d.ts +0 -0
- /package/dist/{api → cjs/api}/data/LDFlagSet.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/data/LDFlagValue.d.ts +0 -0
- /package/dist/{api → cjs/api}/data/LDFlagValue.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/data/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/data/index.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/index.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/logging/BasicLoggerOptions.d.ts +0 -0
- /package/dist/{api → cjs/api}/logging/BasicLoggerOptions.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/logging/LDLogLevel.d.ts +0 -0
- /package/dist/{api → cjs/api}/logging/LDLogLevel.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/logging/LDLogger.d.ts +0 -0
- /package/dist/{api → cjs/api}/logging/LDLogger.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/logging/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/logging/index.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/options/LDClientContext.d.ts +0 -0
- /package/dist/{api → cjs/api}/options/LDClientContext.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/options/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/options/index.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/AutoEnv.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/AutoEnv.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/Crypto.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/Crypto.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/Encoding.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/Encoding.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/EventSource.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/EventSource.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/Filesystem.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/Filesystem.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/Info.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/Info.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/Platform.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/Platform.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/Storage.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/Storage.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/platform/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/platform/index.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDContextDeduplicator.d.ts +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDContextDeduplicator.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDEventProcessor.d.ts +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDEventProcessor.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDEventSender.d.ts +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDEventSender.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDStreamProcessor.d.ts +0 -0
- /package/dist/{api → cjs/api}/subsystem/LDStreamProcessor.d.ts.map +0 -0
- /package/dist/{api → cjs/api}/subsystem/index.d.ts +0 -0
- /package/dist/{api → cjs/api}/subsystem/index.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/context/index.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/context/index.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/diagnostics/index.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/diagnostics/index.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/diagnostics/types.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/diagnostics/types.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/evaluation/ErrorKinds.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/evaluation/ErrorKinds.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/evaluation/index.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/evaluation/index.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/ClientMessages.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/EventSummarizer.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/EventSummarizer.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputClickEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputClickEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputCustomEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputCustomEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputEvalEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputEvalEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputEventBase.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputEventBase.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputIdentifyEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputIdentifyEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputMigrationEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputMigrationEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/InputPageViewEvent.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/InputPageViewEvent.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/LDInternalOptions.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/LDInternalOptions.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/LDInvalidSDKKeyError.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/LDInvalidSDKKeyError.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/NullEventProcessor.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/NullEventProcessor.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/SummaryCounter.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/SummaryCounter.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/guards.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/guards.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/index.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/index.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/events/sampling.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/events/sampling.d.ts.map +0 -0
- /package/dist/{internal → cjs/internal}/stream/index.d.ts +0 -0
- /package/dist/{internal → cjs/internal}/stream/index.d.ts.map +0 -0
- /package/dist/{logging → cjs/logging}/createSafeLogger.d.ts +0 -0
- /package/dist/{logging → cjs/logging}/createSafeLogger.d.ts.map +0 -0
- /package/dist/{logging → cjs/logging}/format.d.ts +0 -0
- /package/dist/{logging → cjs/logging}/format.d.ts.map +0 -0
- /package/dist/{logging → cjs/logging}/index.d.ts +0 -0
- /package/dist/{logging → cjs/logging}/index.d.ts.map +0 -0
- /package/dist/{options → cjs/options}/ApplicationTags.d.ts +0 -0
- /package/dist/{options → cjs/options}/ApplicationTags.d.ts.map +0 -0
- /package/dist/{options → cjs/options}/ClientContext.d.ts +0 -0
- /package/dist/{options → cjs/options}/ClientContext.d.ts.map +0 -0
- /package/dist/{options → cjs/options}/OptionMessages.d.ts +0 -0
- /package/dist/{options → cjs/options}/OptionMessages.d.ts.map +0 -0
- /package/dist/{options → cjs/options}/ServiceEndpoints.d.ts +0 -0
- /package/dist/{options → cjs/options}/index.d.ts +0 -0
- /package/dist/{options → cjs/options}/index.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/VoidFunction.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/VoidFunction.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/cancelableTimedPromise.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/cancelableTimedPromise.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/clone.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/clone.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/date.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/date.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/debounce.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/debounce.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/deepCompact.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/deepCompact.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/fast-deep-equal/index.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/fast-deep-equal/index.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/http.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/http.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/index.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/index.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/isEmptyObject.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/isEmptyObject.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/noop.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/noop.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/sleep.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/sleep.d.ts.map +0 -0
- /package/dist/{utils → cjs/utils}/timedPromise.d.ts +0 -0
- /package/dist/{utils → cjs/utils}/timedPromise.d.ts.map +0 -0
|
@@ -0,0 +1,2432 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts a literal to a ref string.
|
|
3
|
+
* @param value
|
|
4
|
+
* @returns An escaped literal which can be used as a ref.
|
|
5
|
+
*/
|
|
6
|
+
function toRefString(value) {
|
|
7
|
+
return `/${value.replace(/~/g, '~0').replace(/\//g, '~1')}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Produce a literal from a ref component.
|
|
11
|
+
* @param ref
|
|
12
|
+
* @returns A literal version of the ref.
|
|
13
|
+
*/
|
|
14
|
+
function unescape(ref) {
|
|
15
|
+
return ref.indexOf('~') ? ref.replace(/~1/g, '/').replace(/~0/g, '~') : ref;
|
|
16
|
+
}
|
|
17
|
+
function getComponents(reference) {
|
|
18
|
+
const referenceWithoutPrefix = reference.startsWith('/') ? reference.substring(1) : reference;
|
|
19
|
+
return referenceWithoutPrefix.split('/').map((component) => unescape(component));
|
|
20
|
+
}
|
|
21
|
+
function isLiteral(reference) {
|
|
22
|
+
return !reference.startsWith('/');
|
|
23
|
+
}
|
|
24
|
+
function validate(reference) {
|
|
25
|
+
return !reference.match(/\/\/|(^\/.*~[^0|^1])|~$/);
|
|
26
|
+
}
|
|
27
|
+
class AttributeReference {
|
|
28
|
+
/**
|
|
29
|
+
* Take an attribute reference string, or literal string, and produce
|
|
30
|
+
* an attribute reference.
|
|
31
|
+
*
|
|
32
|
+
* Legacy user objects would have been created with names not
|
|
33
|
+
* references. So, in that case, we need to use them as a component
|
|
34
|
+
* without escaping them.
|
|
35
|
+
*
|
|
36
|
+
* e.g. A user could contain a custom attribute of `/a` which would
|
|
37
|
+
* become the literal `a` if treated as a reference. Which would cause
|
|
38
|
+
* it to no longer be redacted.
|
|
39
|
+
* @param refOrLiteral The attribute reference string or literal string.
|
|
40
|
+
* @param literal it true the value should be treated as a literal.
|
|
41
|
+
*/
|
|
42
|
+
constructor(refOrLiteral, literal = false) {
|
|
43
|
+
if (!literal) {
|
|
44
|
+
this.redactionName = refOrLiteral;
|
|
45
|
+
if (refOrLiteral === '' || refOrLiteral === '/' || !validate(refOrLiteral)) {
|
|
46
|
+
this.isValid = false;
|
|
47
|
+
this._components = [];
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (isLiteral(refOrLiteral)) {
|
|
51
|
+
this._components = [refOrLiteral];
|
|
52
|
+
}
|
|
53
|
+
else if (refOrLiteral.indexOf('/', 1) < 0) {
|
|
54
|
+
this._components = [unescape(refOrLiteral.slice(1))];
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this._components = getComponents(refOrLiteral);
|
|
58
|
+
}
|
|
59
|
+
// The items inside of '_meta' are not intended to be addressable.
|
|
60
|
+
// Excluding it as a valid reference means that we can make it non-addressable
|
|
61
|
+
// without having to copy all the attributes out of the context object
|
|
62
|
+
// provided by the user.
|
|
63
|
+
if (this._components[0] === '_meta') {
|
|
64
|
+
this.isValid = false;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
this.isValid = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
const literalVal = refOrLiteral;
|
|
72
|
+
this._components = [literalVal];
|
|
73
|
+
this.isValid = literalVal !== '';
|
|
74
|
+
// Literals which start with '/' need escaped to prevent ambiguity.
|
|
75
|
+
this.redactionName = literalVal.startsWith('/') ? toRefString(literalVal) : literalVal;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
get(target) {
|
|
79
|
+
const { _components: components, isValid } = this;
|
|
80
|
+
if (!isValid) {
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
let current = target;
|
|
84
|
+
// This doesn't use a range based for loops, because those use generators.
|
|
85
|
+
// See `no-restricted-syntax`.
|
|
86
|
+
// It also doesn't use a collection method because this logic is more
|
|
87
|
+
// straightforward with a loop.
|
|
88
|
+
for (let index = 0; index < components.length; index += 1) {
|
|
89
|
+
const component = components[index];
|
|
90
|
+
if (current !== null &&
|
|
91
|
+
current !== undefined &&
|
|
92
|
+
// See https://eslint.org/docs/rules/no-prototype-builtins
|
|
93
|
+
Object.prototype.hasOwnProperty.call(current, component) &&
|
|
94
|
+
typeof current === 'object' &&
|
|
95
|
+
// We do not want to allow indexing into an array.
|
|
96
|
+
!Array.isArray(current)) {
|
|
97
|
+
current = current[component];
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return current;
|
|
104
|
+
}
|
|
105
|
+
getComponent(depth) {
|
|
106
|
+
return this._components[depth];
|
|
107
|
+
}
|
|
108
|
+
get depth() {
|
|
109
|
+
return this._components.length;
|
|
110
|
+
}
|
|
111
|
+
get isKind() {
|
|
112
|
+
return this._components.length === 1 && this._components[0] === 'kind';
|
|
113
|
+
}
|
|
114
|
+
compare(other) {
|
|
115
|
+
return (this.depth === other.depth &&
|
|
116
|
+
this._components.every((value, index) => value === other.getComponent(index)));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* For use as invalid references when deserializing Flag/Segment data.
|
|
121
|
+
*/
|
|
122
|
+
AttributeReference.InvalidReference = new AttributeReference('');
|
|
123
|
+
|
|
124
|
+
/* eslint-disable class-methods-use-this */
|
|
125
|
+
/* eslint-disable max-classes-per-file */
|
|
126
|
+
/**
|
|
127
|
+
* Validate a factory or instance.
|
|
128
|
+
*/
|
|
129
|
+
class FactoryOrInstance {
|
|
130
|
+
is(factoryOrInstance) {
|
|
131
|
+
if (Array.isArray(factoryOrInstance)) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const anyFactory = factoryOrInstance;
|
|
135
|
+
const typeOfFactory = typeof anyFactory;
|
|
136
|
+
return typeOfFactory === 'function' || typeOfFactory === 'object';
|
|
137
|
+
}
|
|
138
|
+
getType() {
|
|
139
|
+
return 'factory method or object';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Validate a basic type.
|
|
144
|
+
*/
|
|
145
|
+
class Type {
|
|
146
|
+
constructor(typeName, example) {
|
|
147
|
+
this._typeName = typeName;
|
|
148
|
+
this.typeOf = typeof example;
|
|
149
|
+
}
|
|
150
|
+
is(u) {
|
|
151
|
+
if (Array.isArray(u)) {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return typeof u === this.typeOf;
|
|
155
|
+
}
|
|
156
|
+
getType() {
|
|
157
|
+
return this._typeName;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Validate an array of the specified type.
|
|
162
|
+
*
|
|
163
|
+
* This does not validate instances of types. All class instances
|
|
164
|
+
* of classes will simply objects.
|
|
165
|
+
*/
|
|
166
|
+
class TypeArray {
|
|
167
|
+
constructor(typeName, example) {
|
|
168
|
+
this._typeName = typeName;
|
|
169
|
+
this.typeOf = typeof example;
|
|
170
|
+
}
|
|
171
|
+
is(u) {
|
|
172
|
+
if (Array.isArray(u)) {
|
|
173
|
+
if (u.length > 0) {
|
|
174
|
+
return u.every((val) => typeof val === this.typeOf);
|
|
175
|
+
}
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
getType() {
|
|
181
|
+
return this._typeName;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Validate a value is a number and is greater or eval than a minimum.
|
|
186
|
+
*/
|
|
187
|
+
class NumberWithMinimum extends Type {
|
|
188
|
+
constructor(min) {
|
|
189
|
+
super(`number with minimum value of ${min}`, 0);
|
|
190
|
+
this.min = min;
|
|
191
|
+
}
|
|
192
|
+
is(u) {
|
|
193
|
+
return typeof u === this.typeOf && u >= this.min;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Validate a value is a string and it matches the given expression.
|
|
198
|
+
*/
|
|
199
|
+
class StringMatchingRegex extends Type {
|
|
200
|
+
constructor(expression) {
|
|
201
|
+
super(`string matching ${expression}`, '');
|
|
202
|
+
this.expression = expression;
|
|
203
|
+
}
|
|
204
|
+
is(u) {
|
|
205
|
+
return typeof u === 'string' && !!u.match(this.expression);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Validate a value is a function.
|
|
210
|
+
*/
|
|
211
|
+
class Function {
|
|
212
|
+
is(u) {
|
|
213
|
+
// We cannot inspect the parameters and there isn't really
|
|
214
|
+
// a generic function type we can instantiate.
|
|
215
|
+
// So the type guard is here just to make TS comfortable
|
|
216
|
+
// calling something after using this guard.
|
|
217
|
+
return typeof u === 'function';
|
|
218
|
+
}
|
|
219
|
+
getType() {
|
|
220
|
+
return 'function';
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
class NullableBoolean {
|
|
224
|
+
is(u) {
|
|
225
|
+
return typeof u === 'boolean' || typeof u === 'undefined' || u === null;
|
|
226
|
+
}
|
|
227
|
+
getType() {
|
|
228
|
+
return 'boolean | undefined | null';
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// Our reference SDK, Go, parses date/time strings with the time.RFC3339Nano format.
|
|
232
|
+
// This regex should match strings that are valid in that format, and no others.
|
|
233
|
+
// Acceptable:
|
|
234
|
+
// 2019-10-31T23:59:59Z, 2019-10-31T23:59:59.100Z,
|
|
235
|
+
// 2019-10-31T23:59:59-07, 2019-10-31T23:59:59-07:00, etc.
|
|
236
|
+
// Unacceptable: no "T", no time zone designation
|
|
237
|
+
const DATE_REGEX = /^\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\d(\.\d\d*)?(Z|[-+]\d\d(:\d\d)?)/;
|
|
238
|
+
/**
|
|
239
|
+
* Validate a value is a date. Values which are numbers are treated as dates and any string
|
|
240
|
+
* which if compliant with `time.RFC3339Nano` is a date.
|
|
241
|
+
*/
|
|
242
|
+
class DateValidator {
|
|
243
|
+
is(u) {
|
|
244
|
+
return typeof u === 'number' || (typeof u === 'string' && DATE_REGEX.test(u));
|
|
245
|
+
}
|
|
246
|
+
getType() {
|
|
247
|
+
return 'date';
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Validates that a string is a valid kind.
|
|
252
|
+
*/
|
|
253
|
+
class KindValidator extends StringMatchingRegex {
|
|
254
|
+
constructor() {
|
|
255
|
+
super(/^(\w|\.|-)+$/);
|
|
256
|
+
}
|
|
257
|
+
is(u) {
|
|
258
|
+
return super.is(u) && u !== 'kind';
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* A set of standard type validators.
|
|
263
|
+
*/
|
|
264
|
+
class TypeValidators {
|
|
265
|
+
static createTypeArray(typeName, example) {
|
|
266
|
+
return new TypeArray(typeName, example);
|
|
267
|
+
}
|
|
268
|
+
static numberWithMin(min) {
|
|
269
|
+
return new NumberWithMinimum(min);
|
|
270
|
+
}
|
|
271
|
+
static stringMatchingRegex(expression) {
|
|
272
|
+
return new StringMatchingRegex(expression);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
TypeValidators.String = new Type('string', '');
|
|
276
|
+
TypeValidators.Number = new Type('number', 0);
|
|
277
|
+
TypeValidators.ObjectOrFactory = new FactoryOrInstance();
|
|
278
|
+
TypeValidators.Object = new Type('object', {});
|
|
279
|
+
TypeValidators.StringArray = new TypeArray('string[]', '');
|
|
280
|
+
TypeValidators.Boolean = new Type('boolean', true);
|
|
281
|
+
TypeValidators.Function = new Function();
|
|
282
|
+
TypeValidators.Date = new DateValidator();
|
|
283
|
+
TypeValidators.Kind = new KindValidator();
|
|
284
|
+
TypeValidators.NullableBoolean = new NullableBoolean();
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Check if a context is a single kind context.
|
|
288
|
+
* @param context
|
|
289
|
+
* @returns true if the context is a single kind context.
|
|
290
|
+
*/
|
|
291
|
+
function isSingleKind(context) {
|
|
292
|
+
if ('kind' in context) {
|
|
293
|
+
return TypeValidators.String.is(context.kind) && context.kind !== 'multi';
|
|
294
|
+
}
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Check if a context is a multi-kind context.
|
|
299
|
+
* @param context
|
|
300
|
+
* @returns true if it is a multi-kind context.
|
|
301
|
+
*/
|
|
302
|
+
function isMultiKind(context) {
|
|
303
|
+
if ('kind' in context) {
|
|
304
|
+
return TypeValidators.String.is(context.kind) && context.kind === 'multi';
|
|
305
|
+
}
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Check if a context is a legacy user context.
|
|
310
|
+
* @param context
|
|
311
|
+
* @returns true if it is a legacy user context.
|
|
312
|
+
*/
|
|
313
|
+
function isLegacyUser(context) {
|
|
314
|
+
return !('kind' in context) || context.kind === null || context.kind === undefined;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// The general strategy for the context is to transform the passed in context
|
|
318
|
+
// as little as possible. We do convert the legacy users to a single kind
|
|
319
|
+
// context, but we do not translate all passed contexts into a rigid structure.
|
|
320
|
+
// The context will have to be copied for events, but we want to avoid any
|
|
321
|
+
// copying that we can.
|
|
322
|
+
// So we validate that the information we are given is correct, and then we
|
|
323
|
+
// just proxy calls with a nicely typed interface.
|
|
324
|
+
// This is to reduce work on the hot-path. Later, for event processing, deeper
|
|
325
|
+
// cloning of the context will be done.
|
|
326
|
+
// When no kind is specified, then this kind will be used.
|
|
327
|
+
const DEFAULT_KIND = 'user';
|
|
328
|
+
// The API allows for calling with an `LDContext` which is
|
|
329
|
+
// `LDUser | LDSingleKindContext | LDMultiKindContext`. When ingesting a context
|
|
330
|
+
// first the type must be determined to allow us to put it into a consistent type.
|
|
331
|
+
/**
|
|
332
|
+
* The partial URL encoding is needed because : is a valid character in context keys.
|
|
333
|
+
*
|
|
334
|
+
* Partial encoding is the replacement of all colon (:) characters with the URL
|
|
335
|
+
* encoded equivalent (%3A) and all percent (%) characters with the URL encoded
|
|
336
|
+
* equivalent (%25).
|
|
337
|
+
* @param key The key to encode.
|
|
338
|
+
* @returns Partially URL encoded key.
|
|
339
|
+
*/
|
|
340
|
+
function encodeKey(key) {
|
|
341
|
+
if (key.includes('%') || key.includes(':')) {
|
|
342
|
+
return key.replace(/%/g, '%25').replace(/:/g, '%3A');
|
|
343
|
+
}
|
|
344
|
+
return key;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Check if the given value is a LDContextCommon.
|
|
348
|
+
* @param kindOrContext
|
|
349
|
+
* @returns true if it is an LDContextCommon
|
|
350
|
+
*
|
|
351
|
+
* Due to a limitation in the expressiveness of these highly polymorphic types any field
|
|
352
|
+
* in a multi-kind context can either be a context or 'kind'. So we need to re-assure
|
|
353
|
+
* the compiler that it isn't the word multi.
|
|
354
|
+
*
|
|
355
|
+
* Because we do not allow top level values in a multi-kind context we can validate
|
|
356
|
+
* that as well.
|
|
357
|
+
*/
|
|
358
|
+
function isContextCommon(kindOrContext) {
|
|
359
|
+
return kindOrContext && TypeValidators.Object.is(kindOrContext);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Validate a context kind.
|
|
363
|
+
* @param kind
|
|
364
|
+
* @returns true if the kind is valid.
|
|
365
|
+
*/
|
|
366
|
+
function validKind(kind) {
|
|
367
|
+
return TypeValidators.Kind.is(kind);
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Validate a context key.
|
|
371
|
+
* @param key
|
|
372
|
+
* @returns true if the key is valid.
|
|
373
|
+
*/
|
|
374
|
+
function validKey(key) {
|
|
375
|
+
return TypeValidators.String.is(key) && key !== '';
|
|
376
|
+
}
|
|
377
|
+
function processPrivateAttributes(privateAttributes, literals = false) {
|
|
378
|
+
if (privateAttributes) {
|
|
379
|
+
return privateAttributes.map((privateAttribute) => new AttributeReference(privateAttribute, literals));
|
|
380
|
+
}
|
|
381
|
+
return [];
|
|
382
|
+
}
|
|
383
|
+
function defined(value) {
|
|
384
|
+
return value !== null && value !== undefined;
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Convert a legacy user to a single kind context.
|
|
388
|
+
* @param user
|
|
389
|
+
* @returns A single kind context.
|
|
390
|
+
*/
|
|
391
|
+
function legacyToSingleKind(user) {
|
|
392
|
+
const singleKindContext = {
|
|
393
|
+
// Key was coerced to a string for eval and events, so we can do that up-front.
|
|
394
|
+
...(user.custom || []),
|
|
395
|
+
kind: 'user',
|
|
396
|
+
key: String(user.key),
|
|
397
|
+
};
|
|
398
|
+
// For legacy users we never established a difference between null
|
|
399
|
+
// and undefined for inputs. Because anonymous can be used in evaluations
|
|
400
|
+
// we would want it to not possibly match true/false unless defined.
|
|
401
|
+
// Which is different than coercing a null/undefined anonymous as `false`.
|
|
402
|
+
if (defined(user.anonymous)) {
|
|
403
|
+
const anonymous = !!user.anonymous;
|
|
404
|
+
delete singleKindContext.anonymous;
|
|
405
|
+
singleKindContext.anonymous = anonymous;
|
|
406
|
+
}
|
|
407
|
+
if (user.name !== null && user.name !== undefined) {
|
|
408
|
+
singleKindContext.name = user.name;
|
|
409
|
+
}
|
|
410
|
+
if (user.ip !== null && user.ip !== undefined) {
|
|
411
|
+
singleKindContext.ip = user.ip;
|
|
412
|
+
}
|
|
413
|
+
if (user.firstName !== null && user.firstName !== undefined) {
|
|
414
|
+
singleKindContext.firstName = user.firstName;
|
|
415
|
+
}
|
|
416
|
+
if (user.lastName !== null && user.lastName !== undefined) {
|
|
417
|
+
singleKindContext.lastName = user.lastName;
|
|
418
|
+
}
|
|
419
|
+
if (user.email !== null && user.email !== undefined) {
|
|
420
|
+
singleKindContext.email = user.email;
|
|
421
|
+
}
|
|
422
|
+
if (user.avatar !== null && user.avatar !== undefined) {
|
|
423
|
+
singleKindContext.avatar = user.avatar;
|
|
424
|
+
}
|
|
425
|
+
if (user.country !== null && user.country !== undefined) {
|
|
426
|
+
singleKindContext.country = user.country;
|
|
427
|
+
}
|
|
428
|
+
if (user.privateAttributeNames !== null && user.privateAttributeNames !== undefined) {
|
|
429
|
+
singleKindContext._meta = {
|
|
430
|
+
privateAttributes: user.privateAttributeNames,
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
// We are not pulling private attributes over because we will serialize
|
|
434
|
+
// those from attribute references for events.
|
|
435
|
+
return singleKindContext;
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Container for a context/contexts. Because contexts come from external code
|
|
439
|
+
* they must be thoroughly validated and then formed to comply with
|
|
440
|
+
* the type system.
|
|
441
|
+
*/
|
|
442
|
+
class Context {
|
|
443
|
+
/**
|
|
444
|
+
* Contexts should be created using the static factory method {@link Context.fromLDContext}.
|
|
445
|
+
* @param kind The kind of the context.
|
|
446
|
+
*
|
|
447
|
+
* The factory methods are static functions within the class because they access private
|
|
448
|
+
* implementation details, so they cannot be free functions.
|
|
449
|
+
*/
|
|
450
|
+
constructor(valid, kind, message) {
|
|
451
|
+
this._isMulti = false;
|
|
452
|
+
this._isUser = false;
|
|
453
|
+
this._wasLegacy = false;
|
|
454
|
+
this._contexts = {};
|
|
455
|
+
this.kind = kind;
|
|
456
|
+
this.valid = valid;
|
|
457
|
+
this.message = message;
|
|
458
|
+
}
|
|
459
|
+
static _contextForError(kind, message) {
|
|
460
|
+
return new Context(false, kind, message);
|
|
461
|
+
}
|
|
462
|
+
static _getValueFromContext(reference, context) {
|
|
463
|
+
if (!context || !reference.isValid) {
|
|
464
|
+
return undefined;
|
|
465
|
+
}
|
|
466
|
+
if (reference.depth === 1 && reference.getComponent(0) === 'anonymous') {
|
|
467
|
+
return !!context?.anonymous;
|
|
468
|
+
}
|
|
469
|
+
return reference.get(context);
|
|
470
|
+
}
|
|
471
|
+
_contextForKind(kind) {
|
|
472
|
+
if (this._isMulti) {
|
|
473
|
+
return this._contexts[kind];
|
|
474
|
+
}
|
|
475
|
+
if (this.kind === kind) {
|
|
476
|
+
return this._context;
|
|
477
|
+
}
|
|
478
|
+
return undefined;
|
|
479
|
+
}
|
|
480
|
+
static _fromMultiKindContext(context) {
|
|
481
|
+
const kinds = Object.keys(context).filter((key) => key !== 'kind');
|
|
482
|
+
const kindsValid = kinds.every(validKind);
|
|
483
|
+
if (!kinds.length) {
|
|
484
|
+
return Context._contextForError('multi', 'A multi-kind context must contain at least one kind');
|
|
485
|
+
}
|
|
486
|
+
if (!kindsValid) {
|
|
487
|
+
return Context._contextForError('multi', 'Context contains invalid kinds');
|
|
488
|
+
}
|
|
489
|
+
const privateAttributes = {};
|
|
490
|
+
let contextsAreObjects = true;
|
|
491
|
+
const contexts = kinds.reduce((acc, kind) => {
|
|
492
|
+
const singleContext = context[kind];
|
|
493
|
+
if (isContextCommon(singleContext)) {
|
|
494
|
+
acc[kind] = singleContext;
|
|
495
|
+
privateAttributes[kind] = processPrivateAttributes(singleContext._meta?.privateAttributes);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
// No early break isn't the most efficient, but it is an error condition.
|
|
499
|
+
contextsAreObjects = false;
|
|
500
|
+
}
|
|
501
|
+
return acc;
|
|
502
|
+
}, {});
|
|
503
|
+
if (!contextsAreObjects) {
|
|
504
|
+
return Context._contextForError('multi', 'Context contained contexts that were not objects');
|
|
505
|
+
}
|
|
506
|
+
if (!Object.values(contexts).every((part) => validKey(part.key))) {
|
|
507
|
+
return Context._contextForError('multi', 'Context contained invalid keys');
|
|
508
|
+
}
|
|
509
|
+
// There was only a single kind in the multi-kind context.
|
|
510
|
+
// So we can just translate this to a single-kind context.
|
|
511
|
+
if (kinds.length === 1) {
|
|
512
|
+
const kind = kinds[0];
|
|
513
|
+
const created = new Context(true, kind);
|
|
514
|
+
created._context = { ...contexts[kind], kind };
|
|
515
|
+
created._privateAttributeReferences = privateAttributes;
|
|
516
|
+
created._isUser = kind === 'user';
|
|
517
|
+
return created;
|
|
518
|
+
}
|
|
519
|
+
const created = new Context(true, context.kind);
|
|
520
|
+
created._contexts = contexts;
|
|
521
|
+
created._privateAttributeReferences = privateAttributes;
|
|
522
|
+
created._isMulti = true;
|
|
523
|
+
return created;
|
|
524
|
+
}
|
|
525
|
+
static _fromSingleKindContext(context) {
|
|
526
|
+
const { key, kind } = context;
|
|
527
|
+
const kindValid = validKind(kind);
|
|
528
|
+
const keyValid = validKey(key);
|
|
529
|
+
if (!kindValid) {
|
|
530
|
+
return Context._contextForError(kind ?? 'unknown', 'The kind was not valid for the context');
|
|
531
|
+
}
|
|
532
|
+
if (!keyValid) {
|
|
533
|
+
return Context._contextForError(kind, 'The key for the context was not valid');
|
|
534
|
+
}
|
|
535
|
+
// The JSON interfaces uses dangling _.
|
|
536
|
+
// eslint-disable-next-line no-underscore-dangle
|
|
537
|
+
const privateAttributeReferences = processPrivateAttributes(context._meta?.privateAttributes);
|
|
538
|
+
const created = new Context(true, kind);
|
|
539
|
+
created._isUser = kind === 'user';
|
|
540
|
+
created._context = context;
|
|
541
|
+
created._privateAttributeReferences = {
|
|
542
|
+
[kind]: privateAttributeReferences,
|
|
543
|
+
};
|
|
544
|
+
return created;
|
|
545
|
+
}
|
|
546
|
+
static _fromLegacyUser(context) {
|
|
547
|
+
const keyValid = context.key !== undefined && context.key !== null;
|
|
548
|
+
// For legacy users we allow empty keys.
|
|
549
|
+
if (!keyValid) {
|
|
550
|
+
return Context._contextForError('user', 'The key for the context was not valid');
|
|
551
|
+
}
|
|
552
|
+
const created = new Context(true, 'user');
|
|
553
|
+
created._isUser = true;
|
|
554
|
+
created._wasLegacy = true;
|
|
555
|
+
created._context = legacyToSingleKind(context);
|
|
556
|
+
created._privateAttributeReferences = {
|
|
557
|
+
user: processPrivateAttributes(context.privateAttributeNames, true),
|
|
558
|
+
};
|
|
559
|
+
return created;
|
|
560
|
+
}
|
|
561
|
+
/**
|
|
562
|
+
* Attempt to create a {@link Context} from an {@link LDContext}.
|
|
563
|
+
* @param context The input context to create a Context from.
|
|
564
|
+
* @returns a {@link Context}, if the context was not valid, then the returned contexts `valid`
|
|
565
|
+
* property will be false.
|
|
566
|
+
*/
|
|
567
|
+
static fromLDContext(context) {
|
|
568
|
+
if (!context) {
|
|
569
|
+
return Context._contextForError('unknown', 'No context specified. Returning default value');
|
|
570
|
+
}
|
|
571
|
+
if (isSingleKind(context)) {
|
|
572
|
+
return Context._fromSingleKindContext(context);
|
|
573
|
+
}
|
|
574
|
+
if (isMultiKind(context)) {
|
|
575
|
+
return Context._fromMultiKindContext(context);
|
|
576
|
+
}
|
|
577
|
+
if (isLegacyUser(context)) {
|
|
578
|
+
return Context._fromLegacyUser(context);
|
|
579
|
+
}
|
|
580
|
+
return Context._contextForError('unknown', 'Context was not of a valid kind');
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Creates a {@link LDContext} from a {@link Context}.
|
|
584
|
+
* @param context to be converted
|
|
585
|
+
* @returns an {@link LDContext} if input was valid, otherwise undefined
|
|
586
|
+
*/
|
|
587
|
+
static toLDContext(context) {
|
|
588
|
+
if (!context.valid) {
|
|
589
|
+
return undefined;
|
|
590
|
+
}
|
|
591
|
+
const contexts = context.getContexts();
|
|
592
|
+
if (!context._isMulti) {
|
|
593
|
+
return contexts[0][1];
|
|
594
|
+
}
|
|
595
|
+
const result = {
|
|
596
|
+
kind: 'multi',
|
|
597
|
+
};
|
|
598
|
+
contexts.forEach((kindAndContext) => {
|
|
599
|
+
const kind = kindAndContext[0];
|
|
600
|
+
const nestedContext = kindAndContext[1];
|
|
601
|
+
result[kind] = nestedContext;
|
|
602
|
+
});
|
|
603
|
+
return result;
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Attempt to get a value for the given context kind using the given reference.
|
|
607
|
+
* @param reference The reference to the value to get.
|
|
608
|
+
* @param kind The kind of the context to get the value for.
|
|
609
|
+
* @returns a value or `undefined` if one is not found.
|
|
610
|
+
*/
|
|
611
|
+
valueForKind(reference, kind = DEFAULT_KIND) {
|
|
612
|
+
if (reference.isKind) {
|
|
613
|
+
return this.kinds;
|
|
614
|
+
}
|
|
615
|
+
return Context._getValueFromContext(reference, this._contextForKind(kind));
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Attempt to get a key for the specified kind.
|
|
619
|
+
* @param kind The kind to get a key for.
|
|
620
|
+
* @returns The key for the specified kind, or undefined.
|
|
621
|
+
*/
|
|
622
|
+
key(kind = DEFAULT_KIND) {
|
|
623
|
+
return this._contextForKind(kind)?.key;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* True if this is a multi-kind context.
|
|
627
|
+
*/
|
|
628
|
+
get isMultiKind() {
|
|
629
|
+
return this._isMulti;
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Get the canonical key for this context.
|
|
633
|
+
*/
|
|
634
|
+
get canonicalKey() {
|
|
635
|
+
if (this._isUser) {
|
|
636
|
+
return this._context.key;
|
|
637
|
+
}
|
|
638
|
+
if (this._isMulti) {
|
|
639
|
+
return Object.keys(this._contexts)
|
|
640
|
+
.sort()
|
|
641
|
+
.map((key) => `${key}:${encodeKey(this._contexts[key].key)}`)
|
|
642
|
+
.join(':');
|
|
643
|
+
}
|
|
644
|
+
return `${this.kind}:${encodeKey(this._context.key)}`;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Get the kinds of this context.
|
|
648
|
+
*/
|
|
649
|
+
get kinds() {
|
|
650
|
+
if (this._isMulti) {
|
|
651
|
+
return Object.keys(this._contexts);
|
|
652
|
+
}
|
|
653
|
+
return [this.kind];
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Get the kinds, and their keys, for this context.
|
|
657
|
+
*/
|
|
658
|
+
get kindsAndKeys() {
|
|
659
|
+
if (this._isMulti) {
|
|
660
|
+
return Object.entries(this._contexts).reduce((acc, [kind, context]) => {
|
|
661
|
+
acc[kind] = context.key;
|
|
662
|
+
return acc;
|
|
663
|
+
}, {});
|
|
664
|
+
}
|
|
665
|
+
return { [this.kind]: this._context.key };
|
|
666
|
+
}
|
|
667
|
+
/**
|
|
668
|
+
* Get the attribute references.
|
|
669
|
+
*
|
|
670
|
+
* @param kind
|
|
671
|
+
*/
|
|
672
|
+
privateAttributes(kind) {
|
|
673
|
+
return this._privateAttributeReferences?.[kind] || [];
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Get the underlying context objects from this context.
|
|
677
|
+
*
|
|
678
|
+
* This method is intended to be used in event generation.
|
|
679
|
+
*
|
|
680
|
+
* The returned objects should not be modified.
|
|
681
|
+
*/
|
|
682
|
+
getContexts() {
|
|
683
|
+
if (this._isMulti) {
|
|
684
|
+
return Object.entries(this._contexts);
|
|
685
|
+
}
|
|
686
|
+
return [[this.kind, this._context]];
|
|
687
|
+
}
|
|
688
|
+
get legacy() {
|
|
689
|
+
return this._wasLegacy;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
Context.UserKind = DEFAULT_KIND;
|
|
693
|
+
|
|
694
|
+
// _meta is part of the specification.
|
|
695
|
+
// These attributes cannot be removed via a private attribute.
|
|
696
|
+
const protectedAttributes = ['key', 'kind', '_meta', 'anonymous'].map((str) => new AttributeReference(str, true));
|
|
697
|
+
// Attributes that should be stringified for legacy users.
|
|
698
|
+
const legacyTopLevelCopyAttributes = [
|
|
699
|
+
'name',
|
|
700
|
+
'ip',
|
|
701
|
+
'firstName',
|
|
702
|
+
'lastName',
|
|
703
|
+
'email',
|
|
704
|
+
'avatar',
|
|
705
|
+
'country',
|
|
706
|
+
];
|
|
707
|
+
function compare(a, b) {
|
|
708
|
+
return a.depth === b.length && b.every((value, index) => value === a.getComponent(index));
|
|
709
|
+
}
|
|
710
|
+
function cloneWithRedactions(target, references) {
|
|
711
|
+
const stack = [];
|
|
712
|
+
const cloned = {};
|
|
713
|
+
const excluded = [];
|
|
714
|
+
stack.push(...Object.keys(target).map((key) => ({
|
|
715
|
+
key,
|
|
716
|
+
ptr: [key],
|
|
717
|
+
source: target,
|
|
718
|
+
parent: cloned,
|
|
719
|
+
visited: [target],
|
|
720
|
+
})));
|
|
721
|
+
while (stack.length) {
|
|
722
|
+
const item = stack.pop();
|
|
723
|
+
const redactRef = references.find((ref) => compare(ref, item.ptr));
|
|
724
|
+
if (!redactRef) {
|
|
725
|
+
const value = item.source[item.key];
|
|
726
|
+
// Handle null because it overlaps with object, which we will want to handle later.
|
|
727
|
+
if (value === null) {
|
|
728
|
+
item.parent[item.key] = value;
|
|
729
|
+
}
|
|
730
|
+
else if (Array.isArray(value)) {
|
|
731
|
+
item.parent[item.key] = [...value];
|
|
732
|
+
}
|
|
733
|
+
else if (typeof value === 'object') {
|
|
734
|
+
// Arrays and null must already be handled.
|
|
735
|
+
// Prevent cycles by not visiting the same object
|
|
736
|
+
// with in the same branch. Different branches
|
|
737
|
+
// may contain the same object.
|
|
738
|
+
//
|
|
739
|
+
// Same object visited twice in different branches.
|
|
740
|
+
// A -> B -> D
|
|
741
|
+
// -> C -> D
|
|
742
|
+
// This is fine, which is why it doesn't just check if the object
|
|
743
|
+
// was visited ever.
|
|
744
|
+
if (!item.visited.includes(value)) {
|
|
745
|
+
item.parent[item.key] = {};
|
|
746
|
+
stack.push(...Object.keys(value).map((key) => ({
|
|
747
|
+
key,
|
|
748
|
+
ptr: [...item.ptr, key],
|
|
749
|
+
source: value,
|
|
750
|
+
parent: item.parent[item.key],
|
|
751
|
+
visited: [...item.visited, value],
|
|
752
|
+
})));
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
else {
|
|
756
|
+
item.parent[item.key] = value;
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
excluded.push(redactRef.redactionName);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
return { cloned, excluded: excluded.sort() };
|
|
764
|
+
}
|
|
765
|
+
class ContextFilter {
|
|
766
|
+
constructor(_allAttributesPrivate, _privateAttributes) {
|
|
767
|
+
this._allAttributesPrivate = _allAttributesPrivate;
|
|
768
|
+
this._privateAttributes = _privateAttributes;
|
|
769
|
+
}
|
|
770
|
+
filter(context, redactAnonymousAttributes = false) {
|
|
771
|
+
const contexts = context.getContexts();
|
|
772
|
+
if (contexts.length === 1) {
|
|
773
|
+
return this._filterSingleKind(context, contexts[0][1], contexts[0][0], redactAnonymousAttributes);
|
|
774
|
+
}
|
|
775
|
+
const filteredMulti = {
|
|
776
|
+
kind: 'multi',
|
|
777
|
+
};
|
|
778
|
+
contexts.forEach(([kind, single]) => {
|
|
779
|
+
filteredMulti[kind] = this._filterSingleKind(context, single, kind, redactAnonymousAttributes);
|
|
780
|
+
});
|
|
781
|
+
return filteredMulti;
|
|
782
|
+
}
|
|
783
|
+
_getAttributesToFilter(context, single, kind, redactAllAttributes) {
|
|
784
|
+
return (redactAllAttributes
|
|
785
|
+
? Object.keys(single).map((k) => new AttributeReference(k, true))
|
|
786
|
+
: [...this._privateAttributes, ...context.privateAttributes(kind)]).filter((attr) => !protectedAttributes.some((protectedAttr) => protectedAttr.compare(attr)));
|
|
787
|
+
}
|
|
788
|
+
_filterSingleKind(context, single, kind, redactAnonymousAttributes) {
|
|
789
|
+
const redactAllAttributes = this._allAttributesPrivate || (redactAnonymousAttributes && single.anonymous === true);
|
|
790
|
+
const { cloned, excluded } = cloneWithRedactions(single, this._getAttributesToFilter(context, single, kind, redactAllAttributes));
|
|
791
|
+
if (context.legacy) {
|
|
792
|
+
legacyTopLevelCopyAttributes.forEach((name) => {
|
|
793
|
+
if (name in cloned) {
|
|
794
|
+
cloned[name] = String(cloned[name]);
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
if (excluded.length) {
|
|
799
|
+
if (!cloned._meta) {
|
|
800
|
+
cloned._meta = {};
|
|
801
|
+
}
|
|
802
|
+
cloned._meta.redactedAttributes = excluded;
|
|
803
|
+
}
|
|
804
|
+
if (cloned._meta) {
|
|
805
|
+
delete cloned._meta.privateAttributes;
|
|
806
|
+
if (Object.keys(cloned._meta).length === 0) {
|
|
807
|
+
delete cloned._meta;
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
return cloned;
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
var DataSourceErrorKind;
|
|
815
|
+
(function (DataSourceErrorKind) {
|
|
816
|
+
/// An unexpected error, such as an uncaught exception, further
|
|
817
|
+
/// described by the error message.
|
|
818
|
+
DataSourceErrorKind["Unknown"] = "UNKNOWN";
|
|
819
|
+
/// An I/O error such as a dropped connection.
|
|
820
|
+
DataSourceErrorKind["NetworkError"] = "NETWORK_ERROR";
|
|
821
|
+
/// The LaunchDarkly service returned an HTTP response with an error
|
|
822
|
+
/// status, available in the status code.
|
|
823
|
+
DataSourceErrorKind["ErrorResponse"] = "ERROR_RESPONSE";
|
|
824
|
+
/// The SDK received malformed data from the LaunchDarkly service.
|
|
825
|
+
DataSourceErrorKind["InvalidData"] = "INVALID_DATA";
|
|
826
|
+
})(DataSourceErrorKind || (DataSourceErrorKind = {}));
|
|
827
|
+
|
|
828
|
+
class LDFileDataSourceError extends Error {
|
|
829
|
+
constructor(message) {
|
|
830
|
+
super(message);
|
|
831
|
+
this.name = 'LaunchDarklyFileDataSourceError';
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
class LDPollingError extends Error {
|
|
835
|
+
constructor(kind, message, status, recoverable = true) {
|
|
836
|
+
super(message);
|
|
837
|
+
this.kind = kind;
|
|
838
|
+
this.status = status;
|
|
839
|
+
this.name = 'LaunchDarklyPollingError';
|
|
840
|
+
this.recoverable = recoverable;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
class LDStreamingError extends Error {
|
|
844
|
+
constructor(kind, message, code, recoverable = true) {
|
|
845
|
+
super(message);
|
|
846
|
+
this.kind = kind;
|
|
847
|
+
this.code = code;
|
|
848
|
+
this.name = 'LaunchDarklyStreamingError';
|
|
849
|
+
this.recoverable = recoverable;
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
/* eslint-disable import/prefer-default-export */
|
|
854
|
+
/**
|
|
855
|
+
* Enable / disable Auto environment attributes. When enabled, the SDK will automatically
|
|
856
|
+
* provide data about the mobile environment where the application is running. This data makes it simpler to target
|
|
857
|
+
* your mobile customers based on application name or version, or on device characteristics including manufacturer,
|
|
858
|
+
* model, operating system, locale, and so on. We recommend enabling this when you configure the SDK. To learn more,
|
|
859
|
+
* read [Automatic environment attributes](https://docs.launchdarkly.com/sdk/features/environment-attributes).
|
|
860
|
+
* for more documentation.
|
|
861
|
+
*
|
|
862
|
+
* The default is disabled.
|
|
863
|
+
*/
|
|
864
|
+
var AutoEnvAttributes;
|
|
865
|
+
(function (AutoEnvAttributes) {
|
|
866
|
+
AutoEnvAttributes[AutoEnvAttributes["Disabled"] = 0] = "Disabled";
|
|
867
|
+
AutoEnvAttributes[AutoEnvAttributes["Enabled"] = 1] = "Enabled";
|
|
868
|
+
})(AutoEnvAttributes || (AutoEnvAttributes = {}));
|
|
869
|
+
|
|
870
|
+
var LDEventType;
|
|
871
|
+
(function (LDEventType) {
|
|
872
|
+
LDEventType[LDEventType["AnalyticsEvents"] = 0] = "AnalyticsEvents";
|
|
873
|
+
LDEventType[LDEventType["DiagnosticEvent"] = 1] = "DiagnosticEvent";
|
|
874
|
+
})(LDEventType || (LDEventType = {}));
|
|
875
|
+
var LDDeliveryStatus;
|
|
876
|
+
(function (LDDeliveryStatus) {
|
|
877
|
+
LDDeliveryStatus[LDDeliveryStatus["Succeeded"] = 0] = "Succeeded";
|
|
878
|
+
LDDeliveryStatus[LDDeliveryStatus["Failed"] = 1] = "Failed";
|
|
879
|
+
LDDeliveryStatus[LDDeliveryStatus["FailedAndMustShutDown"] = 2] = "FailedAndMustShutDown";
|
|
880
|
+
})(LDDeliveryStatus || (LDDeliveryStatus = {}));
|
|
881
|
+
|
|
882
|
+
var index$1 = /*#__PURE__*/Object.freeze({
|
|
883
|
+
__proto__: null,
|
|
884
|
+
get LDDeliveryStatus () { return LDDeliveryStatus; },
|
|
885
|
+
get LDEventType () { return LDEventType; }
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
/**
|
|
889
|
+
* Attempt to produce a string representation of a value.
|
|
890
|
+
* The format should be roughly comparable to `util.format`
|
|
891
|
+
* aside from object which will be JSON versus the `util.inspect`
|
|
892
|
+
* format.
|
|
893
|
+
* @param val
|
|
894
|
+
* @returns A string representation of the value if possible.
|
|
895
|
+
*/
|
|
896
|
+
function tryStringify(val) {
|
|
897
|
+
if (typeof val === 'string') {
|
|
898
|
+
return val;
|
|
899
|
+
}
|
|
900
|
+
if (val === undefined) {
|
|
901
|
+
return 'undefined';
|
|
902
|
+
}
|
|
903
|
+
if (val === null) {
|
|
904
|
+
return 'null';
|
|
905
|
+
}
|
|
906
|
+
if (Object.prototype.hasOwnProperty.call(val, 'toString')) {
|
|
907
|
+
try {
|
|
908
|
+
return val.toString();
|
|
909
|
+
}
|
|
910
|
+
catch {
|
|
911
|
+
/* Keep going */
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (typeof val === 'bigint') {
|
|
915
|
+
return `${val}n`;
|
|
916
|
+
}
|
|
917
|
+
try {
|
|
918
|
+
return JSON.stringify(val);
|
|
919
|
+
}
|
|
920
|
+
catch (error) {
|
|
921
|
+
if (error instanceof TypeError && error.message.indexOf('circular') >= 0) {
|
|
922
|
+
return '[Circular]';
|
|
923
|
+
}
|
|
924
|
+
return '[Not Stringifiable]';
|
|
925
|
+
}
|
|
926
|
+
}
|
|
927
|
+
/**
|
|
928
|
+
* Attempt to produce a numeric representation.
|
|
929
|
+
* BigInts have an `n` suffix.
|
|
930
|
+
* @param val
|
|
931
|
+
* @returns The numeric representation or 'NaN' if not numeric.
|
|
932
|
+
*/
|
|
933
|
+
function toNumber(val) {
|
|
934
|
+
// Symbol has to be treated special because it will
|
|
935
|
+
// throw an exception if an attempt is made to convert it.
|
|
936
|
+
if (typeof val === 'symbol') {
|
|
937
|
+
return 'NaN';
|
|
938
|
+
}
|
|
939
|
+
if (typeof val === 'bigint') {
|
|
940
|
+
return `${val}n`;
|
|
941
|
+
}
|
|
942
|
+
return String(Number(val));
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Attempt to produce an integer representation.
|
|
946
|
+
* BigInts have an `n` suffix.
|
|
947
|
+
* @param val
|
|
948
|
+
* @returns The integer representation or 'NaN' if not numeric.
|
|
949
|
+
*/
|
|
950
|
+
function toInt(val) {
|
|
951
|
+
if (typeof val === 'symbol') {
|
|
952
|
+
return 'NaN';
|
|
953
|
+
}
|
|
954
|
+
if (typeof val === 'bigint') {
|
|
955
|
+
return `${val}n`;
|
|
956
|
+
}
|
|
957
|
+
return String(parseInt(val, 10));
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* Attempt to produce a float representation.
|
|
961
|
+
* BigInts have an `n` suffix.
|
|
962
|
+
* @param val
|
|
963
|
+
* @returns The integer representation or 'NaN' if not numeric.
|
|
964
|
+
*/
|
|
965
|
+
function toFloat(val) {
|
|
966
|
+
if (typeof val === 'symbol') {
|
|
967
|
+
return 'NaN';
|
|
968
|
+
}
|
|
969
|
+
return String(parseFloat(val));
|
|
970
|
+
}
|
|
971
|
+
// Based on:
|
|
972
|
+
// https://nodejs.org/api/util.html#utilformatformat-args
|
|
973
|
+
// The result will not match node exactly, but it should get the
|
|
974
|
+
// right information through.
|
|
975
|
+
const escapes = {
|
|
976
|
+
s: (val) => tryStringify(val),
|
|
977
|
+
d: (val) => toNumber(val),
|
|
978
|
+
i: (val) => toInt(val),
|
|
979
|
+
f: (val) => toFloat(val),
|
|
980
|
+
j: (val) => tryStringify(val),
|
|
981
|
+
o: (val) => tryStringify(val),
|
|
982
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
983
|
+
O: (val) => tryStringify(val),
|
|
984
|
+
c: () => '',
|
|
985
|
+
};
|
|
986
|
+
/**
|
|
987
|
+
* A basic formatted for use where `util.format` is not available.
|
|
988
|
+
* This will not be as performant, but it will produce formatted
|
|
989
|
+
* messages.
|
|
990
|
+
*
|
|
991
|
+
* @internal
|
|
992
|
+
*
|
|
993
|
+
* @param args
|
|
994
|
+
* @returns Formatted string.
|
|
995
|
+
*/
|
|
996
|
+
function format(...args) {
|
|
997
|
+
const formatString = args.shift();
|
|
998
|
+
if (TypeValidators.String.is(formatString)) {
|
|
999
|
+
let out = '';
|
|
1000
|
+
let i = 0;
|
|
1001
|
+
while (i < formatString.length) {
|
|
1002
|
+
const char = formatString.charAt(i);
|
|
1003
|
+
if (char === '%') {
|
|
1004
|
+
const nextIndex = i + 1;
|
|
1005
|
+
if (nextIndex < formatString.length) {
|
|
1006
|
+
const nextChar = formatString.charAt(i + 1);
|
|
1007
|
+
if (nextChar in escapes && args.length) {
|
|
1008
|
+
const value = args.shift();
|
|
1009
|
+
// This rule is for math.
|
|
1010
|
+
// eslint-disable-next-line no-unsafe-optional-chaining
|
|
1011
|
+
out += escapes[nextChar]?.(value);
|
|
1012
|
+
}
|
|
1013
|
+
else if (nextChar === '%') {
|
|
1014
|
+
out += '%';
|
|
1015
|
+
}
|
|
1016
|
+
else {
|
|
1017
|
+
out += `%${nextChar}`;
|
|
1018
|
+
}
|
|
1019
|
+
i += 2;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
else {
|
|
1023
|
+
out += char;
|
|
1024
|
+
i += 1;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// If there are any args left after we exhaust the format string
|
|
1028
|
+
// then just stick those on the end.
|
|
1029
|
+
if (args.length) {
|
|
1030
|
+
if (out.length) {
|
|
1031
|
+
out += ' ';
|
|
1032
|
+
}
|
|
1033
|
+
out += args.map(tryStringify).join(' ');
|
|
1034
|
+
}
|
|
1035
|
+
return out;
|
|
1036
|
+
}
|
|
1037
|
+
return args.map(tryStringify).join(' ');
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
const LogPriority = {
|
|
1041
|
+
debug: 0,
|
|
1042
|
+
info: 1,
|
|
1043
|
+
warn: 2,
|
|
1044
|
+
error: 3,
|
|
1045
|
+
none: 4,
|
|
1046
|
+
};
|
|
1047
|
+
const LevelNames = ['debug', 'info', 'warn', 'error', 'none'];
|
|
1048
|
+
/**
|
|
1049
|
+
* A basic logger which handles filtering by level.
|
|
1050
|
+
*
|
|
1051
|
+
* With the default options it will write to `console.error`
|
|
1052
|
+
* and it will use the formatting provided by `console.error`.
|
|
1053
|
+
* If the destination is overwritten, then it will use an included
|
|
1054
|
+
* formatter similar to `util.format`.
|
|
1055
|
+
*
|
|
1056
|
+
* If a formatter is available, then that should be overridden
|
|
1057
|
+
* as well for performance.
|
|
1058
|
+
*/
|
|
1059
|
+
class BasicLogger {
|
|
1060
|
+
/**
|
|
1061
|
+
* This should only be used as a default fallback and not as a convenient
|
|
1062
|
+
* solution. In most cases you should construct a new instance with the
|
|
1063
|
+
* appropriate options for your specific needs.
|
|
1064
|
+
*/
|
|
1065
|
+
static get() {
|
|
1066
|
+
return new BasicLogger({});
|
|
1067
|
+
}
|
|
1068
|
+
constructor(options) {
|
|
1069
|
+
this._logLevel = LogPriority[options.level ?? 'info'] ?? LogPriority.info;
|
|
1070
|
+
this._name = options.name ?? 'LaunchDarkly';
|
|
1071
|
+
// eslint-disable-next-line no-console
|
|
1072
|
+
this._destination = options.destination;
|
|
1073
|
+
this._formatter = options.formatter;
|
|
1074
|
+
}
|
|
1075
|
+
_tryFormat(...args) {
|
|
1076
|
+
try {
|
|
1077
|
+
if (this._formatter) {
|
|
1078
|
+
// In case the provided formatter fails.
|
|
1079
|
+
return this._formatter?.(...args);
|
|
1080
|
+
}
|
|
1081
|
+
return format(...args);
|
|
1082
|
+
}
|
|
1083
|
+
catch {
|
|
1084
|
+
return format(...args);
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
_tryWrite(msg) {
|
|
1088
|
+
try {
|
|
1089
|
+
this._destination(msg);
|
|
1090
|
+
}
|
|
1091
|
+
catch {
|
|
1092
|
+
// eslint-disable-next-line no-console
|
|
1093
|
+
console.error(msg);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
_log(level, args) {
|
|
1097
|
+
if (level >= this._logLevel) {
|
|
1098
|
+
const prefix = `${LevelNames[level]}: [${this._name}]`;
|
|
1099
|
+
try {
|
|
1100
|
+
if (this._destination) {
|
|
1101
|
+
this._tryWrite(`${prefix} ${this._tryFormat(...args)}`);
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
// `console.error` has its own formatter.
|
|
1105
|
+
// So we don't need to do anything.
|
|
1106
|
+
// eslint-disable-next-line no-console
|
|
1107
|
+
console.error(...args);
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
catch {
|
|
1111
|
+
// If all else fails do not break.
|
|
1112
|
+
// eslint-disable-next-line no-console
|
|
1113
|
+
console.error(...args);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
error(...args) {
|
|
1118
|
+
this._log(LogPriority.error, args);
|
|
1119
|
+
}
|
|
1120
|
+
warn(...args) {
|
|
1121
|
+
this._log(LogPriority.warn, args);
|
|
1122
|
+
}
|
|
1123
|
+
info(...args) {
|
|
1124
|
+
this._log(LogPriority.info, args);
|
|
1125
|
+
}
|
|
1126
|
+
debug(...args) {
|
|
1127
|
+
this._log(LogPriority.debug, args);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
const loggerRequirements = {
|
|
1132
|
+
error: TypeValidators.Function,
|
|
1133
|
+
warn: TypeValidators.Function,
|
|
1134
|
+
info: TypeValidators.Function,
|
|
1135
|
+
debug: TypeValidators.Function,
|
|
1136
|
+
};
|
|
1137
|
+
/**
|
|
1138
|
+
* The safeLogger logic exists because we allow the application to pass in a custom logger, but
|
|
1139
|
+
* there is no guarantee that the logger works correctly and if it ever throws exceptions there
|
|
1140
|
+
* could be serious consequences (e.g. an uncaught exception within an error event handler, due
|
|
1141
|
+
* to the SDK trying to log the error, can terminate the application). An exception could result
|
|
1142
|
+
* from faulty logic in the logger implementation, or it could be that this is not a logger at
|
|
1143
|
+
* all but some other kind of object; the former is handled by a catch block that logs an error
|
|
1144
|
+
* message to the SDK's default logger, and we can at least partly guard against the latter by
|
|
1145
|
+
* checking for the presence of required methods at configuration time.
|
|
1146
|
+
*/
|
|
1147
|
+
class SafeLogger {
|
|
1148
|
+
/**
|
|
1149
|
+
* Construct a safe logger with the specified logger.
|
|
1150
|
+
* @param logger The logger to use.
|
|
1151
|
+
* @param fallback A fallback logger to use in case an issue is encountered using
|
|
1152
|
+
* the provided logger.
|
|
1153
|
+
*/
|
|
1154
|
+
constructor(logger, fallback) {
|
|
1155
|
+
Object.entries(loggerRequirements).forEach(([level, validator]) => {
|
|
1156
|
+
if (!validator.is(logger[level])) {
|
|
1157
|
+
throw new Error(`Provided logger instance must support logger.${level}(...) method`);
|
|
1158
|
+
// Note that the SDK normally does not throw exceptions to the application, but that rule
|
|
1159
|
+
// does not apply to LDClient.init() which will throw an exception if the parameters are so
|
|
1160
|
+
// invalid that we cannot proceed with creating the client. An invalid logger meets those
|
|
1161
|
+
// criteria since the SDK calls the logger during nearly all of its operations.
|
|
1162
|
+
}
|
|
1163
|
+
});
|
|
1164
|
+
this._logger = logger;
|
|
1165
|
+
this._fallback = fallback;
|
|
1166
|
+
}
|
|
1167
|
+
_log(level, args) {
|
|
1168
|
+
try {
|
|
1169
|
+
this._logger[level](...args);
|
|
1170
|
+
}
|
|
1171
|
+
catch {
|
|
1172
|
+
// If all else fails do not break.
|
|
1173
|
+
this._fallback[level](...args);
|
|
1174
|
+
}
|
|
1175
|
+
}
|
|
1176
|
+
error(...args) {
|
|
1177
|
+
this._log('error', args);
|
|
1178
|
+
}
|
|
1179
|
+
warn(...args) {
|
|
1180
|
+
this._log('warn', args);
|
|
1181
|
+
}
|
|
1182
|
+
info(...args) {
|
|
1183
|
+
this._log('info', args);
|
|
1184
|
+
}
|
|
1185
|
+
debug(...args) {
|
|
1186
|
+
this._log('debug', args);
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const createSafeLogger = (logger) => {
|
|
1191
|
+
const basicLogger = new BasicLogger({
|
|
1192
|
+
level: 'info',
|
|
1193
|
+
// eslint-disable-next-line no-console
|
|
1194
|
+
destination: console.error,
|
|
1195
|
+
formatter: format,
|
|
1196
|
+
});
|
|
1197
|
+
return logger ? new SafeLogger(logger, basicLogger) : basicLogger;
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Messages for issues which can be encountered from processing the configuration options.
|
|
1202
|
+
*/
|
|
1203
|
+
class OptionMessages {
|
|
1204
|
+
static deprecated(oldName, newName) {
|
|
1205
|
+
return `"${oldName}" is deprecated, please use "${newName}"`;
|
|
1206
|
+
}
|
|
1207
|
+
static optionBelowMinimum(name, value, min) {
|
|
1208
|
+
return `Config option "${name}" had invalid value of ${value}, using minimum of ${min} instead`;
|
|
1209
|
+
}
|
|
1210
|
+
static unknownOption(name) {
|
|
1211
|
+
return `Ignoring unknown config option "${name}"`;
|
|
1212
|
+
}
|
|
1213
|
+
static wrongOptionType(name, expectedType, actualType) {
|
|
1214
|
+
return `Config option "${name}" should be of type ${expectedType}, got ${actualType}, using default value`;
|
|
1215
|
+
}
|
|
1216
|
+
static wrongOptionTypeBoolean(name, actualType) {
|
|
1217
|
+
return `Config option "${name}" should be a boolean, got ${actualType}, converting to boolean`;
|
|
1218
|
+
}
|
|
1219
|
+
static invalidTagValue(name) {
|
|
1220
|
+
return `Config option "${name}" must only contain letters, numbers, ., _ or -.`;
|
|
1221
|
+
}
|
|
1222
|
+
static tagValueTooLong(name) {
|
|
1223
|
+
return `Value of "${name}" was longer than 64 characters and was discarded.`;
|
|
1224
|
+
}
|
|
1225
|
+
static partialEndpoint(name) {
|
|
1226
|
+
return `You have set custom uris without specifying the ${name} URI; connections may not work properly`;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
/**
|
|
1231
|
+
* Expression to validate characters that are allowed in tag keys and values.
|
|
1232
|
+
*/
|
|
1233
|
+
const allowedTagCharacters = /^(\w|\.|-)+$/;
|
|
1234
|
+
const regexValidator = TypeValidators.stringMatchingRegex(allowedTagCharacters);
|
|
1235
|
+
const tagValidator = {
|
|
1236
|
+
is: (u, name) => {
|
|
1237
|
+
if (regexValidator.is(u)) {
|
|
1238
|
+
if (u.length > 64) {
|
|
1239
|
+
return { valid: false, message: OptionMessages.tagValueTooLong(name) };
|
|
1240
|
+
}
|
|
1241
|
+
return { valid: true };
|
|
1242
|
+
}
|
|
1243
|
+
return { valid: false, message: OptionMessages.invalidTagValue(name) };
|
|
1244
|
+
},
|
|
1245
|
+
};
|
|
1246
|
+
/**
|
|
1247
|
+
* Class for managing tags.
|
|
1248
|
+
*/
|
|
1249
|
+
class ApplicationTags {
|
|
1250
|
+
constructor(options) {
|
|
1251
|
+
const tags = {};
|
|
1252
|
+
const application = options?.application;
|
|
1253
|
+
const logger = options?.logger;
|
|
1254
|
+
if (application) {
|
|
1255
|
+
Object.entries(application).forEach(([key, value]) => {
|
|
1256
|
+
if (value !== null && value !== undefined) {
|
|
1257
|
+
const { valid, message } = tagValidator.is(value, `application.${key}`);
|
|
1258
|
+
if (!valid) {
|
|
1259
|
+
logger?.warn(message);
|
|
1260
|
+
}
|
|
1261
|
+
else if (key === 'versionName') {
|
|
1262
|
+
tags[`application-version-name`] = [value];
|
|
1263
|
+
}
|
|
1264
|
+
else {
|
|
1265
|
+
tags[`application-${key}`] = [value];
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
const tagKeys = Object.keys(tags);
|
|
1271
|
+
if (tagKeys.length) {
|
|
1272
|
+
this.value = tagKeys
|
|
1273
|
+
.sort()
|
|
1274
|
+
.flatMap((key) => tags[key].sort().map((value) => `${key}/${value}`))
|
|
1275
|
+
.join(' ');
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
/**
|
|
1281
|
+
* The client context provides basic configuration and platform support which are required
|
|
1282
|
+
* when building SDK components.
|
|
1283
|
+
*/
|
|
1284
|
+
class ClientContext {
|
|
1285
|
+
constructor(sdkKey, configuration, platform) {
|
|
1286
|
+
this.platform = platform;
|
|
1287
|
+
this.basicConfiguration = {
|
|
1288
|
+
tags: configuration.tags,
|
|
1289
|
+
logger: configuration.logger,
|
|
1290
|
+
offline: configuration.offline,
|
|
1291
|
+
serviceEndpoints: configuration.serviceEndpoints,
|
|
1292
|
+
sdkKey,
|
|
1293
|
+
};
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
function canonicalizeUri(uri) {
|
|
1298
|
+
return uri.replace(/\/+$/, '');
|
|
1299
|
+
}
|
|
1300
|
+
function canonicalizePath(path) {
|
|
1301
|
+
return path.replace(/^\/+/, '').replace(/\?$/, '');
|
|
1302
|
+
}
|
|
1303
|
+
/**
|
|
1304
|
+
* Specifies the base service URIs used by SDK components.
|
|
1305
|
+
*/
|
|
1306
|
+
class ServiceEndpoints {
|
|
1307
|
+
constructor(streaming, polling, events = ServiceEndpoints.DEFAULT_EVENTS, analyticsEventPath = '/bulk', diagnosticEventPath = '/diagnostic', includeAuthorizationHeader = true, payloadFilterKey) {
|
|
1308
|
+
this.streaming = canonicalizeUri(streaming);
|
|
1309
|
+
this.polling = canonicalizeUri(polling);
|
|
1310
|
+
this.events = canonicalizeUri(events);
|
|
1311
|
+
this.analyticsEventPath = analyticsEventPath;
|
|
1312
|
+
this.diagnosticEventPath = diagnosticEventPath;
|
|
1313
|
+
this.includeAuthorizationHeader = includeAuthorizationHeader;
|
|
1314
|
+
this.payloadFilterKey = payloadFilterKey;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1318
|
+
ServiceEndpoints.DEFAULT_EVENTS = 'https://events.launchdarkly.com';
|
|
1319
|
+
function getWithParams(uri, parameters) {
|
|
1320
|
+
if (parameters.length === 0) {
|
|
1321
|
+
return uri;
|
|
1322
|
+
}
|
|
1323
|
+
const parts = parameters.map(({ key, value }) => `${key}=${value}`);
|
|
1324
|
+
return `${uri}?${parts.join('&')}`;
|
|
1325
|
+
}
|
|
1326
|
+
/**
|
|
1327
|
+
* Get the URI for the streaming endpoint.
|
|
1328
|
+
*
|
|
1329
|
+
* @param endpoints The service endpoints.
|
|
1330
|
+
* @param path The path to the resource, devoid of any query parameters or hrefs.
|
|
1331
|
+
* @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
|
|
1332
|
+
*/
|
|
1333
|
+
function getStreamingUri(endpoints, path, parameters) {
|
|
1334
|
+
const canonicalizedPath = canonicalizePath(path);
|
|
1335
|
+
const combinedParameters = [...parameters];
|
|
1336
|
+
if (endpoints.payloadFilterKey) {
|
|
1337
|
+
combinedParameters.push({ key: 'filter', value: endpoints.payloadFilterKey });
|
|
1338
|
+
}
|
|
1339
|
+
return getWithParams(`${endpoints.streaming}/${canonicalizedPath}`, combinedParameters);
|
|
1340
|
+
}
|
|
1341
|
+
/**
|
|
1342
|
+
* Get the URI for the polling endpoint.
|
|
1343
|
+
*
|
|
1344
|
+
* @param endpoints The service endpoints.
|
|
1345
|
+
* @param path The path to the resource, devoid of any query parameters or hrefs.
|
|
1346
|
+
* @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
|
|
1347
|
+
*/
|
|
1348
|
+
function getPollingUri(endpoints, path, parameters) {
|
|
1349
|
+
const canonicalizedPath = canonicalizePath(path);
|
|
1350
|
+
const combinedParameters = [...parameters];
|
|
1351
|
+
if (endpoints.payloadFilterKey) {
|
|
1352
|
+
combinedParameters.push({ key: 'filter', value: endpoints.payloadFilterKey });
|
|
1353
|
+
}
|
|
1354
|
+
return getWithParams(`${endpoints.polling}/${canonicalizedPath}`, combinedParameters);
|
|
1355
|
+
}
|
|
1356
|
+
/**
|
|
1357
|
+
* Get the URI for the events endpoint.
|
|
1358
|
+
*
|
|
1359
|
+
* @param endpoints The service endpoints.
|
|
1360
|
+
* @param path The path to the resource, devoid of any query parameters or hrefs.
|
|
1361
|
+
* @param parameters The query parameters. These query parameters must already have the appropriate encoding applied. This function WILL NOT apply it for you.
|
|
1362
|
+
*/
|
|
1363
|
+
function getEventsUri(endpoints, path, parameters) {
|
|
1364
|
+
const canonicalizedPath = canonicalizePath(path);
|
|
1365
|
+
return getWithParams(`${endpoints.events}/${canonicalizedPath}`, parameters);
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1368
|
+
// These classes are of trivial complexity. If they become
|
|
1369
|
+
// more complex, then they could be independent files.
|
|
1370
|
+
/* eslint-disable max-classes-per-file */
|
|
1371
|
+
class LDUnexpectedResponseError extends Error {
|
|
1372
|
+
constructor(message) {
|
|
1373
|
+
super(message);
|
|
1374
|
+
this.name = 'LaunchDarklyUnexpectedResponseError';
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
class LDClientError extends Error {
|
|
1378
|
+
constructor(message) {
|
|
1379
|
+
super(message);
|
|
1380
|
+
this.name = 'LaunchDarklyClientError';
|
|
1381
|
+
}
|
|
1382
|
+
}
|
|
1383
|
+
class LDTimeoutError extends Error {
|
|
1384
|
+
constructor(message) {
|
|
1385
|
+
super(message);
|
|
1386
|
+
this.name = 'LaunchDarklyTimeoutError';
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Check if the HTTP error is recoverable. This will return false if a request
|
|
1391
|
+
* made with any payload could not recover. If the reason for the failure
|
|
1392
|
+
* is payload specific, for instance a payload that is too large, then
|
|
1393
|
+
* it could recover with a different payload.
|
|
1394
|
+
*/
|
|
1395
|
+
function isHttpRecoverable(status) {
|
|
1396
|
+
if (status >= 400 && status < 500) {
|
|
1397
|
+
return status === 400 || status === 408 || status === 429;
|
|
1398
|
+
}
|
|
1399
|
+
return true;
|
|
1400
|
+
}
|
|
1401
|
+
/**
|
|
1402
|
+
* Returns true if the status could recover for a different payload.
|
|
1403
|
+
*
|
|
1404
|
+
* When used with event processing this indicates that we should discard
|
|
1405
|
+
* the payload, but that a subsequent payload may succeed. Therefore we should
|
|
1406
|
+
* not stop event processing.
|
|
1407
|
+
*/
|
|
1408
|
+
function isHttpLocallyRecoverable(status) {
|
|
1409
|
+
if (status === 413) {
|
|
1410
|
+
return true;
|
|
1411
|
+
}
|
|
1412
|
+
return isHttpRecoverable(status);
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
/**
|
|
1416
|
+
* Returns a promise which errors after t seconds.
|
|
1417
|
+
*
|
|
1418
|
+
* @param t Timeout in seconds.
|
|
1419
|
+
* @param taskName Name of task being timed for logging and error reporting.
|
|
1420
|
+
*/
|
|
1421
|
+
function cancelableTimedPromise(t, taskName) {
|
|
1422
|
+
let timeout;
|
|
1423
|
+
let resolve;
|
|
1424
|
+
const promise = new Promise((_res, reject) => {
|
|
1425
|
+
resolve = _res;
|
|
1426
|
+
timeout = setTimeout(() => {
|
|
1427
|
+
const e = `${taskName} timed out after ${t} seconds.`;
|
|
1428
|
+
reject(new LDTimeoutError(e));
|
|
1429
|
+
}, t * 1000);
|
|
1430
|
+
});
|
|
1431
|
+
return {
|
|
1432
|
+
promise,
|
|
1433
|
+
cancel: () => {
|
|
1434
|
+
resolve();
|
|
1435
|
+
clearTimeout(timeout);
|
|
1436
|
+
},
|
|
1437
|
+
};
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
function clone(obj) {
|
|
1441
|
+
if (obj === undefined || obj === null) {
|
|
1442
|
+
return obj;
|
|
1443
|
+
}
|
|
1444
|
+
return JSON.parse(JSON.stringify(obj));
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
// eslint-disable-next-line import/prefer-default-export
|
|
1448
|
+
function secondsToMillis(sec) {
|
|
1449
|
+
return Math.trunc(sec * 1000);
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* Wait before calling the same function. Useful for expensive calls.
|
|
1454
|
+
* Adapted from https://amitd.co/code/typescript/debounce.
|
|
1455
|
+
*
|
|
1456
|
+
* @return The debounced function.
|
|
1457
|
+
*
|
|
1458
|
+
* @example
|
|
1459
|
+
*
|
|
1460
|
+
* ```js
|
|
1461
|
+
* const debouncedFunction = debounce(e => {
|
|
1462
|
+
* console.log(e);
|
|
1463
|
+
* }, 5000);
|
|
1464
|
+
*
|
|
1465
|
+
* // Console logs 'Hello world again ' after 5 seconds
|
|
1466
|
+
* debouncedFunction('Hello world');
|
|
1467
|
+
* debouncedFunction('Hello world again');
|
|
1468
|
+
* ```
|
|
1469
|
+
* @param fn The function to be debounced.
|
|
1470
|
+
* @param delayMs Defaults to 5 seconds.
|
|
1471
|
+
*/
|
|
1472
|
+
const debounce = (fn, delayMs = 5000) => {
|
|
1473
|
+
let timer;
|
|
1474
|
+
return (...args) => {
|
|
1475
|
+
clearTimeout(timer);
|
|
1476
|
+
timer = setTimeout(() => {
|
|
1477
|
+
fn(...args);
|
|
1478
|
+
}, delayMs);
|
|
1479
|
+
};
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
const isEmptyObject = (obj) => JSON.stringify(obj) === '{}';
|
|
1483
|
+
|
|
1484
|
+
/**
|
|
1485
|
+
* Strips all falsy and empty {} from a given object. Returns a new object with only truthy values.
|
|
1486
|
+
* Sourced from below but modified to include checks for empty object and ignoring keys.
|
|
1487
|
+
* https://www.w3resource.com/javascript-exercises/javascript-array-exercise-47.php
|
|
1488
|
+
*
|
|
1489
|
+
* @param obj
|
|
1490
|
+
* @param ignoreKeys
|
|
1491
|
+
*/
|
|
1492
|
+
const deepCompact = (obj, ignoreKeys) => {
|
|
1493
|
+
if (!obj) {
|
|
1494
|
+
return obj;
|
|
1495
|
+
}
|
|
1496
|
+
return Object.entries(obj).reduce((acc, [key, value]) => {
|
|
1497
|
+
if (Boolean(value) && !isEmptyObject(value) && !ignoreKeys?.includes(key)) {
|
|
1498
|
+
acc[key] = typeof value === 'object' ? deepCompact(value, ignoreKeys) : value;
|
|
1499
|
+
}
|
|
1500
|
+
return acc;
|
|
1501
|
+
}, {});
|
|
1502
|
+
};
|
|
1503
|
+
|
|
1504
|
+
/* eslint-disable */
|
|
1505
|
+
// Ripped from https://github.com/epoberezkin/fast-deep-fastDeepEqual
|
|
1506
|
+
// {{? it.es6 }}
|
|
1507
|
+
// var envHasBigInt64Array = typeof BigInt64Array !== 'undefined';
|
|
1508
|
+
// {{?}}
|
|
1509
|
+
function fastDeepEqual(a, b) {
|
|
1510
|
+
if (a === b)
|
|
1511
|
+
return true;
|
|
1512
|
+
if (a && b && typeof a == 'object' && typeof b == 'object') {
|
|
1513
|
+
if (a.constructor !== b.constructor)
|
|
1514
|
+
return false;
|
|
1515
|
+
var length, i, keys;
|
|
1516
|
+
if (Array.isArray(a)) {
|
|
1517
|
+
length = a.length;
|
|
1518
|
+
if (length != b.length)
|
|
1519
|
+
return false;
|
|
1520
|
+
for (i = length; i-- !== 0;)
|
|
1521
|
+
if (!fastDeepEqual(a[i], b[i]))
|
|
1522
|
+
return false;
|
|
1523
|
+
return true;
|
|
1524
|
+
}
|
|
1525
|
+
// {{? it.es6 }}
|
|
1526
|
+
if (a instanceof Map && b instanceof Map) {
|
|
1527
|
+
if (a.size !== b.size)
|
|
1528
|
+
return false;
|
|
1529
|
+
for (i of a.entries())
|
|
1530
|
+
if (!b.has(i[0]))
|
|
1531
|
+
return false;
|
|
1532
|
+
for (i of a.entries())
|
|
1533
|
+
if (!fastDeepEqual(i[1], b.get(i[0])))
|
|
1534
|
+
return false;
|
|
1535
|
+
return true;
|
|
1536
|
+
}
|
|
1537
|
+
if (a instanceof Set && b instanceof Set) {
|
|
1538
|
+
if (a.size !== b.size)
|
|
1539
|
+
return false;
|
|
1540
|
+
for (i of a.entries())
|
|
1541
|
+
if (!b.has(i[0]))
|
|
1542
|
+
return false;
|
|
1543
|
+
return true;
|
|
1544
|
+
}
|
|
1545
|
+
if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
|
|
1546
|
+
// @ts-ignore
|
|
1547
|
+
length = a.length;
|
|
1548
|
+
// @ts-ignore
|
|
1549
|
+
if (length != b.length)
|
|
1550
|
+
return false;
|
|
1551
|
+
for (i = length; i-- !== 0;) {
|
|
1552
|
+
// @ts-ignore
|
|
1553
|
+
if (a[i] !== b[i])
|
|
1554
|
+
return false;
|
|
1555
|
+
}
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
// {{?}}
|
|
1559
|
+
if (a.constructor === RegExp)
|
|
1560
|
+
return a.source === b.source && a.flags === b.flags;
|
|
1561
|
+
if (a.valueOf !== Object.prototype.valueOf)
|
|
1562
|
+
return a.valueOf() === b.valueOf();
|
|
1563
|
+
if (a.toString !== Object.prototype.toString)
|
|
1564
|
+
return a.toString() === b.toString();
|
|
1565
|
+
keys = Object.keys(a);
|
|
1566
|
+
length = keys.length;
|
|
1567
|
+
if (length !== Object.keys(b).length)
|
|
1568
|
+
return false;
|
|
1569
|
+
for (i = length; i-- !== 0;)
|
|
1570
|
+
if (!Object.prototype.hasOwnProperty.call(b, keys[i]))
|
|
1571
|
+
return false;
|
|
1572
|
+
for (i = length; i-- !== 0;) {
|
|
1573
|
+
var key = keys[i];
|
|
1574
|
+
// {{? it.react }}
|
|
1575
|
+
// if (key === '_owner' && a.$$typeof) {
|
|
1576
|
+
// // React-specific: avoid traversing React elements' _owner.
|
|
1577
|
+
// // _owner contains circular references
|
|
1578
|
+
// // and is not needed when comparing the actual elements (and not their owners)
|
|
1579
|
+
// continue;
|
|
1580
|
+
// }
|
|
1581
|
+
// {{?}}
|
|
1582
|
+
if (!fastDeepEqual(a[key], b[key]))
|
|
1583
|
+
return false;
|
|
1584
|
+
}
|
|
1585
|
+
return true;
|
|
1586
|
+
}
|
|
1587
|
+
// true if both NaN, false otherwise
|
|
1588
|
+
return a !== a && b !== b;
|
|
1589
|
+
}
|
|
1590
|
+
|
|
1591
|
+
function defaultHeaders(sdkKey, info, tags, includeAuthorizationHeader = true, userAgentHeaderName = 'user-agent') {
|
|
1592
|
+
const { userAgentBase, version, wrapperName, wrapperVersion } = info.sdkData();
|
|
1593
|
+
const headers = {
|
|
1594
|
+
[userAgentHeaderName]: `${userAgentBase ?? 'NodeJSClient'}/${version}`,
|
|
1595
|
+
};
|
|
1596
|
+
// edge sdks sets this to false because they use the clientSideID
|
|
1597
|
+
// and they don't need the authorization header
|
|
1598
|
+
if (includeAuthorizationHeader) {
|
|
1599
|
+
headers.authorization = sdkKey;
|
|
1600
|
+
}
|
|
1601
|
+
if (wrapperName) {
|
|
1602
|
+
headers['x-launchdarkly-wrapper'] = wrapperVersion
|
|
1603
|
+
? `${wrapperName}/${wrapperVersion}`
|
|
1604
|
+
: wrapperName;
|
|
1605
|
+
}
|
|
1606
|
+
if (tags?.value) {
|
|
1607
|
+
headers['x-launchdarkly-tags'] = tags.value;
|
|
1608
|
+
}
|
|
1609
|
+
return headers;
|
|
1610
|
+
}
|
|
1611
|
+
function httpErrorMessage(err, context, retryMessage) {
|
|
1612
|
+
let desc;
|
|
1613
|
+
if (err.status) {
|
|
1614
|
+
desc = `error ${err.status}${err.status === 401 ? ' (invalid SDK key)' : ''}`;
|
|
1615
|
+
}
|
|
1616
|
+
else {
|
|
1617
|
+
desc = `I/O error (${err.message || 'unknown error'})`;
|
|
1618
|
+
}
|
|
1619
|
+
const action = retryMessage ?? 'giving up permanently';
|
|
1620
|
+
return `Received ${desc} for ${context} - ${action}`;
|
|
1621
|
+
}
|
|
1622
|
+
function shouldRetry({ status }) {
|
|
1623
|
+
return status ? isHttpRecoverable(status) : true;
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* In react-native use base64-js to polyfill btoa. This is safe
|
|
1627
|
+
* because the react-native repo uses it too. Set the global.btoa to the encode
|
|
1628
|
+
* function of base64-js.
|
|
1629
|
+
* https://github.com/beatgammit/base64-js
|
|
1630
|
+
* https://github.com/axios/axios/issues/2235#issuecomment-512204616
|
|
1631
|
+
*
|
|
1632
|
+
* Ripped from https://thewoods.blog/base64url/
|
|
1633
|
+
*/
|
|
1634
|
+
const base64UrlEncode = (s, encoding) => encoding.btoa(s).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
1635
|
+
|
|
1636
|
+
var noop = () => { };
|
|
1637
|
+
|
|
1638
|
+
const sleep = async (delayMillis = 1000) => new Promise((resolve) => {
|
|
1639
|
+
setTimeout(resolve, delayMillis);
|
|
1640
|
+
});
|
|
1641
|
+
|
|
1642
|
+
/**
|
|
1643
|
+
* Returns a promise which errors after t seconds.
|
|
1644
|
+
*
|
|
1645
|
+
* @param t Timeout in seconds.
|
|
1646
|
+
* @param taskName Name of task being timed for logging and error reporting.
|
|
1647
|
+
*/
|
|
1648
|
+
const timedPromise = (t, taskName) => new Promise((_res, reject) => {
|
|
1649
|
+
setTimeout(() => {
|
|
1650
|
+
const e = `${taskName} timed out after ${t} seconds.`;
|
|
1651
|
+
reject(new LDTimeoutError(e));
|
|
1652
|
+
}, t * 1000);
|
|
1653
|
+
});
|
|
1654
|
+
|
|
1655
|
+
class DiagnosticsManager {
|
|
1656
|
+
constructor(sdkKey, _platform, _diagnosticInitConfig) {
|
|
1657
|
+
this._platform = _platform;
|
|
1658
|
+
this._diagnosticInitConfig = _diagnosticInitConfig;
|
|
1659
|
+
this._streamInits = [];
|
|
1660
|
+
this._startTime = Date.now();
|
|
1661
|
+
this._dataSinceDate = this._startTime;
|
|
1662
|
+
this._id = {
|
|
1663
|
+
diagnosticId: _platform.crypto.randomUUID(),
|
|
1664
|
+
sdkKeySuffix: sdkKey.length > 6 ? sdkKey.substring(sdkKey.length - 6) : sdkKey,
|
|
1665
|
+
};
|
|
1666
|
+
}
|
|
1667
|
+
/**
|
|
1668
|
+
* Creates the initial event that is sent by the event processor when the SDK starts up. This will
|
|
1669
|
+
* not be repeated during the lifetime of the SDK client.
|
|
1670
|
+
*/
|
|
1671
|
+
createInitEvent() {
|
|
1672
|
+
const sdkData = this._platform.info.sdkData();
|
|
1673
|
+
const platformData = this._platform.info.platformData();
|
|
1674
|
+
return {
|
|
1675
|
+
kind: 'diagnostic-init',
|
|
1676
|
+
id: this._id,
|
|
1677
|
+
creationDate: this._startTime,
|
|
1678
|
+
sdk: sdkData,
|
|
1679
|
+
configuration: this._diagnosticInitConfig,
|
|
1680
|
+
platform: {
|
|
1681
|
+
name: platformData.name,
|
|
1682
|
+
osArch: platformData.os?.arch,
|
|
1683
|
+
osName: platformData.os?.name,
|
|
1684
|
+
osVersion: platformData.os?.version,
|
|
1685
|
+
...(platformData.additional || {}),
|
|
1686
|
+
},
|
|
1687
|
+
};
|
|
1688
|
+
}
|
|
1689
|
+
/**
|
|
1690
|
+
* Records a stream connection attempt (called by the stream processor).
|
|
1691
|
+
*
|
|
1692
|
+
* @param timestamp Time of the *beginning* of the connection attempt.
|
|
1693
|
+
* @param failed True if the connection failed, or we got a read timeout before receiving a "put".
|
|
1694
|
+
* @param durationMillis Elapsed time between starting timestamp and when we either gave up/lost
|
|
1695
|
+
* the connection or received a successful "put".
|
|
1696
|
+
*/
|
|
1697
|
+
recordStreamInit(timestamp, failed, durationMillis) {
|
|
1698
|
+
const item = { timestamp, failed, durationMillis };
|
|
1699
|
+
this._streamInits.push(item);
|
|
1700
|
+
}
|
|
1701
|
+
/**
|
|
1702
|
+
* Creates a periodic event containing time-dependent stats, and resets the state of the manager
|
|
1703
|
+
* with regard to those stats.
|
|
1704
|
+
*
|
|
1705
|
+
* Note: the reason droppedEvents, deduplicatedUsers, and eventsInLastBatch are passed into this
|
|
1706
|
+
* function, instead of being properties of the DiagnosticsManager, is that the event processor is
|
|
1707
|
+
* the one who's calling this function and is also the one who's tracking those stats.
|
|
1708
|
+
*/
|
|
1709
|
+
createStatsEventAndReset(droppedEvents, deduplicatedUsers, eventsInLastBatch) {
|
|
1710
|
+
const currentTime = Date.now();
|
|
1711
|
+
const evt = {
|
|
1712
|
+
kind: 'diagnostic',
|
|
1713
|
+
id: this._id,
|
|
1714
|
+
creationDate: currentTime,
|
|
1715
|
+
dataSinceDate: this._dataSinceDate,
|
|
1716
|
+
droppedEvents,
|
|
1717
|
+
deduplicatedUsers,
|
|
1718
|
+
eventsInLastBatch,
|
|
1719
|
+
streamInits: this._streamInits,
|
|
1720
|
+
};
|
|
1721
|
+
this._streamInits = [];
|
|
1722
|
+
this._dataSinceDate = currentTime;
|
|
1723
|
+
return evt;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
/**
|
|
1728
|
+
* Different kinds of error which may be encountered during evaluation.
|
|
1729
|
+
*/
|
|
1730
|
+
var ErrorKinds;
|
|
1731
|
+
(function (ErrorKinds) {
|
|
1732
|
+
ErrorKinds["MalformedFlag"] = "MALFORMED_FLAG";
|
|
1733
|
+
ErrorKinds["UserNotSpecified"] = "USER_NOT_SPECIFIED";
|
|
1734
|
+
ErrorKinds["FlagNotFound"] = "FLAG_NOT_FOUND";
|
|
1735
|
+
ErrorKinds["ClientNotReady"] = "CLIENT_NOT_READY";
|
|
1736
|
+
ErrorKinds["WrongType"] = "WRONG_TYPE";
|
|
1737
|
+
})(ErrorKinds || (ErrorKinds = {}));
|
|
1738
|
+
var ErrorKinds$1 = ErrorKinds;
|
|
1739
|
+
|
|
1740
|
+
/**
|
|
1741
|
+
* Messages for issues which can be encountered processing client requests.
|
|
1742
|
+
*/
|
|
1743
|
+
class ClientMessages {
|
|
1744
|
+
static invalidMetricValue(badType) {
|
|
1745
|
+
return ('The track function was called with a non-numeric "metricValue"' +
|
|
1746
|
+
` (${badType}), only numeric metric values are supported.`);
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
ClientMessages.MissingContextKeyNoEvent = 'Context was unspecified or had no key; event will not be sent';
|
|
1750
|
+
|
|
1751
|
+
class EventSender {
|
|
1752
|
+
constructor(clientContext, baseHeaders) {
|
|
1753
|
+
const { basicConfiguration, platform } = clientContext;
|
|
1754
|
+
const { serviceEndpoints: { analyticsEventPath, diagnosticEventPath }, } = basicConfiguration;
|
|
1755
|
+
const { crypto, requests } = platform;
|
|
1756
|
+
this._defaultHeaders = { ...baseHeaders };
|
|
1757
|
+
this._eventsUri = getEventsUri(basicConfiguration.serviceEndpoints, analyticsEventPath, []);
|
|
1758
|
+
this._diagnosticEventsUri = getEventsUri(basicConfiguration.serviceEndpoints, diagnosticEventPath, []);
|
|
1759
|
+
this._requests = requests;
|
|
1760
|
+
this._crypto = crypto;
|
|
1761
|
+
}
|
|
1762
|
+
async _tryPostingEvents(events, uri, payloadId, canRetry) {
|
|
1763
|
+
const tryRes = {
|
|
1764
|
+
status: LDDeliveryStatus.Succeeded,
|
|
1765
|
+
};
|
|
1766
|
+
const headers = {
|
|
1767
|
+
...this._defaultHeaders,
|
|
1768
|
+
'content-type': 'application/json',
|
|
1769
|
+
};
|
|
1770
|
+
if (payloadId) {
|
|
1771
|
+
headers['x-launchdarkly-payload-id'] = payloadId;
|
|
1772
|
+
headers['x-launchDarkly-event-schema'] = '4';
|
|
1773
|
+
}
|
|
1774
|
+
let error;
|
|
1775
|
+
try {
|
|
1776
|
+
const { status, headers: resHeaders } = await this._requests.fetch(uri, {
|
|
1777
|
+
headers,
|
|
1778
|
+
body: JSON.stringify(events),
|
|
1779
|
+
method: 'POST',
|
|
1780
|
+
// When sending events from browser environments the request should be completed even
|
|
1781
|
+
// if the user is navigating away from the page.
|
|
1782
|
+
keepalive: true,
|
|
1783
|
+
});
|
|
1784
|
+
const serverDate = Date.parse(resHeaders.get('date') || '');
|
|
1785
|
+
if (serverDate) {
|
|
1786
|
+
tryRes.serverTime = serverDate;
|
|
1787
|
+
}
|
|
1788
|
+
if (status <= 204) {
|
|
1789
|
+
return tryRes;
|
|
1790
|
+
}
|
|
1791
|
+
error = new LDUnexpectedResponseError(httpErrorMessage({ status, message: 'some events were dropped' }, 'event posting'));
|
|
1792
|
+
if (!isHttpRecoverable(status)) {
|
|
1793
|
+
// If the HTTP request isn't recoverable. Meaning if we made the same request it
|
|
1794
|
+
// would not recover, then we check if a different request could recover.
|
|
1795
|
+
// If a different request could not recover, then we shutdown. If a different request could
|
|
1796
|
+
// recover, then we just don't retry this specific request.
|
|
1797
|
+
if (!isHttpLocallyRecoverable(status)) {
|
|
1798
|
+
tryRes.status = LDDeliveryStatus.FailedAndMustShutDown;
|
|
1799
|
+
}
|
|
1800
|
+
else {
|
|
1801
|
+
tryRes.status = LDDeliveryStatus.Failed;
|
|
1802
|
+
}
|
|
1803
|
+
tryRes.error = error;
|
|
1804
|
+
return tryRes;
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
catch (err) {
|
|
1808
|
+
error = err;
|
|
1809
|
+
}
|
|
1810
|
+
// recoverable but not retrying
|
|
1811
|
+
if (error && !canRetry) {
|
|
1812
|
+
tryRes.status = LDDeliveryStatus.Failed;
|
|
1813
|
+
tryRes.error = error;
|
|
1814
|
+
return tryRes;
|
|
1815
|
+
}
|
|
1816
|
+
// wait 1 second before retrying
|
|
1817
|
+
await sleep();
|
|
1818
|
+
return this._tryPostingEvents(events, this._eventsUri, payloadId, false);
|
|
1819
|
+
}
|
|
1820
|
+
async sendEventData(type, data) {
|
|
1821
|
+
const payloadId = type === LDEventType.AnalyticsEvents ? this._crypto.randomUUID() : undefined;
|
|
1822
|
+
const uri = type === LDEventType.AnalyticsEvents ? this._eventsUri : this._diagnosticEventsUri;
|
|
1823
|
+
return this._tryPostingEvents(data, uri, payloadId, true);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
function isFeature(u) {
|
|
1828
|
+
return u.kind === 'feature';
|
|
1829
|
+
}
|
|
1830
|
+
function isIdentify(u) {
|
|
1831
|
+
return u.kind === 'identify';
|
|
1832
|
+
}
|
|
1833
|
+
function isMigration(u) {
|
|
1834
|
+
return u.kind === 'migration_op';
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* @internal
|
|
1839
|
+
*/
|
|
1840
|
+
class SummaryCounter {
|
|
1841
|
+
constructor(count, key, value, defValue, version, variation) {
|
|
1842
|
+
this.count = count;
|
|
1843
|
+
this.key = key;
|
|
1844
|
+
this.value = value;
|
|
1845
|
+
this.version = version;
|
|
1846
|
+
this.variation = variation;
|
|
1847
|
+
this.default = defValue;
|
|
1848
|
+
}
|
|
1849
|
+
increment() {
|
|
1850
|
+
this.count += 1;
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
function counterKey(event) {
|
|
1855
|
+
return `${event.key}:${event.variation !== null && event.variation !== undefined ? event.variation : ''}:${event.version !== null && event.version !== undefined ? event.version : ''}`;
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* @internal
|
|
1859
|
+
*/
|
|
1860
|
+
class EventSummarizer {
|
|
1861
|
+
constructor() {
|
|
1862
|
+
this._startDate = 0;
|
|
1863
|
+
this._endDate = 0;
|
|
1864
|
+
this._counters = {};
|
|
1865
|
+
this._contextKinds = {};
|
|
1866
|
+
}
|
|
1867
|
+
summarizeEvent(event) {
|
|
1868
|
+
if (isFeature(event) && !event.excludeFromSummaries) {
|
|
1869
|
+
const countKey = counterKey(event);
|
|
1870
|
+
const counter = this._counters[countKey];
|
|
1871
|
+
let kinds = this._contextKinds[event.key];
|
|
1872
|
+
if (!kinds) {
|
|
1873
|
+
kinds = new Set();
|
|
1874
|
+
this._contextKinds[event.key] = kinds;
|
|
1875
|
+
}
|
|
1876
|
+
event.context.kinds.forEach((kind) => kinds.add(kind));
|
|
1877
|
+
if (counter) {
|
|
1878
|
+
counter.increment();
|
|
1879
|
+
}
|
|
1880
|
+
else {
|
|
1881
|
+
this._counters[countKey] = new SummaryCounter(1, event.key, event.value, event.default, event.version, event.variation);
|
|
1882
|
+
}
|
|
1883
|
+
if (this._startDate === 0 || event.creationDate < this._startDate) {
|
|
1884
|
+
this._startDate = event.creationDate;
|
|
1885
|
+
}
|
|
1886
|
+
if (event.creationDate > this._endDate) {
|
|
1887
|
+
this._endDate = event.creationDate;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
getSummary() {
|
|
1892
|
+
const features = Object.values(this._counters).reduce((acc, counter) => {
|
|
1893
|
+
let flagSummary = acc[counter.key];
|
|
1894
|
+
if (!flagSummary) {
|
|
1895
|
+
flagSummary = {
|
|
1896
|
+
default: counter.default,
|
|
1897
|
+
counters: [],
|
|
1898
|
+
contextKinds: [...this._contextKinds[counter.key]],
|
|
1899
|
+
};
|
|
1900
|
+
acc[counter.key] = flagSummary;
|
|
1901
|
+
}
|
|
1902
|
+
const counterOut = {
|
|
1903
|
+
value: counter.value,
|
|
1904
|
+
count: counter.count,
|
|
1905
|
+
};
|
|
1906
|
+
if (counter.variation !== undefined && counter.variation !== null) {
|
|
1907
|
+
counterOut.variation = counter.variation;
|
|
1908
|
+
}
|
|
1909
|
+
if (counter.version !== undefined && counter.version !== null) {
|
|
1910
|
+
counterOut.version = counter.version;
|
|
1911
|
+
}
|
|
1912
|
+
else {
|
|
1913
|
+
counterOut.unknown = true;
|
|
1914
|
+
}
|
|
1915
|
+
flagSummary.counters.push(counterOut);
|
|
1916
|
+
return acc;
|
|
1917
|
+
}, {});
|
|
1918
|
+
return {
|
|
1919
|
+
startDate: this._startDate,
|
|
1920
|
+
endDate: this._endDate,
|
|
1921
|
+
features,
|
|
1922
|
+
kind: 'summary',
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
clearSummary() {
|
|
1926
|
+
this._startDate = 0;
|
|
1927
|
+
this._endDate = 0;
|
|
1928
|
+
this._counters = {};
|
|
1929
|
+
this._contextKinds = {};
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1932
|
+
|
|
1933
|
+
class LDInvalidSDKKeyError extends Error {
|
|
1934
|
+
constructor(message) {
|
|
1935
|
+
super(message);
|
|
1936
|
+
this.name = 'LaunchDarklyInvalidSDKKeyError';
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
|
|
1940
|
+
/**
|
|
1941
|
+
* The contents of this file are for event sampling. They are not used for
|
|
1942
|
+
* any purpose requiring cryptographic security.
|
|
1943
|
+
* */
|
|
1944
|
+
function shouldSample(ratio) {
|
|
1945
|
+
const truncated = Math.trunc(ratio);
|
|
1946
|
+
// A radio of 1 means 1 in 1. So that will always sample. No need
|
|
1947
|
+
// to draw a random number.
|
|
1948
|
+
if (truncated === 1) {
|
|
1949
|
+
return true;
|
|
1950
|
+
}
|
|
1951
|
+
if (truncated === 0) {
|
|
1952
|
+
return false;
|
|
1953
|
+
}
|
|
1954
|
+
// Math.random() * truncated) would return 0, 1, ... (ratio - 1).
|
|
1955
|
+
// Checking for any number in the range will have approximately a 1 in X
|
|
1956
|
+
// chance. So we check for 0 as it is part of any range.
|
|
1957
|
+
return Math.floor(Math.random() * truncated) === 0;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
class EventProcessor {
|
|
1961
|
+
constructor(_config, clientContext, baseHeaders, _contextDeduplicator, _diagnosticsManager, start = true) {
|
|
1962
|
+
this._config = _config;
|
|
1963
|
+
this._contextDeduplicator = _contextDeduplicator;
|
|
1964
|
+
this._diagnosticsManager = _diagnosticsManager;
|
|
1965
|
+
this._summarizer = new EventSummarizer();
|
|
1966
|
+
this._queue = [];
|
|
1967
|
+
this._lastKnownPastTime = 0;
|
|
1968
|
+
this._droppedEvents = 0;
|
|
1969
|
+
this._deduplicatedUsers = 0;
|
|
1970
|
+
this._exceededCapacity = false;
|
|
1971
|
+
this._eventsInLastBatch = 0;
|
|
1972
|
+
this._shutdown = false;
|
|
1973
|
+
this._flushUsersTimer = null;
|
|
1974
|
+
this._capacity = _config.eventsCapacity;
|
|
1975
|
+
this._logger = clientContext.basicConfiguration.logger;
|
|
1976
|
+
this._eventSender = new EventSender(clientContext, baseHeaders);
|
|
1977
|
+
this._contextFilter = new ContextFilter(_config.allAttributesPrivate, _config.privateAttributes.map((ref) => new AttributeReference(ref)));
|
|
1978
|
+
if (start) {
|
|
1979
|
+
this.start();
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
start() {
|
|
1983
|
+
if (this._contextDeduplicator?.flushInterval !== undefined) {
|
|
1984
|
+
this._flushUsersTimer = setInterval(() => {
|
|
1985
|
+
this._contextDeduplicator?.flush();
|
|
1986
|
+
}, this._contextDeduplicator.flushInterval * 1000);
|
|
1987
|
+
}
|
|
1988
|
+
this._flushTimer = setInterval(async () => {
|
|
1989
|
+
try {
|
|
1990
|
+
await this.flush();
|
|
1991
|
+
}
|
|
1992
|
+
catch (e) {
|
|
1993
|
+
// Log errors and swallow them
|
|
1994
|
+
this._logger?.debug(`Flush failed: ${e}`);
|
|
1995
|
+
}
|
|
1996
|
+
}, this._config.flushInterval * 1000);
|
|
1997
|
+
if (this._diagnosticsManager) {
|
|
1998
|
+
const initEvent = this._diagnosticsManager.createInitEvent();
|
|
1999
|
+
this._postDiagnosticEvent(initEvent);
|
|
2000
|
+
this._diagnosticsTimer = setInterval(() => {
|
|
2001
|
+
const statsEvent = this._diagnosticsManager.createStatsEventAndReset(this._droppedEvents, this._deduplicatedUsers, this._eventsInLastBatch);
|
|
2002
|
+
this._droppedEvents = 0;
|
|
2003
|
+
this._deduplicatedUsers = 0;
|
|
2004
|
+
this._postDiagnosticEvent(statsEvent);
|
|
2005
|
+
}, this._config.diagnosticRecordingInterval * 1000);
|
|
2006
|
+
}
|
|
2007
|
+
this._logger?.debug('Started EventProcessor.');
|
|
2008
|
+
}
|
|
2009
|
+
_postDiagnosticEvent(event) {
|
|
2010
|
+
this._eventSender.sendEventData(LDEventType.DiagnosticEvent, event);
|
|
2011
|
+
}
|
|
2012
|
+
close() {
|
|
2013
|
+
clearInterval(this._flushTimer);
|
|
2014
|
+
if (this._flushUsersTimer) {
|
|
2015
|
+
clearInterval(this._flushUsersTimer);
|
|
2016
|
+
}
|
|
2017
|
+
if (this._diagnosticsTimer) {
|
|
2018
|
+
clearInterval(this._diagnosticsTimer);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
async flush() {
|
|
2022
|
+
if (this._shutdown) {
|
|
2023
|
+
throw new LDInvalidSDKKeyError('Events cannot be posted because a permanent error has been encountered. ' +
|
|
2024
|
+
'This is most likely an invalid SDK key. The specific error information ' +
|
|
2025
|
+
'is logged independently.');
|
|
2026
|
+
}
|
|
2027
|
+
const eventsToFlush = this._queue;
|
|
2028
|
+
this._queue = [];
|
|
2029
|
+
const summary = this._summarizer.getSummary();
|
|
2030
|
+
this._summarizer.clearSummary();
|
|
2031
|
+
if (Object.keys(summary.features).length) {
|
|
2032
|
+
eventsToFlush.push(summary);
|
|
2033
|
+
}
|
|
2034
|
+
if (!eventsToFlush.length) {
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
this._eventsInLastBatch = eventsToFlush.length;
|
|
2038
|
+
this._logger?.debug('Flushing %d events', eventsToFlush.length);
|
|
2039
|
+
await this._tryPostingEvents(eventsToFlush);
|
|
2040
|
+
}
|
|
2041
|
+
sendEvent(inputEvent) {
|
|
2042
|
+
if (this._shutdown) {
|
|
2043
|
+
return;
|
|
2044
|
+
}
|
|
2045
|
+
if (isMigration(inputEvent)) {
|
|
2046
|
+
// These conditions are not combined, because we always want to stop
|
|
2047
|
+
// processing at this point for a migration event. It cannot generate
|
|
2048
|
+
// an index event or debug event.
|
|
2049
|
+
if (shouldSample(inputEvent.samplingRatio)) {
|
|
2050
|
+
const migrationEvent = {
|
|
2051
|
+
...inputEvent,
|
|
2052
|
+
};
|
|
2053
|
+
if (migrationEvent.samplingRatio === 1) {
|
|
2054
|
+
delete migrationEvent.samplingRatio;
|
|
2055
|
+
}
|
|
2056
|
+
this._enqueue(migrationEvent);
|
|
2057
|
+
}
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
this._summarizer.summarizeEvent(inputEvent);
|
|
2061
|
+
const isFeatureEvent = isFeature(inputEvent);
|
|
2062
|
+
const addFullEvent = (isFeatureEvent && inputEvent.trackEvents) || !isFeatureEvent;
|
|
2063
|
+
const addDebugEvent = this._shouldDebugEvent(inputEvent);
|
|
2064
|
+
const isIdentifyEvent = isIdentify(inputEvent);
|
|
2065
|
+
const shouldNotDeduplicate = this._contextDeduplicator?.processContext(inputEvent.context);
|
|
2066
|
+
// If there is no cache, then it will never be in the cache.
|
|
2067
|
+
if (!shouldNotDeduplicate) {
|
|
2068
|
+
if (!isIdentifyEvent) {
|
|
2069
|
+
this._deduplicatedUsers += 1;
|
|
2070
|
+
}
|
|
2071
|
+
}
|
|
2072
|
+
const addIndexEvent = shouldNotDeduplicate && !isIdentifyEvent;
|
|
2073
|
+
if (addIndexEvent) {
|
|
2074
|
+
this._enqueue(this._makeOutputEvent({
|
|
2075
|
+
kind: 'index',
|
|
2076
|
+
creationDate: inputEvent.creationDate,
|
|
2077
|
+
context: inputEvent.context,
|
|
2078
|
+
samplingRatio: 1,
|
|
2079
|
+
}, false));
|
|
2080
|
+
}
|
|
2081
|
+
if (addFullEvent && shouldSample(inputEvent.samplingRatio)) {
|
|
2082
|
+
this._enqueue(this._makeOutputEvent(inputEvent, false));
|
|
2083
|
+
}
|
|
2084
|
+
if (addDebugEvent && shouldSample(inputEvent.samplingRatio)) {
|
|
2085
|
+
this._enqueue(this._makeOutputEvent(inputEvent, true));
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
_makeOutputEvent(event, debug) {
|
|
2089
|
+
switch (event.kind) {
|
|
2090
|
+
case 'feature': {
|
|
2091
|
+
const out = {
|
|
2092
|
+
kind: debug ? 'debug' : 'feature',
|
|
2093
|
+
creationDate: event.creationDate,
|
|
2094
|
+
context: this._contextFilter.filter(event.context, !debug),
|
|
2095
|
+
key: event.key,
|
|
2096
|
+
value: event.value,
|
|
2097
|
+
default: event.default,
|
|
2098
|
+
};
|
|
2099
|
+
if (event.samplingRatio !== 1) {
|
|
2100
|
+
out.samplingRatio = event.samplingRatio;
|
|
2101
|
+
}
|
|
2102
|
+
if (event.prereqOf) {
|
|
2103
|
+
out.prereqOf = event.prereqOf;
|
|
2104
|
+
}
|
|
2105
|
+
if (event.variation !== undefined) {
|
|
2106
|
+
out.variation = event.variation;
|
|
2107
|
+
}
|
|
2108
|
+
if (event.version !== undefined) {
|
|
2109
|
+
out.version = event.version;
|
|
2110
|
+
}
|
|
2111
|
+
if (event.reason) {
|
|
2112
|
+
out.reason = event.reason;
|
|
2113
|
+
}
|
|
2114
|
+
return out;
|
|
2115
|
+
}
|
|
2116
|
+
case 'index': // Intentional fallthrough.
|
|
2117
|
+
case 'identify': {
|
|
2118
|
+
const out = {
|
|
2119
|
+
kind: event.kind,
|
|
2120
|
+
creationDate: event.creationDate,
|
|
2121
|
+
context: this._contextFilter.filter(event.context),
|
|
2122
|
+
};
|
|
2123
|
+
if (event.samplingRatio !== 1) {
|
|
2124
|
+
out.samplingRatio = event.samplingRatio;
|
|
2125
|
+
}
|
|
2126
|
+
return out;
|
|
2127
|
+
}
|
|
2128
|
+
case 'custom': {
|
|
2129
|
+
const out = {
|
|
2130
|
+
kind: 'custom',
|
|
2131
|
+
creationDate: event.creationDate,
|
|
2132
|
+
key: event.key,
|
|
2133
|
+
contextKeys: event.context.kindsAndKeys,
|
|
2134
|
+
};
|
|
2135
|
+
if (event.samplingRatio !== 1) {
|
|
2136
|
+
out.samplingRatio = event.samplingRatio;
|
|
2137
|
+
}
|
|
2138
|
+
if (event.data !== undefined) {
|
|
2139
|
+
out.data = event.data;
|
|
2140
|
+
}
|
|
2141
|
+
if (event.metricValue !== undefined) {
|
|
2142
|
+
out.metricValue = event.metricValue;
|
|
2143
|
+
}
|
|
2144
|
+
if (event.url !== undefined) {
|
|
2145
|
+
out.url = event.url;
|
|
2146
|
+
}
|
|
2147
|
+
return out;
|
|
2148
|
+
}
|
|
2149
|
+
case 'click': {
|
|
2150
|
+
const out = {
|
|
2151
|
+
kind: 'click',
|
|
2152
|
+
creationDate: event.creationDate,
|
|
2153
|
+
contextKeys: event.context.kindsAndKeys,
|
|
2154
|
+
key: event.key,
|
|
2155
|
+
url: event.url,
|
|
2156
|
+
selector: event.selector,
|
|
2157
|
+
};
|
|
2158
|
+
return out;
|
|
2159
|
+
}
|
|
2160
|
+
case 'pageview': {
|
|
2161
|
+
const out = {
|
|
2162
|
+
kind: 'pageview',
|
|
2163
|
+
creationDate: event.creationDate,
|
|
2164
|
+
contextKeys: event.context.kindsAndKeys,
|
|
2165
|
+
key: event.key,
|
|
2166
|
+
url: event.url,
|
|
2167
|
+
};
|
|
2168
|
+
return out;
|
|
2169
|
+
}
|
|
2170
|
+
default:
|
|
2171
|
+
// This would happen during the addition of a new event type to the SDK.
|
|
2172
|
+
return event;
|
|
2173
|
+
}
|
|
2174
|
+
}
|
|
2175
|
+
_enqueue(event) {
|
|
2176
|
+
if (this._queue.length < this._capacity) {
|
|
2177
|
+
this._queue.push(event);
|
|
2178
|
+
this._exceededCapacity = false;
|
|
2179
|
+
}
|
|
2180
|
+
else {
|
|
2181
|
+
if (!this._exceededCapacity) {
|
|
2182
|
+
this._exceededCapacity = true;
|
|
2183
|
+
this._logger?.warn('Exceeded event queue capacity. Increase capacity to avoid dropping events.');
|
|
2184
|
+
}
|
|
2185
|
+
this._droppedEvents += 1;
|
|
2186
|
+
}
|
|
2187
|
+
}
|
|
2188
|
+
_shouldDebugEvent(event) {
|
|
2189
|
+
return (isFeature(event) &&
|
|
2190
|
+
event.debugEventsUntilDate &&
|
|
2191
|
+
event.debugEventsUntilDate > this._lastKnownPastTime &&
|
|
2192
|
+
event.debugEventsUntilDate > Date.now());
|
|
2193
|
+
}
|
|
2194
|
+
async _tryPostingEvents(events) {
|
|
2195
|
+
const res = await this._eventSender.sendEventData(LDEventType.AnalyticsEvents, events);
|
|
2196
|
+
if (res.status === LDDeliveryStatus.FailedAndMustShutDown) {
|
|
2197
|
+
this._shutdown = true;
|
|
2198
|
+
}
|
|
2199
|
+
if (res.serverTime) {
|
|
2200
|
+
this._lastKnownPastTime = res.serverTime;
|
|
2201
|
+
}
|
|
2202
|
+
if (res.error) {
|
|
2203
|
+
throw res.error;
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
|
|
2208
|
+
class InputCustomEvent {
|
|
2209
|
+
constructor(context, key, data, metricValue,
|
|
2210
|
+
// Currently custom events are not sampled, but this is here to make the handling
|
|
2211
|
+
// code more uniform.
|
|
2212
|
+
samplingRatio = 1,
|
|
2213
|
+
// Browser SDKs can include a URL for custom events.
|
|
2214
|
+
url) {
|
|
2215
|
+
this.context = context;
|
|
2216
|
+
this.key = key;
|
|
2217
|
+
this.data = data;
|
|
2218
|
+
this.metricValue = metricValue;
|
|
2219
|
+
this.samplingRatio = samplingRatio;
|
|
2220
|
+
this.url = url;
|
|
2221
|
+
this.kind = 'custom';
|
|
2222
|
+
this.creationDate = Date.now();
|
|
2223
|
+
this.context = context;
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
class InputEvalEvent {
|
|
2228
|
+
constructor(withReasons, context, key, value, defValue, // default is a reserved keyword in this context.
|
|
2229
|
+
version, variation, trackEvents, prereqOf, reason, debugEventsUntilDate, excludeFromSummaries, samplingRatio = 1) {
|
|
2230
|
+
this.withReasons = withReasons;
|
|
2231
|
+
this.context = context;
|
|
2232
|
+
this.key = key;
|
|
2233
|
+
this.samplingRatio = samplingRatio;
|
|
2234
|
+
this.kind = 'feature';
|
|
2235
|
+
this.creationDate = Date.now();
|
|
2236
|
+
this.value = value;
|
|
2237
|
+
this.default = defValue;
|
|
2238
|
+
if (version !== undefined) {
|
|
2239
|
+
this.version = version;
|
|
2240
|
+
}
|
|
2241
|
+
if (variation !== undefined) {
|
|
2242
|
+
this.variation = variation;
|
|
2243
|
+
}
|
|
2244
|
+
if (trackEvents !== undefined) {
|
|
2245
|
+
this.trackEvents = trackEvents;
|
|
2246
|
+
}
|
|
2247
|
+
if (prereqOf !== undefined) {
|
|
2248
|
+
this.prereqOf = prereqOf;
|
|
2249
|
+
}
|
|
2250
|
+
if (reason !== undefined) {
|
|
2251
|
+
this.reason = reason;
|
|
2252
|
+
}
|
|
2253
|
+
if (debugEventsUntilDate !== undefined) {
|
|
2254
|
+
this.debugEventsUntilDate = debugEventsUntilDate;
|
|
2255
|
+
}
|
|
2256
|
+
if (excludeFromSummaries !== undefined) {
|
|
2257
|
+
this.excludeFromSummaries = excludeFromSummaries;
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2261
|
+
|
|
2262
|
+
class InputIdentifyEvent {
|
|
2263
|
+
constructor(context, samplingRatio = 1) {
|
|
2264
|
+
this.context = context;
|
|
2265
|
+
this.samplingRatio = samplingRatio;
|
|
2266
|
+
this.kind = 'identify';
|
|
2267
|
+
this.creationDate = Date.now();
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
|
|
2271
|
+
class NullEventProcessor {
|
|
2272
|
+
close() { }
|
|
2273
|
+
async flush() {
|
|
2274
|
+
// empty comment to keep ts and eslint happy
|
|
2275
|
+
}
|
|
2276
|
+
sendEvent() { }
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
class EventFactoryBase {
|
|
2280
|
+
constructor(_withReasons) {
|
|
2281
|
+
this._withReasons = _withReasons;
|
|
2282
|
+
}
|
|
2283
|
+
evalEvent(e) {
|
|
2284
|
+
return new InputEvalEvent(this._withReasons, e.context, e.flagKey, e.value, e.defaultVal, e.version,
|
|
2285
|
+
// Exclude null as a possibility.
|
|
2286
|
+
e.variation ?? undefined, e.trackEvents || e.addExperimentData, e.prereqOfFlagKey, this._withReasons || e.addExperimentData ? e.reason : undefined, e.debugEventsUntilDate, e.excludeFromSummaries, e.samplingRatio);
|
|
2287
|
+
}
|
|
2288
|
+
unknownFlagEvent(key, defVal, context) {
|
|
2289
|
+
return new InputEvalEvent(this._withReasons, context, key, defVal, defVal,
|
|
2290
|
+
// This isn't ideal, but the purpose of the factory is to at least
|
|
2291
|
+
// handle this situation.
|
|
2292
|
+
undefined, // version
|
|
2293
|
+
undefined, // variation index
|
|
2294
|
+
undefined, // track events
|
|
2295
|
+
undefined, // prereqOf
|
|
2296
|
+
undefined, // reason
|
|
2297
|
+
undefined, // debugEventsUntilDate
|
|
2298
|
+
undefined, // exclude from summaries
|
|
2299
|
+
undefined);
|
|
2300
|
+
}
|
|
2301
|
+
/* eslint-disable-next-line class-methods-use-this */
|
|
2302
|
+
identifyEvent(context) {
|
|
2303
|
+
// Currently sampling for identify events is always 1.
|
|
2304
|
+
return new InputIdentifyEvent(context, 1);
|
|
2305
|
+
}
|
|
2306
|
+
/* eslint-disable-next-line class-methods-use-this */
|
|
2307
|
+
customEvent(key, context, data, metricValue, samplingRatio = 1) {
|
|
2308
|
+
return new InputCustomEvent(context, key, data ?? undefined, metricValue ?? undefined, samplingRatio);
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
const reportJsonError = (type, data, logger, errorHandler) => {
|
|
2313
|
+
logger?.error(`Stream received invalid data in "${type}" message`);
|
|
2314
|
+
logger?.debug(`Invalid JSON follows: ${data}`);
|
|
2315
|
+
errorHandler?.(new LDStreamingError(DataSourceErrorKind.InvalidData, 'Malformed JSON data in event stream'));
|
|
2316
|
+
};
|
|
2317
|
+
// TODO: SDK-156 - Move to Server SDK specific location
|
|
2318
|
+
class StreamingProcessor {
|
|
2319
|
+
constructor(clientContext, streamUriPath, parameters, _listeners, baseHeaders, _diagnosticsManager, _errorHandler, _streamInitialReconnectDelay = 1) {
|
|
2320
|
+
this._listeners = _listeners;
|
|
2321
|
+
this._diagnosticsManager = _diagnosticsManager;
|
|
2322
|
+
this._errorHandler = _errorHandler;
|
|
2323
|
+
this._streamInitialReconnectDelay = _streamInitialReconnectDelay;
|
|
2324
|
+
const { basicConfiguration, platform } = clientContext;
|
|
2325
|
+
const { logger } = basicConfiguration;
|
|
2326
|
+
const { requests } = platform;
|
|
2327
|
+
this._headers = { ...baseHeaders };
|
|
2328
|
+
this._logger = logger;
|
|
2329
|
+
this._requests = requests;
|
|
2330
|
+
this._streamUri = getStreamingUri(basicConfiguration.serviceEndpoints, streamUriPath, parameters);
|
|
2331
|
+
}
|
|
2332
|
+
_logConnectionStarted() {
|
|
2333
|
+
this._connectionAttemptStartTime = Date.now();
|
|
2334
|
+
}
|
|
2335
|
+
_logConnectionResult(success) {
|
|
2336
|
+
if (this._connectionAttemptStartTime && this._diagnosticsManager) {
|
|
2337
|
+
this._diagnosticsManager.recordStreamInit(this._connectionAttemptStartTime, !success, Date.now() - this._connectionAttemptStartTime);
|
|
2338
|
+
}
|
|
2339
|
+
this._connectionAttemptStartTime = undefined;
|
|
2340
|
+
}
|
|
2341
|
+
/**
|
|
2342
|
+
* This is a wrapper around the passed errorHandler which adds additional
|
|
2343
|
+
* diagnostics and logging logic.
|
|
2344
|
+
*
|
|
2345
|
+
* @param err The error to be logged and handled.
|
|
2346
|
+
* @return boolean whether to retry the connection.
|
|
2347
|
+
*
|
|
2348
|
+
* @private
|
|
2349
|
+
*/
|
|
2350
|
+
_retryAndHandleError(err) {
|
|
2351
|
+
if (!shouldRetry(err)) {
|
|
2352
|
+
this._logConnectionResult(false);
|
|
2353
|
+
this._errorHandler?.(new LDStreamingError(DataSourceErrorKind.ErrorResponse, err.message, err.status));
|
|
2354
|
+
this._logger?.error(httpErrorMessage(err, 'streaming request'));
|
|
2355
|
+
return false;
|
|
2356
|
+
}
|
|
2357
|
+
this._logger?.warn(httpErrorMessage(err, 'streaming request', 'will retry'));
|
|
2358
|
+
this._logConnectionResult(false);
|
|
2359
|
+
this._logConnectionStarted();
|
|
2360
|
+
return true;
|
|
2361
|
+
}
|
|
2362
|
+
start() {
|
|
2363
|
+
this._logConnectionStarted();
|
|
2364
|
+
// TLS is handled by the platform implementation.
|
|
2365
|
+
const eventSource = this._requests.createEventSource(this._streamUri, {
|
|
2366
|
+
headers: this._headers,
|
|
2367
|
+
errorFilter: (error) => this._retryAndHandleError(error),
|
|
2368
|
+
initialRetryDelayMillis: 1000 * this._streamInitialReconnectDelay,
|
|
2369
|
+
readTimeoutMillis: 5 * 60 * 1000,
|
|
2370
|
+
retryResetIntervalMillis: 60 * 1000,
|
|
2371
|
+
});
|
|
2372
|
+
this._eventSource = eventSource;
|
|
2373
|
+
eventSource.onclose = () => {
|
|
2374
|
+
this._logger?.info('Closed LaunchDarkly stream connection');
|
|
2375
|
+
};
|
|
2376
|
+
eventSource.onerror = () => {
|
|
2377
|
+
// The work is done by `errorFilter`.
|
|
2378
|
+
};
|
|
2379
|
+
eventSource.onopen = () => {
|
|
2380
|
+
this._logger?.info('Opened LaunchDarkly stream connection');
|
|
2381
|
+
};
|
|
2382
|
+
eventSource.onretrying = (e) => {
|
|
2383
|
+
this._logger?.info(`Will retry stream connection in ${e.delayMillis} milliseconds`);
|
|
2384
|
+
};
|
|
2385
|
+
this._listeners.forEach(({ deserializeData, processJson }, eventName) => {
|
|
2386
|
+
eventSource.addEventListener(eventName, (event) => {
|
|
2387
|
+
this._logger?.debug(`Received ${eventName} event`);
|
|
2388
|
+
if (event?.data) {
|
|
2389
|
+
this._logConnectionResult(true);
|
|
2390
|
+
const { data } = event;
|
|
2391
|
+
const dataJson = deserializeData(data);
|
|
2392
|
+
if (!dataJson) {
|
|
2393
|
+
reportJsonError(eventName, data, this._logger, this._errorHandler);
|
|
2394
|
+
return;
|
|
2395
|
+
}
|
|
2396
|
+
processJson(dataJson);
|
|
2397
|
+
}
|
|
2398
|
+
else {
|
|
2399
|
+
this._errorHandler?.(new LDStreamingError(DataSourceErrorKind.Unknown, 'Unexpected payload from event stream'));
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
});
|
|
2403
|
+
}
|
|
2404
|
+
stop() {
|
|
2405
|
+
this._eventSource?.close();
|
|
2406
|
+
this._eventSource = undefined;
|
|
2407
|
+
}
|
|
2408
|
+
close() {
|
|
2409
|
+
this.stop();
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
|
|
2413
|
+
var index = /*#__PURE__*/Object.freeze({
|
|
2414
|
+
__proto__: null,
|
|
2415
|
+
ClientMessages: ClientMessages,
|
|
2416
|
+
DiagnosticsManager: DiagnosticsManager,
|
|
2417
|
+
ErrorKinds: ErrorKinds$1,
|
|
2418
|
+
EventFactoryBase: EventFactoryBase,
|
|
2419
|
+
EventProcessor: EventProcessor,
|
|
2420
|
+
InputCustomEvent: InputCustomEvent,
|
|
2421
|
+
InputEvalEvent: InputEvalEvent,
|
|
2422
|
+
InputIdentifyEvent: InputIdentifyEvent,
|
|
2423
|
+
NullEventProcessor: NullEventProcessor,
|
|
2424
|
+
StreamingProcessor: StreamingProcessor,
|
|
2425
|
+
isLegacyUser: isLegacyUser,
|
|
2426
|
+
isMultiKind: isMultiKind,
|
|
2427
|
+
isSingleKind: isSingleKind,
|
|
2428
|
+
shouldSample: shouldSample
|
|
2429
|
+
});
|
|
2430
|
+
|
|
2431
|
+
export { ApplicationTags, AttributeReference, AutoEnvAttributes, BasicLogger, ClientContext, Context, ContextFilter, DataSourceErrorKind, DateValidator, FactoryOrInstance, Function, KindValidator, LDClientError, LDFileDataSourceError, LDPollingError, LDStreamingError, LDTimeoutError, LDUnexpectedResponseError, NullableBoolean, NumberWithMinimum, OptionMessages, SafeLogger, ServiceEndpoints, StringMatchingRegex, Type, TypeArray, TypeValidators, base64UrlEncode, cancelableTimedPromise, clone, createSafeLogger, debounce, deepCompact, defaultHeaders, fastDeepEqual, getEventsUri, getPollingUri, getStreamingUri, httpErrorMessage, index as internal, isHttpLocallyRecoverable, isHttpRecoverable, noop, secondsToMillis, shouldRetry, sleep, index$1 as subsystem, timedPromise };
|
|
2432
|
+
//# sourceMappingURL=index.mjs.map
|