Skip to content

Commit 58c5a0e

Browse files
antonisclaude
andcommitted
fix(core): Use native toWellFormed() only, drop manual fallback
Removes the manual charCodeAt-based fallback for surrogate sanitization to reduce bundle size impact. Uses native String.prototype.toWellFormed() which is supported in Node 20+, Chrome 111+, Safari 15.4+, Firefox 119+, and Hermes. On older runtimes the string is returned as-is. Also fixes lint errors from the `as any` cast by using bracket notation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent cc1c1b8 commit 58c5a0e

File tree

1 file changed

+7
-47
lines changed

1 file changed

+7
-47
lines changed

packages/core/src/logs/internal.ts

Lines changed: 7 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -246,54 +246,14 @@ function sanitizeLogAttributes(attributes: Attributes): Attributes {
246246
* on the server to reject the entire log/span batch when they appear in
247247
* JSON-escaped form (e.g. `\uD800`). Replacing them at the SDK level ensures
248248
* only the offending characters are lost instead of the whole payload.
249+
*
250+
* Uses the native `String.prototype.toWellFormed()` when available
251+
* (Node 20+, Chrome 111+, Safari 15.4+, Firefox 119+, Hermes).
252+
* On older runtimes without native support, returns the string as-is.
249253
*/
250254
export function _INTERNAL_removeLoneSurrogates(str: string): string {
251-
// Use native toWellFormed() when available (Node 20+, Safari 15.4+, Chrome 111+)
252-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
253-
const s = str as any;
254-
if (typeof s.isWellFormed === 'function') {
255-
return s.isWellFormed() ? str : s.toWellFormed();
256-
}
257-
258-
// Fast path – scan without allocating. Most strings have no surrogates at all.
259-
let hasLoneSurrogate = false;
260-
for (let i = 0; i < str.length; i++) {
261-
const code = str.charCodeAt(i);
262-
if (code >= 0xd800 && code <= 0xdfff) {
263-
if (code <= 0xdbff && i + 1 < str.length) {
264-
const next = str.charCodeAt(i + 1);
265-
if (next >= 0xdc00 && next <= 0xdfff) {
266-
// Valid surrogate pair – skip the low surrogate
267-
i++;
268-
continue;
269-
}
270-
}
271-
hasLoneSurrogate = true;
272-
break;
273-
}
274-
}
275-
276-
if (!hasLoneSurrogate) {
277-
return str;
278-
}
279-
280-
// Slow path – build a new string, replacing lone surrogates with U+FFFD.
281-
const chars: string[] = [];
282-
for (let i = 0; i < str.length; i++) {
283-
const code = str.charCodeAt(i);
284-
if (code >= 0xd800 && code <= 0xdbff) {
285-
const next = i + 1 < str.length ? str.charCodeAt(i + 1) : 0;
286-
if (next >= 0xdc00 && next <= 0xdfff) {
287-
chars.push(str.charAt(i), str.charAt(i + 1));
288-
i++;
289-
} else {
290-
chars.push('\uFFFD');
291-
}
292-
} else if (code >= 0xdc00 && code <= 0xdfff) {
293-
chars.push('\uFFFD');
294-
} else {
295-
chars.push(str.charAt(i));
296-
}
255+
if (typeof str['isWellFormed'] === 'function') {
256+
return str['isWellFormed']() ? str : str['toWellFormed']();
297257
}
298-
return chars.join('');
258+
return str;
299259
}

0 commit comments

Comments
 (0)