Skip to content

♻️ move batching transport stack to @datadog/js-core#4856

Draft
BenoitZugmeyer wants to merge 11 commits into
mainfrom
benoit/move-transport
Draft

♻️ move batching transport stack to @datadog/js-core#4856
BenoitZugmeyer wants to merge 11 commits into
mainfrom
benoit/move-transport

Conversation

@BenoitZugmeyer

Copy link
Copy Markdown
Member

Motivation

The @datadog/js-core package is meant to hold runtime-agnostic SDK utilities
shared across all Datadog JS SDKs. However, the entire batching transport stack
(createBatch, createHttpRequest, createFlushController, and all their
dependencies) lived exclusively in @datadog/browser-core, making it
unavailable to non-browser consumers such as the Worker or future server-side
SDK targets.

This PR moves the complete transport stack into @datadog/js-core, with
browser-specific concerns (fetch, sendBeacon, DOM events) remaining in
browser-core as injectable strategies.

Changes

  • Pure utilities (byteUtils, jsonStringify, context, objectValues,
    isServerError) moved to @datadog/js-core/util; browser-core re-exports.
  • mockable moved to @datadog/js-core/util.
  • Observable, BufferedObservable, mergeObservables, queueMicrotask
    moved to @datadog/js-core/util; queueMicrotask drops the monitor()
    wrapper (callers handle error wrapping at their own layer).
  • Encoder / createIdentityEncoder moved to @datadog/js-core/transport.
  • timer / getZoneJsOriginalValue moved to @datadog/js-core/util;
    js-core timer has no monitor() wrapping; browser-core keeps monitored wrappers.
  • sendWithRetryStrategy and Payload types moved to
    @datadog/js-core/transport; navigator.onLine access goes through
    globalObject.
  • flushController moved to @datadog/js-core/transport; PageExitReason
    and PageMayExitEvent move with it as pure types; pageMayExitObservable
    is injected as a parameter.
  • createHttpRequest moved to @datadog/js-core/transport with injectable
    sendStrategy / sendOnExitStrategy; browser-core keeps fetchStrategy and
    sendBeaconStrategy and wraps the js-core factory.
  • createBatch moved to @datadog/js-core/transport; request and
    pageMayExitObservable are injected parameters; display.warn becomes an
    injected warn function; browser-core wrapper preserves the existing call
    signature for all current callers.
  • API cleanup: sendWithRetryStrategy, newRetryState, RetryState,
    internal constants, createFlushController, and FlushController are
    removed from js-core's public transport entry — they are internal details.
    Their specs move to js-core alongside the implementation.

Test instructions

This is a pure refactor with no observable behaviour change. All existing unit
tests cover the moved code. To sanity-check the SDK still initialises and
sends data correctly:

  1. Run yarn dev and open http://localhost:8080.
  2. Check the Network tab — RUM and Logs requests should still be sent to the
    intake endpoints on the configured interval.
  3. Navigate away from the page and confirm a flush request is sent on
    beforeunload/visibilitychange.

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.
  • Updated documentation and/or relevant AGENTS.md file

Move byteUtils, context types, jsonStringify, objectValues and isServerError
to js-core — they have no browser-specific dependencies and are needed by the
transport modules that will follow in subsequent commits.

Browser-core files become thin re-exports so all existing relative imports
keep working without touching every consumer.
Both classes have no browser-specific dependencies.
queueMicrotask moves to js-core as a pure scheduling helper (no monitor
wrapping — callers are responsible for error handling at their layer).

browser-core/tools/observable.ts becomes a thin re-export.
browser-core/tools/queueMicrotask.ts is deleted: its only consumer was
observable.ts, which now lives in js-core.
Both files already used globalObject from js-core; moving them avoids
a circular path when transport modules (flushController,
sendWithRetryStrategy) migrate to js-core in upcoming commits.

The js-core timer drops the monitor() wrapping — callers are responsible
for error handling at their own layer.
browser-core/tools/timer.ts keeps its monitored wrappers but now imports
getZoneJsOriginalValue from js-core.
browser-core/tools/getZoneJsOriginalValue.ts becomes a thin re-export.
…ransport

Payload, HttpResponse, BandwidthStats and HttpRequestEvent move to a new
payload.ts — they have no browser-specific dependencies and are needed by
both sendWithRetryStrategy and (later) httpRequest.

navigator.onLine becomes globalObject.navigator?.onLine for
cross-environment safety.

browser-core/transport/sendWithRetryStrategy.ts becomes a thin re-export.
httpRequest.ts and its spec are updated to import the types from js-core.
PageExitReason and PageMayExitEvent move alongside it as pure types —
the browser implementation (createPageMayExitObservable) stays in
browser-core; flushController consumes the observable as an injected
parameter so it needs no browser APIs.

RECOMMENDED_REQUEST_BYTES_LIMIT moves to payload.ts (js-core) since
it is a transport-layer constant used by both flushController and
httpRequest.

browser-core/transport/flushController.ts and
browser-core/browser/pageMayExitObservable.ts become thin re-exports.
fetch and sendBeacon are browser-specific APIs; they become injectable
strategies so createHttpRequest stays free of browser globals.

browser-core/transport/httpRequest.ts keeps fetchStrategy and
sendBeaconStrategy and wraps the js-core function by injecting them.
The public signature (endpointBuilders, reportError, bytesLimit?) is
unchanged for all existing callers.
createHttpRequest and pageMayExitObservable are now explicit parameters,
removing the last browser-specific dependencies from the batching logic.
display.warn is replaced by an injected warn function.

browser-core/transport/batch.ts becomes a thin wrapper that wires up
the browser defaults (createHttpRequest, createPageMayExitObservable,
display.warn) and delegates to the js-core implementation.
All existing callers are unchanged.

isPageExitReason also moves to js-core/transport alongside PageExitReason.
… public API

These were exported as stepping stones during the migration but have no
legitimate external consumers:

- sendWithRetryStrategy, newRetryState, RetryState, and the bandwidth /
  retry constants are internal machinery of createHttpRequest; remove
  them from @datadog/js-core/transport
- FlushController is the internal return type of createFlushController;
  callers that needed the type can use ReturnType<typeof createFlushController>
- browser-core's dead sendWithRetryStrategy.ts re-export shim is deleted
  (nothing ever imported from it)
- sendWithRetryStrategy.spec.ts moves from browser-core to js-core so it
  can use internal imports; a minimal test/ helper (registerCleanupTask,
  mockClock, mockNavigator) is added to js-core for this
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Jul 8, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🎯 Code Coverage (details)
Patch Coverage: 82.76%
Overall Coverage: 77.24% (-0.02%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: db9e0f1 | Docs | Datadog PR Page | Give us feedback!

@BenoitZugmeyer BenoitZugmeyer force-pushed the benoit/move-transport branch from fa999e3 to db9e0f1 Compare July 8, 2026 15:17
@cit-pr-commenter-54b7da

Copy link
Copy Markdown

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 172.61 KiB 172.86 KiB +256 B +0.14%
Rum Profiler 8.22 KiB 8.22 KiB 0 B 0.00%
Rum Recorder 21.14 KiB 21.14 KiB 0 B 0.00%
Logs 54.58 KiB 54.89 KiB +312 B +0.56%
Rum Slim 130.44 KiB 130.74 KiB +305 B +0.23%
Worker 22.96 KiB 22.96 KiB 0 B 0.00%

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant