Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions packages/browser-core/src/browser/cookie.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ONE_SECOND } from '@datadog/js-core/time'
import { buildUrl, globalObject } from '@datadog/js-core/util'
import { buildUrl, globalObject, generateUUID } from '@datadog/js-core/util'
import {
findAllCommaSeparatedValues,
findCommaSeparatedValue,
findCommaSeparatedValues,
generateUUID,
} from '../tools/utils/stringUtils'

export interface CookieOptions {
Expand Down
3 changes: 1 addition & 2 deletions packages/browser-core/src/browser/cookieAccess.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { ONE_MINUTE, ONE_SECOND, dateNow } from '@datadog/js-core/time'
import { globalObject } from '@datadog/js-core/util'
import { globalObject, generateUUID } from '@datadog/js-core/util'
import { setInterval, clearInterval } from '../tools/timer'
import { Observable } from '../tools/observable'
import { mockable } from '../tools/mockable'
import { display } from '../tools/display'
import { generateUUID } from '../tools/utils/stringUtils'
import { addTelemetryDebug } from '../domain/telemetry'
import { addEventListener, DOM_EVENT, isEventSupported } from './addEventListener'
import { getCookies, setCookie } from './cookie'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { clocksNow } from '@datadog/js-core/time'
import { ConsoleApiName, globalConsole } from '@datadog/js-core/util'
import { ConsoleApiName, globalConsole, jsonStringify } from '@datadog/js-core/util'
import { isError, computeRawError } from '../error/error'
import { Observable, mergeObservables } from '../../tools/observable'
import { callMonitored } from '../../tools/monitor'
import { sanitize } from '../../tools/serialisation/sanitize'
import { jsonStringify } from '../../tools/serialisation/jsonStringify'
import type { RawError } from '../error/error.types'
import { ErrorHandling, ErrorSource, NonErrorPrefix } from '../error/error.types'
import { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-core/src/domain/contexts/tabContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Hook } from '@datadog/js-core/assembly'
import { generateUUID } from '../../tools/utils/stringUtils'
import { generateUUID } from '@datadog/js-core/util'

export const TAB_ID_STORAGE_KEY = '_dd_tab_id'

Expand Down
3 changes: 1 addition & 2 deletions packages/browser-core/src/domain/error/error.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { ClocksState } from '@datadog/js-core/time'
import { isIndexableObject } from '@datadog/js-core/util'
import { isIndexableObject, jsonStringify } from '@datadog/js-core/util'
import { sanitize } from '../../tools/serialisation/sanitize'
import type { Context } from '../../tools/serialisation/context'
import { jsonStringify } from '../../tools/serialisation/jsonStringify'
import type { StackTrace } from '../../tools/stackTrace/computeStackTrace'
import { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'
import { toStackTraceString } from '../../tools/stackTrace/handlingStack'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ import {
relativeNow,
} from '@datadog/js-core/time'
import type { TimeStamp, RelativeTime } from '@datadog/js-core/time'
import { isWorkerEnvironment } from '@datadog/js-core/util'
import { isWorkerEnvironment, generateUUID } from '@datadog/js-core/util'
import { Observable } from '../../tools/observable'
import { createValueHistory } from '../../tools/valueHistory'
import { addEventListener, addEventListeners, DOM_EVENT } from '../../browser/addEventListener'
import { clearInterval, clearTimeout, setInterval, setTimeout } from '../../tools/timer'
import { mockable } from '../../tools/mockable'
import { noop, throttle } from '../../tools/utils/functionUtils'
import { generateUUID } from '../../tools/utils/stringUtils'
import type { Configuration } from '../configuration'
import type { TrackingConsentState } from '../trackingConsent'
import { display } from '../../tools/display'
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-core/src/domain/session/sessionState.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { dateNow } from '@datadog/js-core/time'
import type { TimeStamp } from '@datadog/js-core/time'
import { generateUUID } from '@datadog/js-core/util'
import { isEmptyObject } from '../../tools/utils/objectUtils'
import { objectEntries } from '../../tools/utils/polyfills'
import { generateUUID } from '../../tools/utils/stringUtils'
import type { Configuration } from '../configuration'
import { SESSION_EXPIRATION_DELAY, SESSION_TIME_OUT_DELAY } from './sessionConstants'
import { isValidSessionString, SESSION_ENTRY_REGEXP, SESSION_ENTRY_SEPARATOR } from './sessionStateValidation'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { generateUUID } from '../../../tools/utils/stringUtils'
import { generateUUID } from '@datadog/js-core/util'
import { Observable } from '../../../tools/observable'
import { addEventListener } from '../../../browser/addEventListener'
import { SessionPersistence } from '../sessionConstants'
Expand Down
3 changes: 1 addition & 2 deletions packages/browser-core/src/domain/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { clocksNow } from '@datadog/js-core/time'
import { getDebugMode, combine, globalObject, isWorkerEnvironment } from '@datadog/js-core/util'
import { getDebugMode, combine, globalObject, isWorkerEnvironment, jsonStringify } from '@datadog/js-core/util'
import type { Hook } from '@datadog/js-core/assembly'
import type { RecursivePartial } from '@datadog/js-core/util'
import { DISCARDED } from '@datadog/js-core/assembly'
Expand All @@ -21,7 +21,6 @@ import { startMonitorErrorCollection } from '../../tools/monitor'
import { display } from '../../tools/display'
import { sendToExtension } from '../../tools/sendToExtension'
import { performDraw } from '../../tools/utils/numberUtils'
import { jsonStringify } from '../../tools/serialisation/jsonStringify'
import { NonErrorPrefix } from '../error/error.types'
import type { StackTrace } from '../../tools/stackTrace/computeStackTrace'
import { computeStackTrace } from '../../tools/stackTrace/computeStackTrace'
Expand Down
3 changes: 3 additions & 0 deletions packages/browser-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,14 @@ export * from './tools/utils/polyfills'
export * from './tools/utils/timezone'
export * from './tools/utils/numberUtils'
export * from './tools/utils/byteUtils'
export { ONE_KIBI_BYTE, ONE_MEBI_BYTE } from '@datadog/js-core/util'
export * from './tools/utils/objectUtils'
export * from './tools/utils/functionUtils'
export * from './tools/serialisation/jsonStringify'
export { jsonStringify } from '@datadog/js-core/util'
export * from './tools/serialisation/stringify'
export * from './tools/utils/stringUtils'
export { generateUUID } from '@datadog/js-core/util'
export * from './tools/matchOption'
export * from './tools/utils/responseUtils'
export type { RawError, RawErrorCause, ErrorWithCause, Csp } from './domain/error/error.types'
Expand Down
36 changes: 0 additions & 36 deletions packages/browser-core/src/tools/serialisation/jsonStringify.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,5 @@
import { noop } from '../utils/functionUtils'

/**
* Custom implementation of JSON.stringify that ignores some toJSON methods. We need to do that
* because some sites badly override toJSON on certain objects. Removing all toJSON methods from
* nested values would be too costly, so we just detach them from the root value, and native classes
* used to build JSON values (Array and Object).
*
* Note: this still assumes that JSON.stringify is correct.
*/
export function jsonStringify(
value: unknown,
replacer?: Array<string | number>,
space?: string | number
): string | undefined {
if (typeof value !== 'object' || value === null) {
return JSON.stringify(value)
}

// Note: The order matter here. We need to detach toJSON methods on parent classes before their
// subclasses.
const restoreObjectPrototypeToJson = detachToJsonMethod(Object.prototype)
const restoreArrayPrototypeToJson = detachToJsonMethod(Array.prototype)
const restoreValuePrototypeToJson = detachToJsonMethod(Object.getPrototypeOf(value))
const restoreValueToJson = detachToJsonMethod(value)

try {
return JSON.stringify(value, replacer, space)
} catch {
return '<error: unable to serialize object>'
} finally {
restoreObjectPrototypeToJson()
restoreArrayPrototypeToJson()
restoreValuePrototypeToJson()
restoreValueToJson()
}
}

export interface ObjectWithToJsonMethod {
toJSON?: () => unknown
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ONE_KIBI_BYTE } from '@datadog/js-core/util'
import { display } from '../display'
import { ONE_KIBI_BYTE } from '../utils/byteUtils'
import type { Context, ContextArray, ContextValue } from './context'
import type { ObjectWithToJsonMethod } from './jsonStringify'
import { detachToJsonMethod } from './jsonStringify'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { jsonStringify } from './jsonStringify'
import { jsonStringify } from '@datadog/js-core/util'
import { sanitize } from './sanitize'

export function safeToString(value: unknown): string | undefined {
Expand Down
10 changes: 1 addition & 9 deletions packages/browser-core/src/tools/timer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,6 @@ import { noop } from './utils/functionUtils'
zoneJs = mockZoneJs()
})

it('executes the callback asynchronously', () => {
const spy = jasmine.createSpy()
setTimer(spy)
expect(spy).not.toHaveBeenCalled()
clock.tick(0)
expect(spy).toHaveBeenCalledOnceWith()
})

it('schedules an asynchronous task', () => {
const spy = jasmine.createSpy()
setTimer(spy)
Expand Down Expand Up @@ -62,7 +54,7 @@ import { noop } from './utils/functionUtils'
expect(onMonitorErrorCollectedSpy).toHaveBeenCalledOnceWith(new Error('foo'))
})

it('can be canceled', () => {
it('cancels the pending callback when cleared', () => {
const spy = jasmine.createSpy()
const timerId = setTimer(spy)
clearTimer(timerId)
Expand Down
24 changes: 16 additions & 8 deletions packages/browser-core/src/tools/timer.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,30 @@
import type { GlobalObject } from '@datadog/js-core/util'
import { globalObject } from '@datadog/js-core/util'
import { getZoneJsOriginalValue } from './getZoneJsOriginalValue'
import type { TimeoutId } from '@datadog/js-core/util'
import {
setTimeout as coreSetTimeout,
clearTimeout as coreClearTimeout,
setInterval as coreSetInterval,
clearInterval as coreClearInterval,
} from '@datadog/js-core/util'
import { monitor } from './monitor'

export type TimeoutId = ReturnType<GlobalObject['setTimeout']>
export type { TimeoutId }

// @datadog/js-core/util's timer functions already bypass Zone.js patching; we only need to add
// browser-core's monitor() wrapping on top, so errors thrown in the callback are still caught and
// reported instead of becoming uncaught exceptions.

export function setTimeout(callback: () => void, delay?: number): TimeoutId {
return getZoneJsOriginalValue(globalObject, 'setTimeout')(monitor(callback), delay)
return coreSetTimeout(monitor(callback), delay)
}

export function clearTimeout(timeoutId: TimeoutId | undefined) {
getZoneJsOriginalValue(globalObject, 'clearTimeout')(timeoutId)
coreClearTimeout(timeoutId)
}

export function setInterval(callback: () => void, delay?: number): TimeoutId {
return getZoneJsOriginalValue(globalObject, 'setInterval')(monitor(callback), delay)
return coreSetInterval(monitor(callback), delay)
}

export function clearInterval(timeoutId: TimeoutId | undefined) {
getZoneJsOriginalValue(globalObject, 'clearInterval')(timeoutId)
coreClearInterval(timeoutId)
}
3 changes: 0 additions & 3 deletions packages/browser-core/src/tools/utils/byteUtils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
export const ONE_KIBI_BYTE = 1024
export const ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE

// eslint-disable-next-line no-control-regex
const HAS_MULTI_BYTES_CHARACTERS = /[^\u0000-\u007F]/

Expand Down
11 changes: 0 additions & 11 deletions packages/browser-core/src/tools/utils/stringUtils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,3 @@
/**
* UUID v4
* from https://gist.github.com/jed/982883
*/
export function generateUUID(placeholder?: string): string {
return placeholder
? // eslint-disable-next-line no-bitwise
(parseInt(placeholder, 10) ^ ((Math.random() * 16) >> (parseInt(placeholder, 10) / 4))).toString(16)
: `${1e7}-${1e3}-${4e3}-${8e3}-${1e11}`.replace(/[018]/g, generateUUID)
}

// Assuming input string is following the HTTP Cookie format defined in
// https://www.ietf.org/rfc/rfc2616.txt and https://www.ietf.org/rfc/rfc6265.txt, we don't need to
// be too strict with this regex.
Expand Down
4 changes: 2 additions & 2 deletions packages/browser-core/src/transport/batch.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type { EndpointBuilder } from '@datadog/js-core/transport'
import { jsonStringify, ONE_KIBI_BYTE } from '@datadog/js-core/util'
import { DOCS_TROUBLESHOOTING, MORE_DETAILS, display } from '../tools/display'
import type { Context } from '../tools/serialisation/context'
import { objectValues } from '../tools/utils/polyfills'
import { isPageExitReason, createPageMayExitObservable } from '../browser/pageMayExitObservable'
import { jsonStringify } from '../tools/serialisation/jsonStringify'
import { createIdentityEncoder } from '../tools/encoder'
import type { Encoder, EncoderResult } from '../tools/encoder'
import { computeBytesCount, ONE_KIBI_BYTE } from '../tools/utils/byteUtils'
import { computeBytesCount } from '../tools/utils/byteUtils'
import { mockable } from '../tools/mockable'
import type { Observable } from '../tools/observable'
import { createHttpRequest } from './httpRequest'
Expand Down
2 changes: 1 addition & 1 deletion packages/browser-core/src/transport/httpRequest.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { EndpointBuilder, TransportRetryInfo } from '@datadog/js-core/transport'
import { ONE_KIBI_BYTE } from '@datadog/js-core/util'
import type { Context } from '../tools/serialisation/context'
import { fetch } from '../browser/fetch'
import { monitor, monitorError } from '../tools/monitor'
import { Observable } from '../tools/observable'
import { ONE_KIBI_BYTE } from '../tools/utils/byteUtils'
import { newRetryState, sendWithRetryStrategy } from './sendWithRetryStrategy'

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ONE_MEBI_BYTE } from '@datadog/js-core/util'
import { mockClock, setNavigatorOnLine } from '../../test'
import type { Clock } from '../../test'
import { Observable } from '../tools/observable'
import { ONE_MEBI_BYTE } from '../tools/utils/byteUtils'
import type { RetryState } from './sendWithRetryStrategy'
import {
newRetryState,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ONE_MINUTE, ONE_SECOND } from '@datadog/js-core/time'
import type { TrackType } from '@datadog/js-core/transport'
import { ONE_MEBI_BYTE, ONE_KIBI_BYTE } from '@datadog/js-core/util'
import { setTimeout } from '../tools/timer'
import { ONE_MEBI_BYTE, ONE_KIBI_BYTE } from '../tools/utils/byteUtils'
import { isServerError } from '../tools/utils/responseUtils'
import type { Observable } from '../tools/observable'
import type { Payload, HttpRequestEvent, HttpResponse, BandwidthStats } from './httpRequest'
Expand Down
40 changes: 40 additions & 0 deletions packages/js-core/api/util.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@
// @public
export function buildUrl(url: string, base?: string): URL;

// @public
function clearInterval_2(timeoutId: TimeoutId | undefined): void;
export { clearInterval_2 as clearInterval }

// @public
function clearTimeout_2(timeoutId: TimeoutId | undefined): void;
export { clearTimeout_2 as clearTimeout }

// @public
export function combine<A, B>(a: A, b: B): Combined<A, B>;

Expand Down Expand Up @@ -119,6 +127,9 @@ export interface Display {
warn: typeof console.warn;
}

// @public
export function generateUUID(placeholder?: string): string;

// @public
export function getDebugMode(): boolean;

Expand Down Expand Up @@ -155,6 +166,9 @@ export function isValidUrl(url: string): boolean;
// @public
export const isWorkerEnvironment: boolean;

// @public
export function jsonStringify(value: unknown, replacer?: Array<string | number>, space?: string | number): string | undefined;

// @public
export function mergeInto<D, S>(destination: D, source: S): Merged<D, S>;

Expand All @@ -181,6 +195,12 @@ export type NetworkInterface = 'bluetooth' | 'cellular' | 'ethernet' | 'none' |
// @public
export function normalizeUrl(url: string): string;

// @public
export const ONE_KIBI_BYTE = 1024;

// @public
export const ONE_MEBI_BYTE: number;

// @public
export const originalConsoleMethods: Display;

Expand Down Expand Up @@ -250,6 +270,26 @@ export interface SampleBufferFullEvent extends Event {
// @public
export function setDebugMode(newDebugMode: boolean): void;

// @public
function setInterval_2(callback: () => void, delay?: number): TimeoutId;
export { setInterval_2 as setInterval }

// @public
function setTimeout_2(callback: () => void, delay?: number): TimeoutId;
export { setTimeout_2 as setTimeout }

// @public
export function throttle<T extends (...args: any[]) => void>(fn: T, wait: number, options?: {
leading?: boolean;
trailing?: boolean;
}): {
throttled: (...parameters: Parameters<T>) => void;
cancel: () => void;
};

// @public
export type TimeoutId = ReturnType<GlobalObject['setTimeout']>;

// (No @packageDocumentation comment for this package)

```
6 changes: 6 additions & 0 deletions packages/js-core/src/entries/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ export type {
ProfilerConstructor,
} from '../util/globalObject'
export { normalizeUrl, isValidUrl, getPathName, buildUrl, getPristineWindow } from '../util/urlPolyfill'
export { generateUUID } from '../util/stringUtils'
export { jsonStringify } from '../util/jsonStringify'
export { ONE_KIBI_BYTE, ONE_MEBI_BYTE } from '../util/byteUtils'
export { setTimeout, clearTimeout, setInterval, clearInterval } from '../util/timer'
export type { TimeoutId } from '../util/timer'
export { throttle } from '../util/functionUtils'
5 changes: 5 additions & 0 deletions packages/js-core/src/util/byteUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/** Number of bytes in a kibibyte (1024 bytes). */
export const ONE_KIBI_BYTE = 1024

/** Number of bytes in a mebibyte (1024 kibibytes). */
export const ONE_MEBI_BYTE = 1024 * ONE_KIBI_BYTE
Loading
Loading