@newrelic/browser-agent 0.1.230 → 1.232.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -3
- package/dist/cjs/common/browser-version/ios-version.js +4 -3
- package/dist/cjs/common/config/state/configurable.js +27 -21
- package/dist/cjs/common/config/state/info.js +1 -1
- package/dist/cjs/common/config/state/init.js +9 -1
- package/dist/cjs/common/config/state/loader-config.js +1 -1
- package/dist/cjs/common/config/state/runtime.js +26 -28
- package/dist/cjs/common/constants/env.cdn.js +29 -0
- package/dist/cjs/common/constants/env.js +32 -0
- package/dist/cjs/common/constants/env.npm.js +30 -0
- package/dist/cjs/common/context/shared-context.js +2 -1
- package/dist/cjs/common/event-emitter/contextual-ee.test.js +282 -0
- package/dist/cjs/common/event-emitter/handle.test.js +58 -0
- package/dist/cjs/common/event-emitter/register-handler.test.js +55 -0
- package/dist/cjs/common/event-listener/event-listener-opts.js +4 -2
- package/dist/cjs/common/harvest/harvest-scheduler.js +14 -11
- package/dist/cjs/common/harvest/harvest.js +5 -3
- package/dist/cjs/common/ids/id.js +14 -6
- package/dist/cjs/common/ids/id.test.js +85 -0
- package/dist/cjs/common/ids/unique-id.js +75 -51
- package/dist/cjs/common/ids/unique-id.test.js +49 -0
- package/dist/cjs/common/session/constants.js +12 -0
- package/dist/cjs/common/session/session-entity.js +278 -0
- package/dist/cjs/common/session/session-entity.test.js +436 -0
- package/dist/cjs/common/storage/first-party-cookies.js +35 -0
- package/dist/cjs/common/storage/local-memory.js +35 -0
- package/dist/cjs/common/storage/local-memory.test.js +20 -0
- package/dist/cjs/common/storage/local-storage.js +33 -0
- package/dist/cjs/common/storage/local-storage.test.js +14 -0
- package/dist/cjs/common/timer/interaction-timer.js +78 -0
- package/dist/cjs/common/timer/interaction-timer.test.js +216 -0
- package/dist/cjs/common/timer/timer.js +32 -0
- package/dist/cjs/common/timer/timer.test.js +105 -0
- package/dist/cjs/common/timing/nav-timing.js +51 -30
- package/dist/cjs/common/timing/nav-timing.test.js +192 -0
- package/dist/cjs/common/unload/eol.js +2 -2
- package/dist/cjs/common/url/clean-url.test.js +9 -0
- package/dist/cjs/common/url/encode.test.js +74 -0
- package/dist/cjs/common/url/location.js +4 -0
- package/dist/cjs/common/url/location.test.js +13 -0
- package/dist/cjs/common/url/parse-url.test.js +111 -0
- package/dist/cjs/common/url/protocol.js +2 -12
- package/dist/cjs/common/url/protocol.test.js +16 -0
- package/dist/cjs/common/util/console.js +1 -1
- package/dist/cjs/common/util/data-size.js +6 -0
- package/dist/cjs/common/util/data-size.test.js +47 -0
- package/dist/cjs/common/util/invoke.js +73 -0
- package/dist/cjs/common/util/invoke.test.js +49 -0
- package/dist/cjs/common/util/map-own.test.js +3 -3
- package/dist/cjs/common/util/obfuscate.js +1 -5
- package/dist/cjs/common/window/page-visibility.js +5 -2
- package/dist/cjs/common/wrap/index.js +0 -7
- package/dist/cjs/common/wrap/wrap-events.js +6 -9
- package/dist/cjs/common/wrap/wrap-fetch.js +6 -6
- package/dist/cjs/common/wrap/wrap-history.js +7 -6
- package/dist/cjs/common/wrap/wrap-jsonp.js +7 -6
- package/dist/cjs/common/wrap/wrap-mutation.js +7 -6
- package/dist/cjs/common/wrap/wrap-promise.js +7 -6
- package/dist/cjs/common/wrap/wrap-promise.test.js +119 -0
- package/dist/cjs/common/wrap/wrap-raf.js +6 -6
- package/dist/cjs/common/wrap/wrap-timer.js +7 -7
- package/dist/cjs/common/wrap/wrap-xhr.js +5 -6
- package/dist/cjs/features/ajax/aggregate/index.js +3 -3
- package/dist/cjs/features/jserrors/aggregate/compute-stack-trace.test.js +5 -5
- package/dist/cjs/features/jserrors/aggregate/format-stack-trace.test.js +1 -1
- package/dist/cjs/features/jserrors/aggregate/index.js +6 -6
- package/dist/cjs/features/jserrors/instrument/index.js +2 -2
- package/dist/cjs/features/metrics/aggregate/index.js +19 -9
- package/dist/cjs/features/metrics/instrument/index.js +0 -25
- package/dist/cjs/features/metrics/instrument/workers-helper.js +5 -5
- package/dist/cjs/features/page_action/aggregate/index.js +3 -3
- package/dist/cjs/features/page_view_event/aggregate/index.js +23 -9
- package/dist/cjs/features/page_view_timing/aggregate/index.js +40 -30
- package/dist/cjs/features/session_trace/aggregate/index.js +18 -15
- package/dist/cjs/features/spa/aggregate/index.js +6 -5
- package/dist/cjs/features/utils/agent-session.js +73 -0
- package/dist/cjs/features/utils/feature-base.js +1 -1
- package/dist/cjs/features/utils/instrument-base.js +12 -3
- package/dist/cjs/features/utils/lazy-loader.js +2 -2
- package/dist/cjs/loaders/agent.js +2 -2
- package/dist/cjs/loaders/api/api.js +9 -9
- package/dist/cjs/loaders/api/apiAsync.js +3 -2
- package/dist/cjs/loaders/configure/configure.js +0 -6
- package/dist/cjs/loaders/features/enabled-features.js +1 -1
- package/dist/cjs/loaders/micro-agent.js +2 -1
- package/dist/esm/common/browser-version/ios-version.js +4 -3
- package/dist/esm/common/config/state/configurable.js +26 -20
- package/dist/esm/common/config/state/info.js +1 -1
- package/dist/esm/common/config/state/init.js +9 -1
- package/dist/esm/common/config/state/loader-config.js +1 -1
- package/dist/esm/common/config/state/runtime.js +26 -28
- package/dist/esm/common/constants/env.cdn.js +20 -0
- package/dist/esm/common/constants/env.js +23 -0
- package/dist/esm/common/constants/env.npm.js +21 -0
- package/dist/esm/common/context/shared-context.js +2 -1
- package/dist/esm/common/event-emitter/contextual-ee.test.js +278 -0
- package/dist/esm/common/event-emitter/handle.test.js +54 -0
- package/dist/esm/common/event-emitter/register-handler.test.js +51 -0
- package/dist/esm/common/event-listener/event-listener-opts.js +4 -2
- package/dist/esm/common/harvest/harvest-scheduler.js +14 -11
- package/dist/esm/common/harvest/harvest.js +4 -2
- package/dist/esm/common/ids/id.js +16 -6
- package/dist/esm/common/ids/id.test.js +81 -0
- package/dist/esm/common/ids/unique-id.js +75 -51
- package/dist/esm/common/ids/unique-id.test.js +44 -0
- package/dist/esm/common/session/constants.js +3 -0
- package/dist/esm/common/session/session-entity.js +271 -0
- package/dist/esm/common/session/session-entity.test.js +434 -0
- package/dist/esm/common/storage/first-party-cookies.js +28 -0
- package/dist/esm/common/storage/local-memory.js +28 -0
- package/dist/esm/common/storage/local-memory.test.js +18 -0
- package/dist/esm/common/storage/local-storage.js +26 -0
- package/dist/esm/common/storage/local-storage.test.js +12 -0
- package/dist/esm/common/timer/interaction-timer.js +71 -0
- package/dist/esm/common/timer/interaction-timer.test.js +214 -0
- package/dist/esm/common/timer/timer.js +25 -0
- package/dist/esm/common/timer/timer.test.js +103 -0
- package/dist/esm/common/timing/nav-timing.js +51 -29
- package/dist/esm/common/timing/nav-timing.test.js +190 -0
- package/dist/esm/common/unload/eol.js +1 -1
- package/dist/esm/common/url/clean-url.test.js +7 -0
- package/dist/esm/common/url/encode.test.js +70 -0
- package/dist/esm/common/url/location.js +4 -0
- package/dist/esm/common/url/location.test.js +11 -0
- package/dist/esm/common/url/parse-url.test.js +107 -0
- package/dist/esm/common/url/protocol.js +3 -12
- package/dist/esm/common/url/protocol.test.js +14 -0
- package/dist/esm/common/util/console.js +1 -1
- package/dist/esm/common/util/data-size.js +7 -0
- package/dist/esm/common/util/data-size.test.js +45 -0
- package/dist/esm/common/util/invoke.js +66 -0
- package/dist/esm/common/util/invoke.test.js +47 -0
- package/dist/esm/common/util/map-own.test.js +3 -3
- package/dist/esm/common/util/obfuscate.js +2 -6
- package/dist/esm/common/window/page-visibility.js +5 -2
- package/dist/esm/common/wrap/index.js +1 -2
- package/dist/esm/common/wrap/wrap-events.js +6 -9
- package/dist/esm/common/wrap/wrap-fetch.js +6 -6
- package/dist/esm/common/wrap/wrap-history.js +7 -6
- package/dist/esm/common/wrap/wrap-jsonp.js +7 -6
- package/dist/esm/common/wrap/wrap-mutation.js +7 -6
- package/dist/esm/common/wrap/wrap-promise.js +7 -6
- package/dist/esm/common/wrap/wrap-promise.test.js +115 -0
- package/dist/esm/common/wrap/wrap-raf.js +6 -6
- package/dist/esm/common/wrap/wrap-timer.js +7 -7
- package/dist/esm/common/wrap/wrap-xhr.js +5 -6
- package/dist/esm/features/ajax/aggregate/index.js +3 -3
- package/dist/esm/features/jserrors/aggregate/compute-stack-trace.test.js +5 -5
- package/dist/esm/features/jserrors/aggregate/format-stack-trace.test.js +1 -1
- package/dist/esm/features/jserrors/aggregate/index.js +6 -6
- package/dist/esm/features/jserrors/instrument/index.js +2 -2
- package/dist/esm/features/metrics/aggregate/index.js +21 -11
- package/dist/esm/features/metrics/instrument/index.js +0 -25
- package/dist/esm/features/metrics/instrument/workers-helper.js +5 -5
- package/dist/esm/features/page_action/aggregate/index.js +3 -3
- package/dist/esm/features/page_view_event/aggregate/index.js +23 -9
- package/dist/esm/features/page_view_timing/aggregate/index.js +40 -30
- package/dist/esm/features/session_trace/aggregate/index.js +18 -15
- package/dist/esm/features/spa/aggregate/index.js +6 -5
- package/dist/esm/features/utils/agent-session.js +67 -0
- package/dist/esm/features/utils/feature-base.js +1 -1
- package/dist/esm/features/utils/instrument-base.js +7 -2
- package/dist/esm/features/utils/lazy-loader.js +2 -2
- package/dist/esm/loaders/agent.js +2 -2
- package/dist/esm/loaders/api/api.js +6 -9
- package/dist/esm/loaders/api/apiAsync.js +2 -1
- package/dist/esm/loaders/configure/configure.js +2 -8
- package/dist/esm/loaders/features/enabled-features.js +1 -1
- package/dist/types/common/config/state/configurable.d.ts.map +1 -1
- package/dist/types/common/config/state/init.d.ts.map +1 -1
- package/dist/types/common/config/state/runtime.d.ts.map +1 -1
- package/dist/types/common/constants/env.cdn.d.ts +18 -0
- package/dist/types/common/constants/env.cdn.d.ts.map +1 -0
- package/dist/types/common/constants/env.d.ts +13 -0
- package/dist/types/common/constants/env.d.ts.map +1 -0
- package/dist/types/common/constants/env.npm.d.ts +19 -0
- package/dist/types/common/constants/env.npm.d.ts.map +1 -0
- package/dist/types/common/context/shared-context.d.ts.map +1 -1
- package/dist/types/common/event-listener/event-listener-opts.d.ts +2 -2
- package/dist/types/common/event-listener/event-listener-opts.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest-scheduler.d.ts +1 -0
- package/dist/types/common/harvest/harvest-scheduler.d.ts.map +1 -1
- package/dist/types/common/harvest/harvest.d.ts.map +1 -1
- package/dist/types/common/ids/id.d.ts +11 -1
- package/dist/types/common/ids/id.d.ts.map +1 -1
- package/dist/types/common/ids/unique-id.d.ts +24 -1
- package/dist/types/common/ids/unique-id.d.ts.map +1 -1
- package/dist/types/common/session/constants.d.ts +4 -0
- package/dist/types/common/session/constants.d.ts.map +1 -0
- package/dist/types/common/session/session-entity.d.ts +72 -0
- package/dist/types/common/session/session-entity.d.ts.map +1 -0
- package/dist/types/common/storage/first-party-cookies.d.ts +8 -0
- package/dist/types/common/storage/first-party-cookies.d.ts.map +1 -0
- package/dist/types/common/storage/local-memory.d.ts +8 -0
- package/dist/types/common/storage/local-memory.d.ts.map +1 -0
- package/dist/types/common/storage/local-storage.d.ts +6 -0
- package/dist/types/common/storage/local-storage.d.ts.map +1 -0
- package/dist/types/common/timer/interaction-timer.d.ts +11 -0
- package/dist/types/common/timer/interaction-timer.d.ts.map +1 -0
- package/dist/types/common/timer/timer.d.ts +12 -0
- package/dist/types/common/timer/timer.d.ts.map +1 -0
- package/dist/types/common/timing/nav-timing.d.ts +1 -2
- package/dist/types/common/timing/nav-timing.d.ts.map +1 -1
- package/dist/types/common/unload/eol.d.ts.map +1 -1
- package/dist/types/common/url/location.d.ts +4 -0
- package/dist/types/common/url/location.d.ts.map +1 -1
- package/dist/types/common/url/parse-url.d.ts.map +1 -1
- package/dist/types/common/url/protocol.d.ts +1 -6
- package/dist/types/common/url/protocol.d.ts.map +1 -1
- package/dist/types/common/util/data-size.d.ts +7 -1
- package/dist/types/common/util/data-size.d.ts.map +1 -1
- package/dist/types/common/util/global-scope.d.ts.map +1 -1
- package/dist/types/common/util/invoke.d.ts +35 -0
- package/dist/types/common/util/invoke.d.ts.map +1 -0
- package/dist/types/common/util/obfuscate.d.ts.map +1 -1
- package/dist/types/common/window/page-visibility.d.ts +1 -1
- package/dist/types/common/window/page-visibility.d.ts.map +1 -1
- package/dist/types/common/wrap/index.d.ts +1 -2
- package/dist/types/common/wrap/index.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-fetch.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-history.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-jsonp.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-mutation.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-promise.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-raf.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-timer.d.ts.map +1 -1
- package/dist/types/common/wrap/wrap-xhr.d.ts.map +1 -1
- package/dist/types/features/ajax/aggregate/index.d.ts +2 -2
- package/dist/types/features/ajax/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/aggregate/index.d.ts +2 -2
- package/dist/types/features/jserrors/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/jserrors/instrument/index.d.ts.map +1 -1
- package/dist/types/features/metrics/aggregate/index.d.ts +2 -2
- package/dist/types/features/metrics/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/metrics/instrument/index.d.ts +0 -1
- package/dist/types/features/metrics/instrument/index.d.ts.map +1 -1
- package/dist/types/features/metrics/instrument/workers-helper.d.ts.map +1 -1
- package/dist/types/features/page_action/aggregate/index.d.ts +2 -2
- package/dist/types/features/page_action/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_event/aggregate/index.d.ts +2 -2
- package/dist/types/features/page_view_event/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/page_view_timing/aggregate/index.d.ts +3 -4
- package/dist/types/features/page_view_timing/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/aggregate/index.d.ts +3 -3
- package/dist/types/features/session_trace/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/session_trace/instrument/index.d.ts.map +1 -1
- package/dist/types/features/spa/aggregate/index.d.ts +2 -2
- package/dist/types/features/spa/aggregate/index.d.ts.map +1 -1
- package/dist/types/features/spa/instrument/index.d.ts.map +1 -1
- package/dist/types/features/utils/agent-session.d.ts +2 -0
- package/dist/types/features/utils/agent-session.d.ts.map +1 -0
- package/dist/types/features/utils/instrument-base.d.ts.map +1 -1
- package/dist/types/features/utils/lazy-loader.d.ts +2 -2
- package/dist/types/features/utils/lazy-loader.d.ts.map +1 -1
- package/dist/types/loaders/api/api.d.ts.map +1 -1
- package/dist/types/loaders/api/apiAsync.d.ts.map +1 -1
- package/dist/types/loaders/configure/configure.d.ts.map +1 -1
- package/package.json +12 -32
- package/src/common/browser-version/ios-version.js +4 -3
- package/src/common/config/state/configurable.js +26 -19
- package/src/common/config/state/init.js +7 -0
- package/src/common/config/state/runtime.js +7 -10
- package/src/common/constants/env.cdn.js +20 -0
- package/src/common/constants/env.js +23 -0
- package/src/common/constants/env.npm.js +21 -0
- package/src/common/context/shared-context.js +2 -1
- package/src/common/event-emitter/contextual-ee.test.js +310 -0
- package/src/common/event-emitter/handle.test.js +56 -0
- package/src/common/event-emitter/register-handler.test.js +61 -0
- package/src/common/event-listener/event-listener-opts.js +4 -4
- package/src/common/harvest/harvest-scheduler.js +12 -8
- package/src/common/harvest/harvest.js +5 -3
- package/src/common/ids/id.js +15 -6
- package/src/common/ids/id.test.js +92 -0
- package/src/common/ids/unique-id.js +77 -54
- package/src/common/ids/unique-id.test.js +58 -0
- package/src/common/session/constants.js +3 -0
- package/src/common/session/session-entity.js +271 -0
- package/src/common/session/session-entity.test.js +317 -0
- package/src/common/storage/first-party-cookies.js +31 -0
- package/src/common/storage/local-memory.js +30 -0
- package/src/common/storage/local-memory.test.js +19 -0
- package/src/common/storage/local-storage.js +28 -0
- package/src/common/storage/local-storage.test.js +17 -0
- package/src/common/timer/interaction-timer.js +75 -0
- package/src/common/timer/interaction-timer.test.js +167 -0
- package/src/common/timer/timer.js +31 -0
- package/src/common/timer/timer.test.js +100 -0
- package/src/common/timing/nav-timing.js +50 -30
- package/src/common/timing/nav-timing.test.js +161 -0
- package/src/common/unload/eol.js +2 -3
- package/src/common/url/clean-url.test.js +25 -0
- package/src/common/url/encode.test.js +80 -0
- package/src/common/url/location.js +4 -0
- package/src/common/url/location.test.js +15 -0
- package/src/common/url/parse-url.js +1 -2
- package/src/common/url/parse-url.test.js +110 -0
- package/src/common/url/protocol.js +3 -13
- package/src/common/url/protocol.test.js +18 -0
- package/src/common/util/data-size.js +6 -0
- package/src/common/util/data-size.test.js +50 -0
- package/src/common/util/global-scope.js +1 -2
- package/src/common/util/invoke.js +55 -0
- package/src/common/util/invoke.test.js +65 -0
- package/src/common/util/obfuscate.js +2 -6
- package/src/common/window/page-visibility.js +3 -3
- package/src/common/wrap/index.js +1 -2
- package/src/common/wrap/wrap-events.js +5 -5
- package/src/common/wrap/wrap-fetch.js +4 -3
- package/src/common/wrap/wrap-history.js +6 -3
- package/src/common/wrap/wrap-jsonp.js +5 -3
- package/src/common/wrap/wrap-mutation.js +6 -3
- package/src/common/wrap/wrap-promise.js +7 -6
- package/src/common/wrap/wrap-promise.test.js +140 -0
- package/src/common/wrap/wrap-raf.js +5 -3
- package/src/common/wrap/wrap-timer.js +6 -4
- package/src/common/wrap/wrap-xhr.js +4 -3
- package/src/features/ajax/aggregate/index.js +2 -2
- package/src/features/ajax/instrument/index.js +1 -1
- package/src/features/jserrors/aggregate/index.js +3 -3
- package/src/features/jserrors/instrument/index.js +4 -2
- package/src/features/metrics/aggregate/index.js +21 -7
- package/src/features/metrics/instrument/index.js +0 -30
- package/src/features/metrics/instrument/workers-helper.js +9 -6
- package/src/features/page_action/aggregate/index.js +2 -2
- package/src/features/page_view_event/aggregate/index.js +21 -9
- package/src/features/page_view_timing/aggregate/index.js +40 -29
- package/src/features/page_view_timing/long-tasks.js +10 -10
- package/src/features/session_trace/aggregate/index.js +17 -14
- package/src/features/session_trace/instrument/index.js +3 -2
- package/src/features/spa/aggregate/index.js +5 -5
- package/src/features/spa/instrument/index.js +4 -2
- package/src/features/utils/agent-session.js +68 -0
- package/src/features/utils/feature-base.js +1 -1
- package/src/features/utils/instrument-base.js +5 -2
- package/src/features/utils/lazy-loader.js +1 -1
- package/src/loaders/agent.js +1 -1
- package/src/loaders/api/api.js +3 -6
- package/src/loaders/api/apiAsync.js +2 -1
- package/src/loaders/configure/configure.js +2 -7
- package/dist/cjs/common/constants/environment-variables.js +0 -20
- package/dist/cjs/common/util/single.js +0 -23
- package/dist/cjs/common/window/session-storage.js +0 -87
- package/dist/cjs/common/wrap/wrap-console.js +0 -54
- package/dist/cjs/features/utils/aggregate-base.js +0 -13
- package/dist/esm/common/constants/environment-variables.js +0 -11
- package/dist/esm/common/util/single.js +0 -16
- package/dist/esm/common/window/session-storage.js +0 -77
- package/dist/esm/common/wrap/wrap-console.js +0 -46
- package/dist/esm/features/utils/aggregate-base.js +0 -6
- package/dist/types/common/constants/environment-variables.d.ts +0 -4
- package/dist/types/common/constants/environment-variables.d.ts.map +0 -1
- package/dist/types/common/util/single.d.ts +0 -2
- package/dist/types/common/util/single.d.ts.map +0 -1
- package/dist/types/common/window/session-storage.d.ts +0 -18
- package/dist/types/common/window/session-storage.d.ts.map +0 -1
- package/dist/types/common/wrap/wrap-console.d.ts +0 -16
- package/dist/types/common/wrap/wrap-console.d.ts.map +0 -1
- package/dist/types/features/utils/aggregate-base.d.ts +0 -4
- package/dist/types/features/utils/aggregate-base.d.ts.map +0 -1
- package/src/common/constants/environment-variables.js +0 -11
- package/src/common/util/single.js +0 -18
- package/src/common/window/session-storage.js +0 -75
- package/src/common/wrap/wrap-console.js +0 -47
- package/src/features/utils/aggregate-base.js +0 -7
|
@@ -4,72 +4,96 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { globalScope } from '../util/global-scope';
|
|
7
|
+
const uuidv4Template = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Creates a random single hexadecimal value from a provided random value
|
|
11
|
+
* table and corresponding index. If a random value table is not provided,
|
|
12
|
+
* Math.random will be used to generate the value.
|
|
13
|
+
*
|
|
14
|
+
* @param {Uint8Array} valueTable Random value table typically generated using
|
|
15
|
+
* the built-in crypto engine.
|
|
16
|
+
* @param {int} tableIndex The index of the value table to use for generating
|
|
17
|
+
* the hexadecimal value.
|
|
18
|
+
* @returns {int} single hexadecimal value in decimal format
|
|
19
|
+
*/
|
|
20
|
+
function getRandomValue(valueTable, tableIndex) {
|
|
21
|
+
if (valueTable) {
|
|
22
|
+
/**
|
|
23
|
+
* The value table could have any number value in the given index. Use
|
|
24
|
+
* bitwise AND to ensure the value we generate is a valid hex value.
|
|
25
|
+
* x & 15 will ensure the value converted to hex using `toString(16)`
|
|
26
|
+
* falls within the range of 0 and 15 inclusively.
|
|
27
|
+
*/
|
|
28
|
+
return valueTable[tableIndex] & 15;
|
|
29
|
+
} else {
|
|
30
|
+
return Math.random() * 16 | 0;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generates a RFC compliant UUIDv4 using native browser crypto engine. If the browser
|
|
36
|
+
* does not support the crypto engine, the function will fallback to insecure Math.random()
|
|
37
|
+
* @returns {string} uuid version 4 string
|
|
38
|
+
*/
|
|
7
39
|
export function generateUuid() {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
40
|
+
const crypto = globalScope?.crypto || globalScope?.msCrypto;
|
|
41
|
+
let randomValueTable;
|
|
42
|
+
let randomValueIndex = 0;
|
|
11
43
|
if (crypto && crypto.getRandomValues) {
|
|
12
44
|
// eslint-disable-next-line
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
function getRandomValue() {
|
|
16
|
-
if (randomVals) {
|
|
17
|
-
// same as % 16
|
|
18
|
-
return randomVals[rvIndex++] & 15;
|
|
19
|
-
} else {
|
|
20
|
-
return Math.random() * 16 | 0;
|
|
21
|
-
}
|
|
45
|
+
randomValueTable = crypto.getRandomValues(new Uint8Array(31));
|
|
22
46
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
var c;
|
|
28
|
-
for (var i = 0; i < template.length; i++) {
|
|
29
|
-
c = template[i];
|
|
30
|
-
if (c === 'x') {
|
|
31
|
-
id += getRandomValue().toString(16);
|
|
32
|
-
} else if (c === 'y') {
|
|
47
|
+
return uuidv4Template.split('').map(templateInput => {
|
|
48
|
+
if (templateInput === 'x') {
|
|
49
|
+
return getRandomValue(randomValueTable, ++randomValueIndex).toString(16);
|
|
50
|
+
} else if (templateInput === 'y') {
|
|
33
51
|
// this is the uuid variant per spec (8, 9, a, b)
|
|
34
52
|
// % 4, then shift to get values 8-11
|
|
35
|
-
|
|
36
|
-
id += c.toString(16);
|
|
53
|
+
return (getRandomValue() & 0x3 | 0x8).toString(16);
|
|
37
54
|
} else {
|
|
38
|
-
|
|
55
|
+
return templateInput;
|
|
39
56
|
}
|
|
57
|
+
}).join('');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generates a string of the given length containing only hexadecimal
|
|
62
|
+
* value 0-9 and a-f.
|
|
63
|
+
* @param {int} length length of the string to generate
|
|
64
|
+
* @returns {string} generated hex string
|
|
65
|
+
*/
|
|
66
|
+
export function generateRandomHexString(length) {
|
|
67
|
+
const crypto = globalScope?.crypto || globalScope?.msCrypto;
|
|
68
|
+
let randomValueTable;
|
|
69
|
+
let randomValueIndex = 0;
|
|
70
|
+
if (crypto && crypto.getRandomValues) {
|
|
71
|
+
// eslint-disable-next-line
|
|
72
|
+
randomValueTable = crypto.getRandomValues(new Uint8Array(31));
|
|
40
73
|
}
|
|
41
|
-
|
|
74
|
+
const chars = [];
|
|
75
|
+
for (var i = 0; i < length; i++) {
|
|
76
|
+
chars.push(getRandomValue(randomValueTable, ++randomValueIndex).toString(16));
|
|
77
|
+
}
|
|
78
|
+
return chars.join('');
|
|
42
79
|
}
|
|
43
80
|
|
|
44
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Generates a 16 character length hexadecimal string.
|
|
83
|
+
* per DT-spec.
|
|
84
|
+
* @see generateRandomHexString
|
|
85
|
+
* @returns {string} generated hex string
|
|
86
|
+
*/
|
|
45
87
|
export function generateSpanId() {
|
|
46
88
|
return generateRandomHexString(16);
|
|
47
89
|
}
|
|
48
90
|
|
|
49
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Generates a 32 character length hexadecimal string.
|
|
93
|
+
* per DT-spec.
|
|
94
|
+
* @see generateRandomHexString
|
|
95
|
+
* @returns {string} generated hex string
|
|
96
|
+
*/
|
|
50
97
|
export function generateTraceId() {
|
|
51
98
|
return generateRandomHexString(32);
|
|
52
|
-
}
|
|
53
|
-
export function generateRandomHexString(length) {
|
|
54
|
-
var randomVals = null;
|
|
55
|
-
var rvIndex = 0;
|
|
56
|
-
var crypto = self.crypto || self.msCrypto;
|
|
57
|
-
// eslint-disable-next-line
|
|
58
|
-
if (crypto && crypto.getRandomValues && Uint8Array) {
|
|
59
|
-
// eslint-disable-next-line
|
|
60
|
-
randomVals = crypto.getRandomValues(new Uint8Array(31));
|
|
61
|
-
}
|
|
62
|
-
var chars = [];
|
|
63
|
-
for (var i = 0; i < length; i++) {
|
|
64
|
-
chars.push(getRandomValue().toString(16));
|
|
65
|
-
}
|
|
66
|
-
return chars.join('');
|
|
67
|
-
function getRandomValue() {
|
|
68
|
-
if (randomVals) {
|
|
69
|
-
// same as % 16
|
|
70
|
-
return randomVals[rvIndex++] & 15;
|
|
71
|
-
} else {
|
|
72
|
-
return Math.random() * 16 | 0;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
99
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import crypto from 'crypto';
|
|
2
|
+
import * as uniqueId from './unique-id';
|
|
3
|
+
const getRandomValues = jest.fn(arr => crypto.randomBytes(arr.length));
|
|
4
|
+
afterEach(() => {
|
|
5
|
+
delete global.crypto;
|
|
6
|
+
});
|
|
7
|
+
describe('generateUuid', () => {
|
|
8
|
+
const uuidv4Regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
9
|
+
test('should generate a uuidv4 that matches the expected format', () => {
|
|
10
|
+
const id = uniqueId.generateUuid();
|
|
11
|
+
expect(uuidv4Regex.test(id)).toEqual(true);
|
|
12
|
+
});
|
|
13
|
+
test('should support using native crypto library', () => {
|
|
14
|
+
global.crypto = {
|
|
15
|
+
getRandomValues
|
|
16
|
+
};
|
|
17
|
+
const id = uniqueId.generateUuid();
|
|
18
|
+
expect(uuidv4Regex.test(id)).toEqual(true);
|
|
19
|
+
expect(getRandomValues).toHaveBeenCalledTimes(1);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
describe('generateRandomHexString', () => {
|
|
23
|
+
const hexRegex = /^[0-9a-f]{8}$/;
|
|
24
|
+
test('should generate a valid hex string', () => {
|
|
25
|
+
const id = uniqueId.generateRandomHexString(8);
|
|
26
|
+
expect(hexRegex.test(id)).toEqual(true);
|
|
27
|
+
});
|
|
28
|
+
test('should support using native crypto library', () => {
|
|
29
|
+
global.crypto = {
|
|
30
|
+
getRandomValues
|
|
31
|
+
};
|
|
32
|
+
const id = uniqueId.generateRandomHexString(8);
|
|
33
|
+
expect(hexRegex.test(id)).toEqual(true);
|
|
34
|
+
expect(getRandomValues).toHaveBeenCalledTimes(1);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
test('generateSpanId should generate a 16 character hex string', () => {
|
|
38
|
+
const id = uniqueId.generateSpanId();
|
|
39
|
+
expect(/^[0-9a-f]{16}$/.test(id)).toEqual(true);
|
|
40
|
+
});
|
|
41
|
+
test('generateTraceId should generate a 32 character hex string', () => {
|
|
42
|
+
const id = uniqueId.generateTraceId();
|
|
43
|
+
expect(/^[0-9a-f]{32}$/.test(id)).toEqual(true);
|
|
44
|
+
});
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { generateRandomHexString } from '../ids/unique-id';
|
|
2
|
+
import { warn } from '../util/console';
|
|
3
|
+
import { stringify } from '../util/stringify';
|
|
4
|
+
import { ee } from '../event-emitter/contextual-ee';
|
|
5
|
+
import { Timer } from '../timer/timer';
|
|
6
|
+
import { isBrowserScope } from '../util/global-scope';
|
|
7
|
+
import { DEFAULT_EXPIRES_MS, DEFAULT_INACTIVE_MS, PREFIX } from './constants';
|
|
8
|
+
import { LocalMemory } from '../storage/local-memory';
|
|
9
|
+
import { InteractionTimer } from '../timer/interaction-timer';
|
|
10
|
+
import { wrapEvents } from '../wrap';
|
|
11
|
+
import { Configurable } from '../config/state/configurable';
|
|
12
|
+
import { handle } from '../event-emitter/handle';
|
|
13
|
+
import { SUPPORTABILITY_METRIC_CHANNEL } from '../../features/metrics/constants';
|
|
14
|
+
import { FEATURE_NAMES } from '../../loaders/features/features';
|
|
15
|
+
const model = {
|
|
16
|
+
value: '',
|
|
17
|
+
inactiveAt: 0,
|
|
18
|
+
expiresAt: 0,
|
|
19
|
+
updatedAt: Date.now(),
|
|
20
|
+
sessionReplayActive: false,
|
|
21
|
+
sessionTraceActive: false,
|
|
22
|
+
custom: {}
|
|
23
|
+
};
|
|
24
|
+
export class SessionEntity {
|
|
25
|
+
/**
|
|
26
|
+
* Create a self-managing Session Entity. This entity is scoped to the agent identifier which triggered it, allowing for multiple simultaneous session objects to exist.
|
|
27
|
+
* There is one "namespace" an agent can store data in LS -- NRBA_{key}. If there are two agents on one page, and they both use the same key, they could overwrite each other since they would both use the same namespace in LS by default.
|
|
28
|
+
* The value can be overridden in the constructor, but will default to a unique 16 character hex string
|
|
29
|
+
* expiresMs and inactiveMs are used to "expire" the session, but can be overridden in the constructor. Pass 0 to disable expiration timers.
|
|
30
|
+
*/
|
|
31
|
+
constructor(opts) {
|
|
32
|
+
this.setup(opts);
|
|
33
|
+
}
|
|
34
|
+
setup(_ref) {
|
|
35
|
+
let {
|
|
36
|
+
agentIdentifier,
|
|
37
|
+
key,
|
|
38
|
+
value = generateRandomHexString(16),
|
|
39
|
+
expiresMs = DEFAULT_EXPIRES_MS,
|
|
40
|
+
inactiveMs = DEFAULT_INACTIVE_MS,
|
|
41
|
+
storageAPI = new LocalMemory()
|
|
42
|
+
} = _ref;
|
|
43
|
+
if (!agentIdentifier || !key) throw new Error('Missing Required Fields');
|
|
44
|
+
if (!isBrowserScope) this.storage = new LocalMemory();else this.storage = storageAPI;
|
|
45
|
+
this.sync(model);
|
|
46
|
+
this.agentIdentifier = agentIdentifier;
|
|
47
|
+
|
|
48
|
+
// key is intended to act as the k=v pair
|
|
49
|
+
this.key = key;
|
|
50
|
+
// value is intended to act as the primary value of the k=v pair
|
|
51
|
+
this.value = value;
|
|
52
|
+
this.expiresMs = expiresMs;
|
|
53
|
+
this.inactiveMs = inactiveMs;
|
|
54
|
+
this.ee = ee.get(agentIdentifier);
|
|
55
|
+
wrapEvents(this.ee);
|
|
56
|
+
|
|
57
|
+
// the first time the session entity class is instantiated, we check the storage API for an existing
|
|
58
|
+
// object. If it exists, the values inside the object are used to inform the timers that run locally.
|
|
59
|
+
// if the initial read is empty, it allows us to set a "fresh" "new" session immediately.
|
|
60
|
+
// the local timers are used after the session is running to "expire" the session, allowing for pausing timers etc.
|
|
61
|
+
// the timestamps stored in the storage API can be checked at initial run, and when the page is restored, otherwise we lean
|
|
62
|
+
// on the local timers to expire the session
|
|
63
|
+
const initialRead = this.read();
|
|
64
|
+
|
|
65
|
+
// the set-up of the timer used to expire the session "naturally" at a certain time
|
|
66
|
+
// this gets ignored if the value is falsy, allowing for session entities that do not expire
|
|
67
|
+
if (expiresMs) {
|
|
68
|
+
this.expiresAt = initialRead?.expiresAt || this.getFutureTimestamp(expiresMs);
|
|
69
|
+
this.expiresTimer = new Timer({
|
|
70
|
+
// When the inactive timer ends, collect a SM and reset the session
|
|
71
|
+
onEnd: () => {
|
|
72
|
+
this.collectSM('expired', this);
|
|
73
|
+
this.collectSM('duration', this);
|
|
74
|
+
this.reset();
|
|
75
|
+
}
|
|
76
|
+
}, this.expiresAt - Date.now());
|
|
77
|
+
} else {
|
|
78
|
+
this.expiresAt = Infinity;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// the set-up of the timer used to expire the session due to "inactivity" at a certain time
|
|
82
|
+
// this gets ignored if the value is falsy, allowing for session entities that do not expire
|
|
83
|
+
// this gets "refreshed" when "activity" is observed
|
|
84
|
+
if (inactiveMs) {
|
|
85
|
+
this.inactiveAt = initialRead?.inactiveAt || this.getFutureTimestamp(inactiveMs);
|
|
86
|
+
this.inactiveTimer = new InteractionTimer({
|
|
87
|
+
// When the inactive timer ends, collect a SM and reset the session
|
|
88
|
+
onEnd: () => {
|
|
89
|
+
this.collectSM('inactive', this);
|
|
90
|
+
this.collectSM('duration', this);
|
|
91
|
+
this.reset();
|
|
92
|
+
},
|
|
93
|
+
// When the inactive timer refreshes, it will update the storage values with an update timestamp
|
|
94
|
+
onRefresh: this.refresh.bind(this),
|
|
95
|
+
// When the inactive timer pauses, update the storage values with an update timestamp
|
|
96
|
+
onPause: () => this.write(new Configurable(this.read(), model)),
|
|
97
|
+
ee: this.ee,
|
|
98
|
+
refreshEvents: ['click', 'keydown', 'scroll']
|
|
99
|
+
}, this.inactiveAt - Date.now());
|
|
100
|
+
} else {
|
|
101
|
+
this.inactiveAt = Infinity;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The fact that the session is "new" or pre-existing is used in some places in the agent. Session Replay and Trace
|
|
105
|
+
// can use this info to inform whether to trust a new sampling decision vs continue a previous tracking effort.
|
|
106
|
+
this.isNew = !Object.keys(initialRead).length;
|
|
107
|
+
// if its a "new" session, we write to storage API with the default values. These values may change over the lifespan of the agent run.
|
|
108
|
+
// we can use configurable here to help us know and manage what values are being used. -- see "model" above
|
|
109
|
+
if (this.isNew) this.write(new Configurable(this, model), true);else this.sync(initialRead);
|
|
110
|
+
this.initialized = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// This is the actual key appended to the storage API
|
|
114
|
+
get lookupKey() {
|
|
115
|
+
return "".concat(PREFIX, "_").concat(this.key);
|
|
116
|
+
}
|
|
117
|
+
sync(data) {
|
|
118
|
+
Object.assign(this, data);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Fetch the stored values from the storage API tied to this entity
|
|
123
|
+
* @returns {Object}
|
|
124
|
+
*/
|
|
125
|
+
read() {
|
|
126
|
+
try {
|
|
127
|
+
const val = this.storage.get(this.lookupKey);
|
|
128
|
+
if (!val) return {};
|
|
129
|
+
// TODO - decompression would need to happen here if we decide to do it
|
|
130
|
+
const obj = typeof val === 'string' ? JSON.parse(val) : val;
|
|
131
|
+
if (this.isInvalid(obj)) return {};
|
|
132
|
+
// if the session expires, collect a SM count before resetting
|
|
133
|
+
if (this.isExpired(obj.expiresAt)) {
|
|
134
|
+
this.collectSM('expired', this);
|
|
135
|
+
this.collectSM('duration', obj, true);
|
|
136
|
+
return this.reset();
|
|
137
|
+
}
|
|
138
|
+
// if "inactive" timer is expired at "read" time -- esp. initial read -- reset
|
|
139
|
+
// collect a SM count before resetting
|
|
140
|
+
if (this.isExpired(obj.inactiveAt)) {
|
|
141
|
+
this.collectSM('inactive', this);
|
|
142
|
+
this.collectSM('duration', obj, true);
|
|
143
|
+
return this.reset();
|
|
144
|
+
}
|
|
145
|
+
return obj;
|
|
146
|
+
} catch (e) {
|
|
147
|
+
warn('Failed to read from storage API', e);
|
|
148
|
+
// storage is inaccessible
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Store data to the storage API tied to this entity
|
|
155
|
+
* To preseve existing attributes, the output of ...session.read()
|
|
156
|
+
* should be appended to the data argument
|
|
157
|
+
* @param {Object} data
|
|
158
|
+
* @returns {Object}
|
|
159
|
+
*/
|
|
160
|
+
write(data) {
|
|
161
|
+
try {
|
|
162
|
+
if (!data || typeof data !== 'object') return;
|
|
163
|
+
// everytime we update, we can update a timestamp for sanity
|
|
164
|
+
data.updatedAt = Date.now();
|
|
165
|
+
this.sync(data);
|
|
166
|
+
// TODO - compression would need happen here if we decide to do it
|
|
167
|
+
this.storage.set(this.lookupKey, stringify(data));
|
|
168
|
+
return data;
|
|
169
|
+
} catch (e) {
|
|
170
|
+
// storage is inaccessible
|
|
171
|
+
warn('Failed to write to the storage API', e);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
reset() {
|
|
176
|
+
// this method should set off a chain of actions across the features by emitting 'new-session'
|
|
177
|
+
// * send off pending payloads
|
|
178
|
+
// * stop recording (stn and sr)...
|
|
179
|
+
// * delete the session and start over
|
|
180
|
+
try {
|
|
181
|
+
if (this.initialized) this.ee.emit('session-reset');
|
|
182
|
+
this.storage.remove(this.lookupKey);
|
|
183
|
+
this.inactiveTimer?.abort?.();
|
|
184
|
+
this.expiresTimer?.clear?.();
|
|
185
|
+
delete this.custom;
|
|
186
|
+
delete this.value;
|
|
187
|
+
this.setup({
|
|
188
|
+
agentIdentifier: this.agentIdentifier,
|
|
189
|
+
key: this.key,
|
|
190
|
+
storageAPI: this.storage,
|
|
191
|
+
expiresMs: this.expiresMs,
|
|
192
|
+
inactiveMs: this.inactiveMs
|
|
193
|
+
});
|
|
194
|
+
return this.read();
|
|
195
|
+
} catch (e) {
|
|
196
|
+
return {};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Refresh the inactivity timer data
|
|
202
|
+
*/
|
|
203
|
+
refresh() {
|
|
204
|
+
// read here & invalidate
|
|
205
|
+
const existingData = this.read();
|
|
206
|
+
this.inactiveAt = this.getFutureTimestamp(this.inactiveMs);
|
|
207
|
+
this.write({
|
|
208
|
+
...existingData,
|
|
209
|
+
inactiveAt: this.inactiveAt
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* @param {number} timestamp
|
|
215
|
+
* @returns {boolean}
|
|
216
|
+
*/
|
|
217
|
+
isExpired(timestamp) {
|
|
218
|
+
return Date.now() > timestamp;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* @param {Object} data
|
|
223
|
+
* @returns {boolean}
|
|
224
|
+
*/
|
|
225
|
+
isInvalid(data) {
|
|
226
|
+
const requiredKeys = ['value', 'expiresAt', 'inactiveAt'];
|
|
227
|
+
return !requiredKeys.every(x => Object.keys(data).includes(x));
|
|
228
|
+
}
|
|
229
|
+
collectSM(type, data, useUpdatedAt) {
|
|
230
|
+
let value, tag;
|
|
231
|
+
if (type === 'duration') {
|
|
232
|
+
const startingTimestamp = data.expiresAt - data.expiresMs;
|
|
233
|
+
const endingTimestamp = useUpdatedAt ? data.updatedAt : Date.now();
|
|
234
|
+
value = endingTimestamp - startingTimestamp;
|
|
235
|
+
tag = 'Session/Duration/Ms';
|
|
236
|
+
}
|
|
237
|
+
if (type === 'expired') tag = 'Session/Expired/Seen';
|
|
238
|
+
if (type === 'inactive') tag = 'Session/Inactive/Seen';
|
|
239
|
+
if (tag) handle(SUPPORTABILITY_METRIC_CHANNEL, [tag, value], undefined, FEATURE_NAMES.metrics, this.ee);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* @param {number} futureMs - The number of ms to use to generate a future timestamp
|
|
244
|
+
* @returns {number}
|
|
245
|
+
*/
|
|
246
|
+
getFutureTimestamp(futureMs) {
|
|
247
|
+
return Date.now() + futureMs;
|
|
248
|
+
}
|
|
249
|
+
syncCustomAttribute(key, value) {
|
|
250
|
+
if (!isBrowserScope) return;
|
|
251
|
+
if (value === null) {
|
|
252
|
+
const curr = this.read();
|
|
253
|
+
if (curr.custom) {
|
|
254
|
+
delete curr.custom[key];
|
|
255
|
+
this.write({
|
|
256
|
+
...curr
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
} else {
|
|
260
|
+
const curr = this.read();
|
|
261
|
+
this.custom = {
|
|
262
|
+
...(curr?.custom || {}),
|
|
263
|
+
[key]: value
|
|
264
|
+
};
|
|
265
|
+
this.write({
|
|
266
|
+
...curr,
|
|
267
|
+
custom: this.custom
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|