Skip to content

feat(web-record): browser session recording over OTLP - #6

Merged
zwobill merged 4 commits into
mainfrom
feat/web-record-otlp
Aug 11, 2026
Merged

zwobill merged 4 commits into
mainfrom
feat/web-record-otlp

Conversation

@zwobill

@zwobill zwobill commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add @softprobe/web-record (migrated from softprobe-web-record-sdk) that captures rrweb events and exports batches to thelake via OTLP /v1/traces.
  • Reuse @softprobe/tracing/config for Softprobe credentials and {baseUrl}/v1/traces derivation; soft-disable when credentials are missing.
  • Export OTLP contract: span softprobe.web.recording, sp.observation.type=recording, event sp.recording.batch / sp.recording.events.

Test plan

  • pnpm --filter @softprobe/web-record test
  • pnpm --filter @softprobe/web-record build
  • Manually flush a batch against a local thelake and confirm a recording span lands with sp.session.id

Made with Cursor

Migrate softprobe-web-record-sdk into the monorepo as @softprobe/web-record,
flushing rrweb batches to thelake via OTLP spans and shared tracing/config helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8e2b4351aa

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/web-record/package.json Outdated
},
"dependencies": {
"@rrweb/types": "2.0.0-alpha.4",
"@softprobe/tracing": "file:../tracing",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Publish tracing as a registry or workspace dependency

Publishing this package leaves @softprobe/tracing as a local file:../tracing dependency, but that sibling directory is not included in the @softprobe/web-record tarball. Registry consumers therefore cannot resolve the import from @softprobe/tracing/config; use the workspace protocol or a published version so the packed manifest contains an installable registry dependency.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: is now workspace (npm workspaces, matches sibling packages) so the published tarball resolves the registry dependency.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: @softprobe/tracing is now workspace "*" (npm workspaces, matches sibling packages) so the published tarball resolves the registry dependency.

Comment thread packages/web-record/src/sdk.ts Outdated
`OTLP export failed: ${res.status} ${await res.text()}`,
);
}
this.events = [];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve events recorded while an export is in flight

When rrweb emits events while the fetch is awaiting its response, those events are appended to the same array captured as batch, but they were not included in the body because JSON.stringify already ran. Replacing the entire queue after the response consequently discards every event recorded during network latency; detach the batch before exporting or remove only the events actually serialized.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: flush splices the batch out of the queue before , so events recorded while export is in flight stay queued for the next flush (and are restored on failure).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: flush splices the batch out of the queue before await fetch, so events recorded while export is in flight stay queued for the next flush (and are restored on failure).

Comment thread packages/web-record/src/sdk.ts Outdated
Comment on lines +184 to +188
setSessionId(sessionId: string): void {
const next = asNonEmptyString(sessionId);
if (!next) return;
this._sessionId = next;
if (this.enabled) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Rotate buffered events before changing the session ID

If the documented setSessionId call occurs before the current five-second buffer has flushed, the already-recorded events remain queued while _sessionId changes. Since flush() reads _sessionId only when constructing the export, all of those events are then attributed to the new chat session, corrupting session replay correlation; retain the session ID with each batch or rotate the queue synchronously before switching.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: setSessionId now drains/flushes under the old id (drainFlush) before rebinding _sessionId / the HTTP interceptor.

Comment on lines +92 to +95
next.headers = {
...(headers as Record<string, string>),
[HEADER_X_SP_SESSION_ID]: sid,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Limit session headers to application-owned origins

Once recording is enabled, this wrapper adds the session identifier to every fetch request, including requests to analytics, CDNs, or other third-party origins; the XHR patch behaves likewise. On cross-origin calls the non-safelisted header can introduce a CORS preflight and break requests whose server does not allow it, while permissive servers receive a private correlation ID, so injection needs an origin or endpoint check.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: session header is only injected for same-origin requests (isSameOriginRequest); third-party/CDN URLs are skipped.

Comment on lines +83 to +84
const next: RequestInit = { ...(options ?? {}) };
const headers = next.headers ?? {};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve headers supplied by Request inputs

For fetch(new Request(url, { headers: ... })) without options.headers, next.headers starts as an empty object and is then set to only the session header. Passing that init alongside the existing Request overrides rather than merges its headers, silently dropping values such as Authorization and Content-Type; initialize from the Request headers as well as from options.headers.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed: fetch(Request) merges existing Request headers (+ init headers) before setting x-sp-session-id.

zwobill and others added 3 commits August 11, 2026 03:12
Adopt npm workspaces from main, keep @softprobe/web-record, and address
PR review: flush race, setSessionId drain, same-origin session header.
Return flush success, abort drain on failure, chain session switches, and
only advance batch_index after a successful OTLP export.

Co-authored-by: Cursor <cursoragent@cursor.com>
@zwobill
zwobill merged commit 1107933 into main Aug 11, 2026
1 check passed
@zwobill
zwobill deleted the feat/web-record-otlp branch August 11, 2026 05:28
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