Skip to main content

better_auth_core/utils/
json.rs

1//! JavaScript JSON numbers at request, response, callback and storage boundaries.
2//!
3//! `JsValue` carries IEEE754 numbers, including Infinity and signed zero, without
4//! enabling `serde_json` features that reserve otherwise valid application keys.
5
6use indexmap::IndexMap;
7use serde::{
8    Deserialize, Deserializer, Serialize, Serializer,
9    de::{DeserializeOwned, IntoDeserializer, MapAccess, SeqAccess, Visitor},
10};
11use serde_json::{Number, Value};
12use std::{
13    any::{Any, TypeId},
14    fmt,
15};
16
17#[derive(Clone, Debug, PartialEq)]
18pub enum JsValue {
19    Null,
20    Bool(bool),
21    Number(f64),
22    String(String),
23    Array(Vec<Self>),
24    Object(IndexMap<String, Self>),
25}
26
27impl JsValue {
28    /// JavaScript String coercion used by trusted endpoint ID schemas.
29    /// # Errors
30    /// Returns an error when an own toString value prevents object coercion.
31    pub fn coerce_string(&self) -> Result<String, &'static str> {
32        match self {
33            Self::Null => Ok("null".to_owned()),
34            Self::Bool(value) => Ok(value.to_string()),
35            Self::String(value) => Ok(value.clone()),
36            Self::Number(value) => Ok(ryu_js::Buffer::new().format(*value).to_owned()),
37            Self::Array(values) => values
38                .iter()
39                .map(|value| match value {
40                    Self::Null => Ok(String::new()),
41                    value => value.coerce_string(),
42                })
43                .collect::<Result<Vec<_>, _>>()
44                .map(|values| values.join(",")),
45            Self::Object(value) if value.contains_key("toString") => {
46                Err("Cannot convert object to primitive value")
47            }
48            Self::Object(_) => Ok("[object Object]".to_owned()),
49        }
50    }
51
52    #[must_use]
53    pub const fn as_f64(&self) -> Option<f64> {
54        if let Self::Number(n) = self {
55            Some(*n)
56        } else {
57            None
58        }
59    }
60    #[must_use]
61    pub fn as_str(&self) -> Option<&str> {
62        if let Self::String(s) = self {
63            Some(s)
64        } else {
65            None
66        }
67    }
68    #[must_use]
69    pub const fn as_bool(&self) -> Option<bool> {
70        if let Self::Bool(b) = self {
71            Some(*b)
72        } else {
73            None
74        }
75    }
76    #[must_use]
77    pub const fn as_object(&self) -> Option<&IndexMap<String, Self>> {
78        if let Self::Object(m) = self {
79            Some(m)
80        } else {
81            None
82        }
83    }
84    #[must_use]
85    pub fn as_array(&self) -> Option<&[Self]> {
86        if let Self::Array(a) = self {
87            Some(a)
88        } else {
89            None
90        }
91    }
92    #[must_use]
93    pub fn get(&self, key: &str) -> Option<&Self> {
94        self.as_object()?.get(key)
95    }
96    #[must_use]
97    pub const fn is_null(&self) -> bool {
98        matches!(self, Self::Null)
99    }
100    #[must_use]
101    pub const fn is_string(&self) -> bool {
102        matches!(self, Self::String(_))
103    }
104    #[must_use]
105    pub const fn is_boolean(&self) -> bool {
106        matches!(self, Self::Bool(_))
107    }
108    #[must_use]
109    pub const fn is_array(&self) -> bool {
110        matches!(self, Self::Array(_))
111    }
112    #[must_use]
113    pub const fn is_object(&self) -> bool {
114        matches!(self, Self::Object(_))
115    }
116    /// Apply JSON.stringify before converting to `serde_json`'s finite representation.
117    ///
118    /// # Errors
119    ///
120    /// Returns an error if the JavaScript value cannot be represented as JSON.
121    pub fn to_json_value(&self) -> Result<Value, serde_json::Error> {
122        finite_value(self)
123    }
124}
125
126impl From<Value> for JsValue {
127    fn from(value: Value) -> Self {
128        match value {
129            Value::Null => Self::Null,
130            Value::Bool(b) => Self::Bool(b),
131            Value::Number(n) => Self::Number(n.as_f64().unwrap_or_default()),
132            Value::String(s) => Self::String(s),
133            Value::Array(a) => Self::Array(a.into_iter().map(Self::from).collect()),
134            Value::Object(m) => {
135                Self::Object(m.into_iter().map(|(k, v)| (k, Self::from(v))).collect())
136            }
137        }
138    }
139}
140
141impl Serialize for JsValue {
142    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
143        match self {
144            Self::Null => serializer.serialize_unit(),
145            Self::Bool(b) => serializer.serialize_bool(*b),
146            Self::Number(n) => serializer.serialize_f64(*n),
147            Self::String(s) => serializer.serialize_str(s),
148            Self::Array(a) => a.serialize(serializer),
149            Self::Object(m) => m.serialize(serializer),
150        }
151    }
152}
153
154impl<'de> Deserialize<'de> for JsValue {
155    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
156        struct JsVisitor;
157        impl<'de> Visitor<'de> for JsVisitor {
158            type Value = JsValue;
159            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
160                formatter.write_str("a JSON value")
161            }
162            fn visit_unit<E>(self) -> Result<Self::Value, E> {
163                Ok(JsValue::Null)
164            }
165            fn visit_none<E>(self) -> Result<Self::Value, E> {
166                Ok(JsValue::Null)
167            }
168            fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E> {
169                Ok(JsValue::Bool(v))
170            }
171            #[expect(
172                clippy::as_conversions,
173                clippy::cast_precision_loss,
174                reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
175            )]
176            fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> {
177                Ok(JsValue::Number(v as f64))
178            }
179            #[expect(
180                clippy::as_conversions,
181                clippy::cast_precision_loss,
182                reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
183            )]
184            fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> {
185                Ok(JsValue::Number(v as f64))
186            }
187            fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E> {
188                Ok(JsValue::Number(v))
189            }
190            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
191                Ok(JsValue::String(v.into()))
192            }
193            fn visit_string<E>(self, v: String) -> Result<Self::Value, E> {
194                Ok(JsValue::String(v))
195            }
196            fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
197                let mut values = Vec::new();
198                while let Some(value) = seq.next_element()? {
199                    values.push(value);
200                }
201                Ok(JsValue::Array(values))
202            }
203            fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
204                let mut values = IndexMap::new();
205                while let Some((key, value)) = map.next_entry()? {
206                    drop(values.insert(key, value));
207                }
208                Ok(JsValue::Object(values))
209            }
210        }
211        deserializer.deserialize_any(JsVisitor)
212    }
213}
214
215impl IntoDeserializer<'_, serde_json::Error> for JsValue {
216    type Deserializer = Self;
217    fn into_deserializer(self) -> Self {
218        self
219    }
220}
221
222macro_rules! signed_number {
223    ($($method:ident),*) => {$(
224#[expect(clippy::as_conversions, clippy::cast_possible_truncation, reason = "Finite integral values are checked against the target integer bounds before conversion")]
225        fn $method<V: Visitor<'de>>(self,visitor:V)->Result<V::Value,Self::Error> {
226            match self {
227                Self::Number(n) if n.is_finite() && n.fract()==0.0 && (-9_223_372_036_854_776_000.0..9_223_372_036_854_776_000.0).contains(&n) => visitor.visit_i64(n as i64),
228                value => value.deserialize_any(visitor),
229            }
230        }
231    )*};
232}
233
234macro_rules! unsigned_number {
235    ($($method:ident),*) => {$(
236#[expect(clippy::as_conversions, clippy::cast_possible_truncation, clippy::cast_sign_loss, reason = "Finite integral values are checked against the target integer bounds before conversion")]
237        fn $method<V: Visitor<'de>>(self,visitor:V)->Result<V::Value,Self::Error> {
238            match self {
239                Self::Number(n) if n.is_finite() && n.fract()==0.0 && (0.0..18_446_744_073_709_552_000.0).contains(&n) => visitor.visit_u64(n as u64),
240                value => value.deserialize_any(visitor),
241            }
242        }
243    )*};
244}
245
246impl<'de> Deserializer<'de> for JsValue {
247    type Error = serde_json::Error;
248    fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
249        match self {
250            Self::Null => visitor.visit_unit(),
251            Self::Bool(b) => visitor.visit_bool(b),
252            Self::Number(n) => visitor.visit_f64(n),
253            Self::String(s) => visitor.visit_string(s),
254            Self::Array(a) => {
255                visitor.visit_seq(serde::de::value::SeqDeserializer::new(a.into_iter()))
256            }
257            Self::Object(m) => {
258                visitor.visit_map(serde::de::value::MapDeserializer::new(m.into_iter()))
259            }
260        }
261    }
262    fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
263        if self.is_null() {
264            visitor.visit_none()
265        } else {
266            visitor.visit_some(self)
267        }
268    }
269    fn deserialize_newtype_struct<V: Visitor<'de>>(
270        self,
271        _name: &'static str,
272        visitor: V,
273    ) -> Result<V::Value, Self::Error> {
274        visitor.visit_newtype_struct(self)
275    }
276    fn deserialize_enum<V: Visitor<'de>>(
277        self,
278        _name: &'static str,
279        _variants: &'static [&'static str],
280        visitor: V,
281    ) -> Result<V::Value, Self::Error> {
282        match self {
283            Self::String(s) => visitor.visit_enum(s.into_deserializer()),
284            Self::Object(map) => visitor.visit_enum(serde::de::value::MapAccessDeserializer::new(
285                serde::de::value::MapDeserializer::new(map.into_iter()),
286            )),
287            value @ (Self::Null | Self::Bool(_) | Self::Number(_) | Self::Array(_)) => {
288                value.deserialize_any(visitor)
289            }
290        }
291    }
292    signed_number!(
293        deserialize_i8,
294        deserialize_i16,
295        deserialize_i32,
296        deserialize_i64,
297        deserialize_i128
298    );
299    unsigned_number!(
300        deserialize_u8,
301        deserialize_u16,
302        deserialize_u32,
303        deserialize_u64,
304        deserialize_u128
305    );
306    serde::forward_to_deserialize_any! {
307        bool f32 f64 char str string bytes
308        byte_buf unit unit_struct seq tuple tuple_struct map struct identifier ignored_any
309    }
310}
311
312struct Parser<'a> {
313    input: &'a str,
314    position: usize,
315}
316
317impl Parser<'_> {
318    fn peek(&self) -> Option<u8> {
319        self.input.as_bytes().get(self.position).copied()
320    }
321    fn whitespace(&mut self) {
322        while self
323            .peek()
324            .is_some_and(|b| matches!(b, b' ' | b'\n' | b'\r' | b'\t'))
325        {
326            self.position += 1;
327        }
328    }
329    fn take(&mut self, byte: u8) -> bool {
330        if self.peek() == Some(byte) {
331            self.position += 1;
332            true
333        } else {
334            false
335        }
336    }
337    fn string(&mut self) -> Result<String, serde_json::Error> {
338        let start = self.position;
339        if !self.take(b'"') {
340            return Err(invalid("expected JSON string"));
341        }
342        loop {
343            match self.peek() {
344                Some(b'"') => {
345                    self.position += 1;
346                    return serde_json::from_str(
347                        self.input
348                            .get(start..self.position)
349                            .ok_or_else(|| invalid("invalid JSON string bounds"))?,
350                    );
351                }
352                Some(b'\\') => {
353                    self.position += 1;
354                    if self.peek().is_none() {
355                        return Err(invalid("unterminated JSON escape"));
356                    }
357                    self.position += 1;
358                }
359                Some(_) => self.position += 1,
360                None => return Err(invalid("unterminated JSON string")),
361            }
362        }
363    }
364    fn value(&mut self, depth: usize) -> Result<JsValue, serde_json::Error> {
365        self.whitespace();
366        match self.peek() {
367            Some(b'"') => self.string().map(JsValue::String),
368            Some(b'{') => {
369                if depth >= 127 {
370                    return Err(invalid("recursion limit exceeded"));
371                }
372                self.position += 1;
373                self.whitespace();
374                let mut values = IndexMap::new();
375                if self.take(b'}') {
376                    return Ok(JsValue::Object(values));
377                }
378                loop {
379                    self.whitespace();
380                    let key = self.string()?;
381                    self.whitespace();
382                    if !self.take(b':') {
383                        return Err(invalid("expected JSON colon"));
384                    }
385                    let value = self.value(depth + 1)?;
386                    drop(values.insert(key, value));
387                    self.whitespace();
388                    if self.take(b'}') {
389                        break;
390                    }
391                    if !self.take(b',') {
392                        return Err(invalid("expected JSON object delimiter"));
393                    }
394                }
395                Ok(JsValue::Object(values))
396            }
397            Some(b'[') => {
398                if depth >= 127 {
399                    return Err(invalid("recursion limit exceeded"));
400                }
401                self.position += 1;
402                self.whitespace();
403                let mut values = Vec::new();
404                if self.take(b']') {
405                    return Ok(JsValue::Array(values));
406                }
407                loop {
408                    values.push(self.value(depth + 1)?);
409                    self.whitespace();
410                    if self.take(b']') {
411                        break;
412                    }
413                    if !self.take(b',') {
414                        return Err(invalid("expected JSON array delimiter"));
415                    }
416                }
417                Ok(JsValue::Array(values))
418            }
419            Some(b't') => self.literal("true", JsValue::Bool(true)),
420            Some(b'f') => self.literal("false", JsValue::Bool(false)),
421            Some(b'n') => self.literal("null", JsValue::Null),
422            Some(b'-' | b'0'..=b'9') => self.number(),
423            _ => Err(invalid("expected JSON value")),
424        }
425    }
426    fn literal(&mut self, text: &str, value: JsValue) -> Result<JsValue, serde_json::Error> {
427        if self
428            .input
429            .get(self.position..)
430            .is_some_and(|rest| rest.starts_with(text))
431        {
432            self.position += text.len();
433            Ok(value)
434        } else {
435            Err(invalid("invalid JSON literal"))
436        }
437    }
438    fn digits(&mut self) -> bool {
439        let start = self.position;
440        while self.peek().is_some_and(|b| b.is_ascii_digit()) {
441            self.position += 1;
442        }
443        self.position > start
444    }
445    fn number(&mut self) -> Result<JsValue, serde_json::Error> {
446        let start = self.position;
447        let _ignored_take = self.take(b'-');
448        if !self.take(b'0') && !self.digits() {
449            return Err(invalid("invalid JSON number"));
450        }
451        if self.take(b'.') && !self.digits() {
452            return Err(invalid("invalid JSON fraction"));
453        }
454        if self.take(b'e') || self.take(b'E') {
455            let _ignored_take_2 = self.take(b'+') || self.take(b'-');
456            if !self.digits() {
457                return Err(invalid("invalid JSON exponent"));
458            }
459        }
460        self.input
461            .get(start..self.position)
462            .ok_or_else(|| invalid("invalid JSON number bounds"))?
463            .parse::<f64>()
464            .map(JsValue::Number)
465            .map_err(|_error| invalid("invalid JSON number"))
466    }
467}
468
469/// Parse RFC8259 structure with JavaScript f64 conversion. Strings use `serde_json`'s
470/// escape/UTF8 validation; structural recursion is bounded to its default depth.
471///
472/// # Errors
473///
474/// Returns an error if the input is not valid JSON.
475pub fn parse_value(input: &str) -> Result<JsValue, serde_json::Error> {
476    let mut parser = Parser { input, position: 0 };
477    let value = parser.value(0)?;
478    parser.whitespace();
479    if parser.position != input.len() {
480        return Err(invalid("trailing JSON input"));
481    }
482    Ok(value)
483}
484
485///
486/// # Errors
487///
488/// Returns an error if the input is not valid JSON or cannot be deserialized into the requested type.
489pub fn from_slice<T: DeserializeOwned + 'static>(input: &[u8]) -> Result<T, serde_json::Error> {
490    let text = std::str::from_utf8(input).map_err(|_error| invalid("invalid JSON UTF8"))?;
491    from_value(parse_value(text)?)
492}
493
494/// Decode an owned DTO.
495///
496/// Root `Value` is constructed without `serde_json`'s private map markers. Arbitrary nested
497/// `Value` fields must use `deserialize_value` or `deserialize_optional_value`; `JsValue` fields
498/// require no adapter.
499///
500/// # Errors
501///
502/// Returns an error if the value cannot be deserialized into the requested type.
503pub fn from_value<T: DeserializeOwned + 'static>(input: JsValue) -> Result<T, serde_json::Error> {
504    if TypeId::of::<T>() == TypeId::of::<Value>() {
505        let value: Box<dyn Any> = Box::new(input.to_json_value()?);
506        return value
507            .downcast::<T>()
508            .map(|value| *value)
509            .map_err(|_error| invalid("invalid JSON value type"));
510    }
511    T::deserialize(input)
512}
513
514/// Preserve arbitrary JSON object keys in a typed `serde_json::Value` field.
515///
516/// # Errors
517///
518/// Propagates errors from deserializing the JavaScript-compatible JSON representation.
519pub fn deserialize_value<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Value, D::Error> {
520    JsValue::deserialize(deserializer)?
521        .to_json_value()
522        .map_err(serde::de::Error::custom)
523}
524
525/// Optional arbitrary JSON field adapter; null retains normal Option semantics.
526///
527/// # Errors
528///
529/// Propagates errors from deserializing the optional JSON representation.
530pub fn deserialize_optional_value<'de, D: Deserializer<'de>>(
531    deserializer: D,
532) -> Result<Option<Value>, D::Error> {
533    Option::<JsValue>::deserialize(deserializer)?
534        .map(|value| value.to_json_value().map_err(serde::de::Error::custom))
535        .transpose()
536}
537
538/// Typed object adapter for arbitrary values inside a string-keyed map.
539///
540/// # Errors
541///
542/// Returns an error if the deserializer does not supply a valid JSON object.
543pub fn deserialize_map<'de, D: Deserializer<'de>>(
544    deserializer: D,
545) -> Result<serde_json::Map<String, Value>, D::Error> {
546    match deserialize_value(deserializer)? {
547        Value::Object(map) => Ok(map),
548        Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Array(_) => {
549            Err(serde::de::Error::custom("expected JSON object"))
550        }
551    }
552}
553
554/// Deserialize arbitrary values in an optional object field.
555///
556/// # Errors
557///
558/// Returns an error if the supplied non-null value is not a valid JSON object.
559pub fn deserialize_optional_map<'de, D: Deserializer<'de>>(
560    deserializer: D,
561) -> Result<Option<serde_json::Map<String, Value>>, D::Error> {
562    deserialize_optional_value(deserializer)?
563        .map(|value| match value {
564            Value::Object(map) => Ok(map),
565            Value::Null
566            | Value::Bool(_)
567            | Value::Number(_)
568            | Value::String(_)
569            | Value::Array(_) => Err(serde::de::Error::custom("expected JSON object")),
570        })
571        .transpose()
572}
573
574/// Preserve arbitrary values in flattened public view extension fields.
575///
576/// # Errors
577///
578/// Returns an error if the deserializer does not supply a valid JSON object.
579pub fn deserialize_btree_map<'de, D: Deserializer<'de>>(
580    deserializer: D,
581) -> Result<std::collections::BTreeMap<String, Value>, D::Error> {
582    deserialize_map(deserializer).map(|map| map.into_iter().collect())
583}
584
585fn invalid(message: &str) -> serde_json::Error {
586    <serde_json::Error as serde::de::Error>::custom(message)
587}
588
589#[must_use]
590pub fn number_as_f64(number: &Number) -> Option<f64> {
591    number.as_f64()
592}
593
594///
595/// # Errors
596///
597/// Returns an error if the number cannot be serialized in JavaScript-compatible notation.
598pub fn number_to_string(number: &Number) -> Result<String, serde_json::Error> {
599    Ok(ryu_js::Buffer::new()
600        .format(number_as_f64(number).ok_or_else(|| invalid("invalid JSON number"))?)
601        .to_owned())
602}
603
604/// Encode Serialize data exactly as JSON.stringify would encode its numbers.
605///
606/// # Errors
607///
608/// Returns an error if the input cannot be serialized as JavaScript-compatible JSON.
609pub fn to_vec<T: Serialize + ?Sized>(data: &T) -> Result<Vec<u8>, serde_json::Error> {
610    let value = JsValue::from(serde_json::to_value(data)?);
611    let mut bytes = Vec::new();
612    write_value(&value, &mut bytes)?;
613    Ok(bytes)
614}
615
616///
617/// # Errors
618///
619/// Returns an error if the input cannot be serialized as JavaScript-compatible JSON.
620pub fn to_string<T: Serialize + ?Sized>(data: &T) -> Result<String, serde_json::Error> {
621    String::from_utf8(to_vec(data)?).map_err(|_error| invalid("invalid JSON output UTF8"))
622}
623
624///
625/// # Errors
626///
627/// Returns an error if the `OpenAPI` document cannot be serialized.
628pub fn to_value<T: Serialize + ?Sized>(data: &T) -> Result<Value, serde_json::Error> {
629    finite_value(&JsValue::from(serde_json::to_value(data)?))
630}
631
632///
633/// # Errors
634///
635/// Propagates errors from the serializer.
636pub fn serialize<T: Serialize + ?Sized, S: Serializer>(
637    data: &T,
638    serializer: S,
639) -> Result<S::Ok, S::Error> {
640    to_value(data)
641        .map_err(serde::ser::Error::custom)?
642        .serialize(serializer)
643}
644
645#[expect(
646    clippy::as_conversions,
647    clippy::cast_possible_truncation,
648    clippy::cast_sign_loss,
649    reason = "JavaScript-compatible numbers deliberately retain IEEE754 rounding and guarded integer coercion at the wire or adapter boundary"
650)]
651fn finite_value(value: &JsValue) -> Result<Value, serde_json::Error> {
652    match value {
653        JsValue::Null => Ok(Value::Null),
654        JsValue::Bool(b) => Ok(Value::Bool(*b)),
655        JsValue::Number(n) if !n.is_finite() => Ok(Value::Null),
656        JsValue::Number(n)
657            if n.fract() == 0.0
658                && (-9_223_372_036_854_776_000.0..9_223_372_036_854_776_000.0).contains(n) =>
659        {
660            Ok(Value::Number(Number::from(*n as i64)))
661        }
662        JsValue::Number(n)
663            if n.fract() == 0.0 && (0.0..18_446_744_073_709_552_000.0).contains(n) =>
664        {
665            Ok(Value::Number(Number::from(*n as u64)))
666        }
667        JsValue::Number(n) => Number::from_f64(*n)
668            .map(Value::Number)
669            .ok_or_else(|| invalid("invalid finite JSON number")),
670        JsValue::String(s) => Ok(Value::String(s.clone())),
671        JsValue::Array(a) => a
672            .iter()
673            .map(finite_value)
674            .collect::<Result<_, _>>()
675            .map(Value::Array),
676        JsValue::Object(m) => {
677            let mut entries: Vec<_> = m.iter().collect();
678            entries.sort_by_key(|(name, _)| array_index(name).unwrap_or(u32::MAX));
679            let mut values = serde_json::Map::new();
680            for (key, value_2) in entries {
681                drop(values.insert(key.clone(), finite_value(value_2)?));
682            }
683            Ok(Value::Object(values))
684        }
685    }
686}
687
688fn write_value(value: &JsValue, bytes: &mut Vec<u8>) -> Result<(), serde_json::Error> {
689    match value {
690        JsValue::Null => bytes.extend_from_slice(b"null"),
691        JsValue::Bool(true) => bytes.extend_from_slice(b"true"),
692        JsValue::Bool(false) => bytes.extend_from_slice(b"false"),
693        JsValue::Number(n) => {
694            if n.is_finite() {
695                bytes.extend_from_slice(ryu_js::Buffer::new().format_finite(*n).as_bytes());
696            } else {
697                bytes.extend_from_slice(b"null");
698            }
699        }
700        JsValue::String(s) => serde_json::to_writer(bytes, s)?,
701        JsValue::Array(a) => {
702            bytes.push(b'[');
703            for (index, value_2) in a.iter().enumerate() {
704                if index != 0 {
705                    bytes.push(b',');
706                }
707                write_value(value_2, bytes)?;
708            }
709            bytes.push(b']');
710        }
711        JsValue::Object(m) => {
712            let mut entries: Vec<_> = m.iter().collect();
713            entries.sort_by_key(|(name, _)| array_index(name).unwrap_or(u32::MAX));
714            bytes.push(b'{');
715            for (index, (key, value_3)) in entries.into_iter().enumerate() {
716                if index != 0 {
717                    bytes.push(b',');
718                }
719                serde_json::to_writer(&mut *bytes, key)?;
720                bytes.push(b':');
721                write_value(value_3, bytes)?;
722            }
723            bytes.push(b'}');
724        }
725    }
726    Ok(())
727}
728
729fn array_index(name: &str) -> Option<u32> {
730    let index = name.parse::<u32>().ok()?;
731    (index != u32::MAX && index.to_string() == name).then_some(index)
732}