from __future__ import annotations

import collections.abc as c
import hashlib
import typing as t
from collections.abc import MutableMapping
from datetime import datetime
from datetime import timezone

from itsdangerous import BadSignature
from itsdangerous import URLSafeTimedSerializer
from werkzeug.datastructures import CallbackDict

from .json.tag import TaggedJSONSerializer

if t.TYPE_CHECKING:  # pragma: no cover
    import typing_extensions as te

    from .app import Flask
    from .wrappers import Request
    from .wrappers import Response

class SessionMixin(MutableMapping[str, t.Any]):
    """Expands a basic dictionary with session attributes."""

    @property
    def permanent(self) -> bool:
        """This reflects the ``'_permanent'`` key in the dict."""
        return self.get("_permanent", False)  # type: ignore[no-any-return]

    @permanent.setter
    def permanent(self, value: bool) -> None:
        self["_permanent"] = bool(value)

    #: Some implementations can detect whether a session is newly
    #: created, but that is not guaranteed. Use with caution. The mixin
    # default is hard-coded ``False``.
    new = False

    #: Some implementations can detect changes to the session and set
    #: this when that happens. The mixin default is hard coded to
    #: ``True``.
    modified = True

    accessed = False
    """Indicates if the session was accessed, even if it was not modified. This
    is set when the session object is accessed through the request context,
    including the global :data:`.session` proxy. A ``Vary: cookie`` header will
    be added if this is ``True``.

    .. versionchanged:: 3.1.3
        This is tracked by the request context.
    """

class SecureCookieSession(CallbackDict[str, t.Any], SessionMixin):
    """Base class for sessions based on signed cookies.

    This session backend will set the :attr:`modified` and
    :attr:`accessed` attributes. It cannot reliably track whether a
    session is new (vs. empty), so :attr:`new` remains hard coded to
    ``False``.
    """

    #: When data is changed, this is set to ``True``. Only the session
    #: dictionary itself is tracked; if the session contains mutable
    #: data (for example a nested dict) then this must be set to
    #: ``True`` manually when modifying that data. The session cookie
    #: will only be written to the response if this is ``True``.
    modified = False

    def __init__(
        self,
        initial: c.Mapping[str, t.Any] | None = None,
    ) -> None:
        def on_update(self: te.Self) -> None:
            self.modified = True

        super().__init__(initial, on_update)

class NullSession(SecureCookieSession):
    """Class used to generate nicer error messages if sessions are not
    available.  Will still allow read-only access to the empty session
    but fail on setting.
    """

    def _fail(self, *args: t.Any, **kwargs: t.Any) -> t.NoReturn:
        raise RuntimeError(
            "The session is unavailable because no secret "
            "key was set.  Set the secret_key on the "
            "application to something unique and secret."
        )

    __setitem__ = __delitem__ = clear = pop = popitem = update = setdefault = _fail
    del _fail

class SessionInterface:
    """The basic interface you have to implement in order to replace the
    default session interface which uses werkzeug's securecookie
    implementation.  The only methods you have to implement are
    :meth:`open_session` and :meth:`save_session`, the others have
    useful defaults which you don't need to change.

    The session object returned by the :meth:`open_session` method has to
    provide a dictionary like interface plus the properties and methods
    from the :class:`SessionMixin`.  We recommend just subclassing a dict
    and adding that mixin::

        class Session(dict, SessionMixin):
            pass

    If :meth:`open_session` returns ``None`` Flask will call into
    :meth:`make_null_session` to create a session that acts as replacement
    if the session support cannot work because some requirement is not
    fulfilled.  The default :class:`NullSession` class that is created
    will complain that the secret key was not set.

    To replace the session interface on an application all you have to do
    is to assign :attr:`flask.Flask.session_interface`::

        app = Flask(__name__)
        app.session_interface = MySessionInterface()

    Multiple requests with the same session may be sent and handled
    concurrently. When implementing a new session interface, consider
    whether reads or writes to the backing store must be synchronized.
    There is no guarantee on the order in which the session for each
    request is opened or saved, it will occur in the order that requests
    begin and end processing.

    .. versionadded:: 0.8
    """

    #: :meth:`make_null_session` will look here for the class that should
    #: be created when a null session is requested.  Likewise the
    #: :meth:`is_null_session` method will perform a typecheck against
    #: this type.
    null_session_class = NullSession

    #: A flag that indicates if the session interface is pickle based.
    #: This can be used by Flask extensions to make a decision in regards
    #: to how to deal with the session object.
    #:
    #: .. versionadded:: 0.10
    pickle_based = False

    def make_null_session(self, app: Flask) -> NullSession:
        """Creates a null session which acts as a replacement object if the
        real session support could not be loaded due to a configuration
        error.  This mainly aids the user experience because the job of the
        null session is to still support lookup without complaining but
        modifications are answered with a helpful error message of what
        failed.

        This creates an instance of :attr:`null_session_class` by default.
        """
        return self.null_session_class()

    def is_null_session(self, obj: object) -> bool:
        """Checks if a given object is a null session.  Null sessions are
        not asked to be saved.

        This checks if the object is an instance of :attr:`null_session_class`
        by default.
        """
        return isinstance(obj, self.null_session_class)

    def get_cookie_name(self, app: Flask) -> str:
        """The name of the session cookie. Uses``app.config["SESSION_COOKIE_NAME"]``."""
        return app.config["SESSION_COOKIE_NAME"]  # type: ignore[no-any-return]

    def get_cookie_domain(self, app: Flask) -> str | None:
        """The value of the ``Domain`` parameter on the session cookie. If not set,
        browsers will only send the cookie to the exact domain it was set from.
        Otherwise, they will send it to any subdomain of the given value as well.

        Uses the :data:`SESSION_COOKIE_DOMAIN` config.

        .. versionchanged:: 2.3
            Not set by default, does not fall back to ``SERVER_NAME``.
        """
        return app.config["SESSION_COOKIE_DOMAIN"]  # type: ignore[no-any-return]

    def get_cookie_path(self, app: Flask) -> str:
        """Returns the path for which the cookie should be valid.  The
        default implementation uses the value from the ``SESSION_COOKIE_PATH``
        config var if it's set, and falls back to ``APPLICATION_ROOT`` or
        uses ``/`` if it's ``None``.
        """
        return app.config["SESSION_COOKIE_PATH"] or app.config["APPLICATION_ROOT"]  # type: ignore[no-any-return]

    def get_cookie_httponly(self, app: Flask) -> bool:
        """Returns True if the session cookie should be httponly.  This
        currently just returns the value of the ``SESSION_COOKIE_HTTPONLY``
        config var.
        """
        return app.config["SESSION_COOKIE_HTTPONLY"]  # type: ignore[no-any-return]

    def get_cookie_secure(self, app: Flask) -> bool:
        """Returns True if the cookie should be secure.  This currently
        just returns the value of the ``SESSION_COOKIE_SECURE`` setting.
        """
        return app.config["SESSION_COOKIE_SECURE"]  # type: ignore[no-any-return]

    def get_cookie_samesite(self, app: Flask) -> str | None:
        """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the
        ``SameSite`` attribute. This currently just returns the value of
        the :data:`SESSION_COOKIE_SAMESITE` setting.
        """
        return app.config["SESSION_COOKIE_SAMESITE"]  # type: ignore[no-any-return]

    def get_cookie_partitioned(self, app: Flask) -> bool:
        """Returns True if the cookie should be partitioned. By default, uses
        the value of :data:`SESSION_COOKIE_PARTITIONED`.

        .. versionadded:: 3.1
        """
        return app.config["SESSION_COOKIE_PARTITIONED"]  # type: ignore[no-any-return]

    def get_expiration_time(self, app: Flask, session: SessionMixin) -> datetime | None:
        """A helper method that returns an expiration date for the session
        or ``None`` if the session is linked to the browser session.  The
        default implementation returns now + the permanent session
        lifetime configured on the application.
        """
        if session.permanent:
            return datetime.now(timezone.utc) + app.permanent_session_lifetime
        return None

    def should_set_cookie(self, app: Flask, session: SessionMixin) -> bool:
        """Used by session backends to determine if a ``Set-Cookie`` header
        should be set for this session cookie for this response. If the session
        has been modified, the cookie is set. If the session is permanent and
        the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is
        always set.

        This check is usually skipped if the session was deleted.

        .. versionadded:: 0.11
        """

        return session.modified or (
            session.permanent and app.config["SESSION_REFRESH_EACH_REQUEST"]
        )

    def open_session(self, app: Flask, request: Request) -> SessionMixin | None:
        """This is called at the beginning of each request, after
        pushing the request context, before matching the URL.

        This must return an object which implements a dictionary-like
        interface as well as the :class:`SessionMixin` interface.

        This will return ``None`` to indicate that loading failed in
        some way that is not immediately an error. The request
        context will fall back to using :meth:`make_null_session`
        in this case.
        """
        raise NotImplementedError()

    def save_session(
        self, app: Flask, session: SessionMixin, response: Response
    ) -> None:
        """This is called at the end of each request, after generating
        a response, before removing the request context. It is skipped
        if :meth:`is_null_session` returns ``True``.
        """
        raise NotImplementedError()

session_json_serializer = TaggedJSONSerializer()

def _lazy_sha1(string: bytes = b"") -> t.Any:
    """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include
    SHA-1, in which case the import and use as a default would fail before the
    developer can configure something else.
    """
    return hashlib.sha1(string)

class SecureCookieSessionInterface(SessionInterface):
    """The default session interface that stores sessions in signed cookies
    through the :mod:`itsdangerous` module.
    """

    #: the salt that should be applied on top of the secret key for the
    #: signing of cookie based sessions.
    salt = "cookie-session"
    #: the hash function to use for the signature.  The default is sha1
    digest_method = staticmethod(_lazy_sha1)
    #: the name of the itsdangerous supported key derivation.  The default
    #: is hmac.
    key_derivation = "hmac"
    #: A python serializer for the payload.  The default is a compact
    #: JSON derived serializer with support for some extra Python types
    #: such as datetime objects or tuples.
    serializer = session_json_serializer
    session_class = SecureCookieSession

    def get_signing_serializer(self, app: Flask) -> URLSafeTimedSerializer | None:
        if not app.secret_key:
            return None

        keys: list[str | bytes] = []

        if fallbacks := app.config["SECRET_KEY_FALLBACKS"]:
            keys.extend(fallbacks)

        keys.append(app.secret_key)  # itsdangerous expects current key at top
        return URLSafeTimedSerializer(
            keys,  # type: ignore[arg-type]
            salt=self.salt,
            serializer=self.serializer,
            signer_kwargs={
                "key_derivation": self.key_derivation,
                "digest_method": self.digest_method,
            },
        )

    def open_session(self, app: Flask, request: Request) -> SecureCookieSession | None:
        s = self.get_signing_serializer(app)
        if s is None:
            return None
        val = request.cookies.get(self.get_cookie_name(app))
        if not val:
            return self.session_class()
        max_age = int(app.permanent_session_lifetime.total_seconds())
        try:
            data = s.loads(val, max_age=max_age)
            return self.session_class(data)
        except BadSignature:
            return self.session_class()

    def save_session(
        self, app: Flask, session: SessionMixin, response: Response
    ) -> None:
        name = self.get_cookie_name(app)
        domain = self.get_cookie_domain(app)
        path = self.get_cookie_path(app)
        secure = self.get_cookie_secure(app)
        partitioned = self.get_cookie_partitioned(app)
        samesite = self.get_cookie_samesite(app)
        httponly = self.get_cookie_httponly(app)

        # Add a "Vary: Cookie" header if the session was accessed at all.
        if session.accessed:
            response.vary.add("Cookie")

        # If the session is modified to be empty, remove the cookie.
        # If the session is empty, return without setting the cookie.
        if not session:
            if session.modified:
                response.delete_cookie(
                    name,
                    domain=domain,
                    path=path,
                    secure=secure,
                    partitioned=partitioned,
                    samesite=samesite,
                    httponly=httponly,
                )
                response.vary.add("Cookie")

            return

        if not self.should_set_cookie(app, session):
            return

        expires = self.get_expiration_time(app, session)
        val = self.get_signing_serializer(app).dumps(dict(session))  # type: ignore[union-attr]
        response.set_cookie(
            name,
            val,
            expires=expires,
            httponly=httponly,
            domain=domain,
            path=path,
            secure=secure,
            partitioned=partitioned,
            samesite=samesite,
        )
        response.vary.add("Cookie")


# <small>nlohmann::</small>basic_json

<small>Defined in header `<nlohmann/json.hpp>`</small>

```cpp
template<
    template<typename U, typename V, typename... Args> class ObjectType = std::map,
    template<typename U, typename... Args> class ArrayType = std::vector,
    class StringType = std::string,
    class BooleanType = bool,
    class NumberIntegerType = std::int64_t,
    class NumberUnsignedType = std::uint64_t,
    class NumberFloatType = double,
    template<typename U> class AllocatorType = std::allocator,
    template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer,
    class BinaryType = std::vector<std::uint8_t>,
    class CustomBaseClass = void
>
class basic_json;
```

## Template parameters

| Template parameter   | Description                                                               | Derived type                                |
|----------------------|---------------------------------------------------------------------------|---------------------------------------------|
| `ObjectType`         | type for JSON objects                                                     | [`object_t`](object_t.md)                   |
| `ArrayType`          | type for JSON arrays                                                      | [`array_t`](array_t.md)                     |
| `StringType`         | type for JSON strings and object keys                                     | [`string_t`](string_t.md)                   |
| `BooleanType`        | type for JSON booleans                                                    | [`boolean_t`](boolean_t.md)                 |
| `NumberIntegerType`  | type for JSON integer numbers                                             | [`number_integer_t`](number_integer_t.md)   |
| `NumberUnsignedType` | type for JSON unsigned integer numbers                                    | [`number_unsigned_t`](number_unsigned_t.md) |
| `NumberFloatType`    | type for JSON floating-point numbers                                      | [`number_float_t`](number_float_t.md)       |
| `AllocatorType`      | type of the allocator to use                                              |                                             |
| `JSONSerializer`     | the serializer to resolve internal calls to `to_json()` and `from_json()` | [`json_serializer`](json_serializer.md)     |
| `BinaryType`         | type for binary arrays                                                    | [`binary_t`](binary_t.md)                   |
| `CustomBaseClass`    | extension point for user code                                             | [`json_base_class_t`](json_base_class_t.md) |

## Specializations

- [**json**](../json.md) - default specialization
- [**ordered_json**](../ordered_json.md) - a specialization that maintains the insertion order of object keys

## Iterator invalidation

All operations that add values to an **array** ([`push_back`](push_back.md) , [`operator+=`](operator+=.md),
[`emplace_back`](emplace_back.md), [`insert`](insert.md), and [`operator[]`](operator%5B%5D.md) for a non-existing
index) can yield a reallocation, in which case all iterators (including the [`end()`](end.md) iterator) and all
references to the elements are invalidated.

For [`ordered_json`](../ordered_json.md), also all operations that add a value to an **object**
([`push_back`](push_back.md), [`operator+=`](operator+=.md), [`emplace`](emplace.md), [`insert`](insert.md),
[`update`](update.md), and [`operator[]`](operator%5B%5D.md) for a non-existing key) can yield a reallocation, in
which case all iterators (including the [`end()`](end.md) iterator) and all references to the elements are invalidated.

## Requirements

The class satisfies the following concept requirements:

### Basic

- [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): JSON values can be
  default-constructed. The result will be a JSON null value.
- [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): A JSON value can be constructed
  from an rvalue argument.
- [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): A JSON value can be
  copy-constructed from an lvalue expression.
- [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): A JSON value can be assigned from an
  rvalue argument.
- [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): A JSON value can be copy-assigned from
  an lvalue expression.
- [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): JSON values can be destructed.

### Layout

- [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): JSON values have
  [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): All non-static data
  members are private and standard layout types, the class has no virtual functions or (virtual) base classes.

### Library-wide

- [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): JSON values can be compared with
  `==`, see [`operator==`](operator_eq.md).
- [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): JSON values can be compared with
  `<`, see [`operator<`](operator_le.md).
- [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): Any JSON lvalue or rvalue of can be swapped with
  any lvalue or rvalue of other compatible types, using unqualified function `swap`.
- [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): JSON values can be compared against
  `std::nullptr_t` objects which are used to model the `null` value.

### Container

- [Container](https://en.cppreference.com/w/cpp/named_req/Container): JSON values can be used like STL containers and
  provide iterator access.
- [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer): JSON values can be used like
  STL containers and provide reverse iterator access.

## Member types

- [**adl_serializer**](../adl_serializer/index.md) - the default serializer
- [**value_t**](value_t.md) - the JSON type enumeration
- [**json_pointer**](../json_pointer/index.md) - JSON Pointer implementation
- [**json_serializer**](json_serializer.md) - type of the serializer to for conversions from/to JSON
- [**error_handler_t**](error_handler_t.md) - type to choose behavior on decoding errors
- [**cbor_tag_handler_t**](cbor_tag_handler_t.md) - type to choose how to handle CBOR tags
- **initializer_list_t** - type for initializer lists of `basic_json` values
- [**input_format_t**](input_format_t.md) - type to choose the format to parse
- [**json_sax_t**](../json_sax/index.md) - type for SAX events

### Exceptions

- [**exception**](exception.md) - general exception of the `basic_json` class
    - [**parse_error**](parse_error.md) - exception indicating a parse error
    - [**invalid_iterator**](invalid_iterator.md) - exception indicating errors with iterators
    - [**type_error**](type_error.md) - exception indicating executing a member function with a wrong type
    - [**out_of_range**](out_of_range.md) - exception indicating access out of the defined range
    - [**other_error**](other_error.md) - exception indicating other library errors

### Container types

| Type                     | Definition                                                                                |
|--------------------------|-----------------------------------------------------------------------------------------------------------|
| `value_type`             | `#!cpp basic_json`                                                                                |
| `reference`              | `#!cpp value_type&`                                                                                |
| `const_reference`        | `#!cpp const value_type&`                                                                                |
| `difference_type`        | `#!cpp std::ptrdiff_t`                                                                                |
| `size_type`              | `#!cpp std::size_t`                                                                                |
| `allocator_type`         | `#!cpp AllocatorType<basic_json>`                                                                         |
| `pointer`                | `#!cpp std::allocator_traits<allocator_type>::pointer`                                                    |
| `const_pointer`          | `#!cpp std::allocator_traits<allocator_type>::const_pointer`                                              |
| `iterator`               | [LegacyBidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator)          |
| `const_iterator`         | constant [LegacyBidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator) |
| `reverse_iterator`       | reverse iterator, derived from `iterator`                                                                 |
| `const_reverse_iterator` | reverse iterator, derived from `const_iterator`                                                           |
| `iteration_proxy`        | helper type for [`items`](items.md) function                                                              |

### JSON value data types

- [**array_t**](array_t.md) - type for arrays
- [**binary_t**](binary_t.md) - type for binary arrays
- [**boolean_t**](boolean_t.md) - type for booleans
- [**default_object_comparator_t**](default_object_comparator_t.md) - default comparator for objects
- [**number_float_t**](number_float_t.md) - type for numbers (floating-point)
- [**number_integer_t**](number_integer_t.md) - type for numbers (integer)
- [**number_unsigned_t**](number_unsigned_t.md) - type for numbers (unsigned)
- [**object_comparator_t**](object_comparator_t.md) - comparator for objects
- [**object_t**](object_t.md) - type for objects
- [**string_t**](string_t.md) - type for strings

### Parser callback

- [**parse_event_t**](parse_event_t.md) - parser event types
- [**parser_callback_t**](parser_callback_t.md) - per-element parser callback type

## Member functions

- [(constructor)](basic_json.md)
- [(destructor)](~basic_json.md)
- [**operator=**](operator=.md) - copy assignment
- [**array**](array.md) (_static_) - explicitly create an array
- [**binary**](binary.md) (_static_) - explicitly create a binary array
- [**object**](object.md) (_static_) - explicitly create an object

### Object inspection

Functions to inspect the type of a JSON value.

- [**type**](type.md) - return the type of the JSON value
- [**operator value_t**](operator_value_t.md) - return the type of the JSON value
- [**type_name**](type_name.md) - return the type as string
- [**is_primitive**](is_primitive.md) - return whether the type is primitive
- [**is_structured**](is_structured.md) - return whether the type is structured
- [**is_null**](is_null.md) - return whether the value is null
- [**is_boolean**](is_boolean.md) - return whether the value is a boolean
- [**is_number**](is_number.md) - return whether the value is a number
- [**is_number_integer**](is_number_integer.md) - return whether the value is an integer number
- [**is_number_unsigned**](is_number_unsigned.md) - return whether the value is an unsigned integer number
- [**is_number_float**](is_number_float.md) - return whether the value is a floating-point number
- [**is_object**](is_object.md) - return whether the value is an object
- [**is_array**](is_array.md) - return whether the value is an array
- [**is_string**](is_string.md) - return whether the value is a string
- [**is_binary**](is_binary.md) - return whether the value is a binary array
- [**is_discarded**](is_discarded.md) - return whether the value is discarded

Optional functions to access the [diagnostic positions](../macros/json_diagnostic_positions.md).

- [**start_pos**](start_pos.md) - return the start position of the value
- [**end_pos**](end_pos.md) - return the one past the end position of the value

### Value access

Direct access to the stored value of a JSON value.

- [**get**](get.md) - get a value
- [**get_to**](get_to.md) - get a value and write it to a destination
- [**get_ptr**](get_ptr.md) - get a pointer value
- [**get_ref**](get_ref.md) - get a reference value
- [**operator ValueType**](operator_ValueType.md) - get a value
- [**get_binary**](get_binary.md) - get a binary value

### Element access

Access to the JSON value

- [**at**](at.md) - access specified element with bounds checking
- [**operator[]**](operator[].md) - access specified element
- [**value**](value.md) - access specified object element with default value
- [**front**](front.md) - access the first element
- [**back**](back.md) - access the last element

### Lookup

- [**find**](find.md) - find an element in a JSON object
- [**count**](count.md) - returns the number of occurrences of a key in a JSON object
- [**contains**](contains.md) - check the existence of an element in a JSON object

### Iterators

- [**begin**](begin.md) - returns an iterator to the first element
- [**cbegin**](cbegin.md) - returns a const iterator to the first element
- [**end**](end.md) - returns an iterator to one past the last element
- [**cend**](cend.md) - returns a const iterator to one past the last element
- [**rbegin**](rbegin.md) - returns an iterator to the reverse-beginning
- [**rend**](rend.md) - returns an iterator to the reverse-end
- [**crbegin**](crbegin.md) - returns a const iterator to the reverse-beginning
- [**crend**](crend.md) - returns a const iterator to the reverse-end
- [**items**](items.md) - wrapper to access iterator member functions in range-based for

### Capacity

- [**empty**](empty.md) - checks whether the container is empty
- [**size**](size.md) - returns the number of elements
- [**max_size**](max_size.md) - returns the maximum possible number of elements

### Modifiers

- [**clear**](clear.md) - clears the contents
- [**push_back**](push_back.md) - add a value to an array/object
- [**operator+=**](operator+=.md) - add a value to an array/object
- [**emplace_back**](emplace_back.md) - add a value to an array
- [**emplace**](emplace.md) - add a value to an object if a key does not exist
- [**erase**](erase.md) - remove elements
- [**insert**](insert.md) - inserts elements
- [**update**](update.md) - updates a JSON object from another object, overwriting existing keys
- [**swap**](swap.md) - exchanges the values

### Lexicographical comparison operators

- [**operator==**](operator_eq.md) - comparison: equal
- [**operator!=**](operator_ne.md) - comparison: not equal
- [**operator<**](operator_lt.md) - comparison: less than
- [**operator>**](operator_gt.md) - comparison: greater than
- [**operator<=**](operator_le.md) - comparison: less than or equal
- [**operator>=**](operator_ge.md) - comparison: greater than or equal
- [**operator<=>**](operator_spaceship.md) - comparison: 3-way

### Serialization / Dumping

- [**dump**](dump.md) - serialization

### Deserialization / Parsing

- [**parse**](parse.md) (_static_) - deserialize from a compatible input
- [**accept**](accept.md) (_static_) - check if the input is valid JSON
- [**sax_parse**](sax_parse.md) (_static_) - generate SAX events

### JSON Pointer functions

- [**flatten**](flatten.md) - return flattened JSON value
- [**unflatten**](unflatten.md) - unflatten a previously flattened JSON value

### JSON Patch functions

- [**patch**](patch.md) - applies a JSON patch
- [**patch_inplace**](patch_inplace.md) - applies a JSON patch in place
- [**diff**](diff.md) (_static_) - creates a diff as a JSON patch

### JSON Merge Patch functions

- [**merge_patch**](merge_patch.md) - applies a JSON Merge Patch

## Static functions

- [**meta**](meta.md) - returns version information on the library
- [**get_allocator**](get_allocator.md) - returns the allocator associated with the container

### Binary formats

- [**from_bjdata**](from_bjdata.md) (_static_) - create a JSON value from an input in BJData format
- [**from_bson**](from_bson.md) (_static_) - create a JSON value from an input in BSON format
- [**from_cbor**](from_cbor.md) (_static_) - create a JSON value from an input in CBOR format
- [**from_msgpack**](from_msgpack.md) (_static_) - create a JSON value from an input in MessagePack format
- [**from_ubjson**](from_ubjson.md) (_static_) - create a JSON value from an input in UBJSON format
- [**to_bjdata**](to_bjdata.md) (_static_) - create a BJData serialization of a given JSON value
- [**to_bson**](to_bson.md) (_static_) - create a BSON serialization of a given JSON value
- [**to_cbor**](to_cbor.md) (_static_) - create a CBOR serialization of a given JSON value
- [**to_msgpack**](to_msgpack.md) (_static_) - create a MessagePack serialization of a given JSON value
- [**to_ubjson**](to_ubjson.md) (_static_) - create a UBJSON serialization of a given JSON value

## Non-member functions

- [**operator<<(std::ostream&)**](../operator_ltlt.md) - serialize to stream
- [**operator>>(std::istream&)**](../operator_gtgt.md) - deserialize from stream
- [**to_string**](to_string.md) - user-defined `to_string` function for JSON values
- [**format_as**](format_as.md) - user-defined `format_as` function for JSON values (fmt support)

## Literals

- [**operator""_json**](../operator_literal_json.md) - user-defined string literal for JSON values

## Helper classes

- [**std::formatter<basic_json>**](std_formatter.md) - make JSON values formattable with `std::format`
- [**std::hash<basic_json>**](std_hash.md) - return a hash value for a JSON object
- [**std::swap<basic_json>**](std_swap.md) - exchanges the values of two JSON objects

## Examples

??? example

    The example shows how the library is used.

    ```cpp
    --8<-- "examples/README.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/README.output"
    ```

## See also

- [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange Format](https://tools.ietf.org/html/rfc8259)

## Version history

- Added in version 1.0.0.


import { expect, test } from 'vitest'
import { isBuild, page, viteTestUrl } from '~utils'

test.runIf(isBuild)('includes only a single script tag', async () => {
  await page.goto(viteTestUrl + '/no-polyfills-no-systemjs.html')

  await expect
    .poll(() => page.getAttribute('#vite-legacy-entry', 'data-src'))
    .toMatch(/.\/assets\/index-legacy-(.+)\.js/)

  expect(await page.locator('script').count()).toBe(1)
  expect(await page.locator('#vite-legacy-polyfill').count()).toBe(0)
  expect(await page.locator('#vite-legacy-entry').count()).toBe(1)
})


# Types

This page gives an overview of how JSON values are stored and how this can be configured.

## Overview

By default, JSON values are stored as follows:

| JSON type | C++ type                                      |
|-----------|-----------------------------------------------|
| object    | `std::map<std::string, basic_json>`           |
| array     | `std::vector<basic_json>`                     |
| null      | `std::nullptr_t`                              |
| string    | `std::string`                                 |
| boolean   | `bool`                                        |
| number    | `std::int64_t`, `std::uint64_t`, and `double` |

Note there are three different types for numbers - when parsing JSON text, the best fitting type is chosen.

## Storage

```mermaid
classDiagram

class value_t {
    <<enumeration>>
    null
    object
    array
    string
    boolean
    number_integer
    number_unsigned
    number_float
    binary
    discarded
}

class json_value {
    <<union>>
    object_t* object
    array_t* array
    string_t* string
    binary_t* binary
    boolean_t boolean
    number_integer_t number_integer
    number_unsigned_t number_unsigned
    number_float_t number_float
}

class basic_json {
    -value_t m_type
    -json_value m_value
    +typedef object_t
    +typedef array_t
    +typedef binary_t
    +typedef boolean_t
    +typedef number_integer_t
    +typedef number_unsigned_t
    +typedef number_float_t
}

basic_json .. json_value
basic_json .. value_t
```

## Template arguments

The data types to store a JSON value are derived from the template arguments passed to class `basic_json`:

```cpp
template<
    template<typename U, typename V, typename... Args> class ObjectType = std::map,
    template<typename U, typename... Args> class ArrayType = std::vector,
    class StringType = std::string,
    class BooleanType = bool,
    class NumberIntegerType = std::int64_t,
    class NumberUnsignedType = std::uint64_t,
    class NumberFloatType = double,
    template<typename U> class AllocatorType = std::allocator,
    template<typename T, typename SFINAE = void> class JSONSerializer = adl_serializer,
    class BinaryType = std::vector<std::uint8_t>
>
class basic_json;
```

Type `json` is an alias for `basic_json<>` and uses the default types.

From the template arguments, the following types are derived:

```cpp
using object_comparator_t = std::less<>;
using object_t = ObjectType<StringType, basic_json, object_comparator_t,
                   AllocatorType<std::pair<const StringType, basic_json>>>;

using array_t = ArrayType<basic_json, AllocatorType<basic_json>>;

using string_t = StringType;

using boolean_t = BooleanType;

using number_integer_t = NumberIntegerType;
using number_unsigned_t = NumberUnsignedType;
using number_float_t = NumberFloatType;

using binary_t = nlohmann::byte_container_with_subtype<BinaryType>;
```

## Objects

[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows:

> An object is an unordered collection of zero or more name/value pairs, where a name is a string and a value is a string, number, boolean, null, object, or array.

### Default type

With the default values for *ObjectType* (`std::map`), *StringType* (`std::string`), and *AllocatorType* (`std::allocator`), the default value for `object_t` is:

```cpp
std::map<
  std::string, // key_type
  basic_json, // value_type
  std::less<>, // key_compare
  std::allocator<std::pair<const std::string, basic_json>> // allocator_type
>
```

### Behavior

The choice of `object_t` influences the behavior of the JSON class. With the default type, objects have the following behavior:

- When all names are unique, objects will be interoperable in the sense that all software implementations receiving that object will agree on the name-value mappings.
- When the names within an object are not unique, it is unspecified which one of the values for a given key will be chosen. For instance, `#!json {"key": 2, "key": 1}` could be equal to either `#!json {"key": 1}` or `#!json {"key": 2}`. To reject duplicate keys instead of silently resolving them one way or another, see [this parsing recipe](../parsing/parser_callbacks.md#recipe-rejecting-duplicate-object-keys).
- Internally, name/value pairs are stored in lexicographical order of the names. Objects will also be serialized (see `dump`) in this order. For instance, both `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be stored and serialized as `#!json {"a": 2, "b": 1}`.
- When comparing objects, the order of the name/value pairs is irrelevant. This makes objects interoperable in the sense that they will not be affected by these differences. For instance, `#!json {"b": 1, "a": 2}` and `#!json {"a": 2, "b": 1}` will be treated as equal.

### Key order

The order in which name/value pairs are added to the object is *not* preserved by the library. Therefore, iterating an object may return name/value pairs in a different order than they were originally stored. In fact, keys will be traversed in alphabetical order as `std::map` with `std::less` is used by default. Please note this behavior conforms to [RFC 8259](https://tools.ietf.org/html/rfc8259), because any order implements the specified "unordered" nature of JSON objects.

### Limits

[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:

> An implementation may set limits on the maximum depth of nesting.

In this class, the object's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the `max_size` function of a JSON object.

### Storage

Objects are stored as pointers in a `basic_json` type. That is, for any access to object values, a pointer of type `object_t*` must be dereferenced.

### Converting maps with non-string keys

A `std::map`/`std::unordered_map` whose key type is not string-like (e.g., `std::map<int, std::string>`) is
converted to a JSON *array* of 2-element `[key, value]` arrays rather than a JSON object, because JSON object
keys must be strings:

```cpp
std::map<int, std::string> m{{1, "one"}, {2, "two"}};
json j = m;
// j is [[1,"one"],[2,"two"]], not {"1":"one","2":"two"}
```

## Arrays

[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows:

> An array is an ordered sequence of zero or more values.

### Default type

With the default values for *ArrayType* (`std::vector`) and *AllocatorType* (`std::allocator`), the default value for `array_t` is:

```cpp
std::vector<
  basic_json, // value_type
  std::allocator<basic_json> // allocator_type
>
```

### Limits

[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:

> An implementation may set limits on the maximum depth of nesting.

In this class, the array's limit of nesting is not explicitly constrained. However, a maximum depth of nesting may be introduced by the compiler or runtime environment. A theoretical limit can be queried by calling the `max_size` function of a JSON array.

### Storage

Arrays are stored as pointers in a `basic_json` type. That is, for any access to array values, a pointer of type `array_t*` must be dereferenced.

## Strings

[RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows:

> A string is a sequence of zero or more Unicode characters.

Unicode values are split by the JSON class into byte-sized characters during deserialization.

### Default type

With the default values for *StringType* (`std::string`), the default value for `string_t` is `#!cpp std::string`.

### Encoding

Strings are stored in UTF-8 encoding. Therefore, functions like `std::string::size()` or `std::string::length()` return the number of **bytes** in the string rather than the number of characters or glyphs.

### String comparison

[RFC 8259](https://tools.ietf.org/html/rfc8259) states:

> Software implementations are typically required to test names of object members for equality. Implementations that transform the textual representation into sequences of Unicode code units and then perform the comparison numerically, code unit by code unit are interoperable in the sense that implementations will agree in all cases on equality or inequality of two strings. For example, implementations that compare strings with escaped characters unconverted may incorrectly find that `"a\\b"` and `"a\u005Cb"` are not equal.

This implementation is interoperable as it does compare strings code unit by code unit.

### Storage

String values are stored as pointers in a `basic_json` type. That is, for any access to string values, a pointer of type `string_t*` must be dereferenced.

## Booleans

[RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a type which differentiates the two literals `true` and `false`.

### Default type

With the default values for *BooleanType* (`#!cpp bool`), the default value for `boolean_t` is `#!cpp bool`.

### Storage

Boolean values are stored directly inside a `basic_json` type.

## Numbers

See the [number handling](number_handling.md) article for a detailed discussion on how numbers are handled by this library.

[RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows:

> The representation of numbers is similar to that used in most programming languages. A number is represented in base 10 using decimal digits. It contains an integer component that may be prefixed with an optional minus sign, which may be followed by a fraction part and/or an exponent part. Leading zeros are not allowed. (...) Numeric values that cannot be represented in the grammar below (such as Infinity and NaN) are not permitted.

This description includes both integer and floating-point numbers. However, C++ allows more precise storage if it is known whether the number is a signed integer, an unsigned integer, or a floating-point number. Therefore, three different types, `number_integer_t`, `number_unsigned_t`, and `number_float_t` are used.

### Default types

With the default values for *NumberIntegerType* (`std::int64_t`), the default value for `number_integer_t` is `std::int64_t`.
With the default values for *NumberUnsignedType* (`std::uint64_t`), the default value for `number_unsigned_t` is `std::uint64_t`.
With the default values for *NumberFloatType* (`#!cpp double`), the default value for `number_float_t` is `#!cpp double`.

### Default behavior

- The restrictions about leading zeros are not enforced in C++. Instead, leading zeros in integer literals lead to an interpretation as an octal number. Internally, the value will be stored as a decimal number. For instance, the C++ integer literal `#!c 010` will be serialized to `#!c 8`. During deserialization, leading zeros yield an error.
- Not-a-number (NaN) values will be serialized to `#!json null`.

### Limits

[RFC 8259](https://tools.ietf.org/html/rfc8259) specifies:

> An implementation may set limits on the range and precision of numbers.

When the default type is used, the maximal integer number that can be stored is `#!c 9223372036854775807` (`INT64_MAX`) and the minimal integer number that can be stored is `#!c -9223372036854775808` (`INT64_MIN`). Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored as `number_unsigned_t` or `number_float_t`.

When the default type is used, the maximal unsigned integer number that can be stored is `#!c 18446744073709551615` (`UINT64_MAX`) and the minimal integer number that can be stored is `#!c 0`. Integer numbers that are out of range will yield over/underflow when used in a constructor. During deserialization, too large or small integer numbers will automatically be stored as `number_integer_t` or `number_float_t`.

[RFC 8259](https://tools.ietf.org/html/rfc8259) further states:

> Note that when such software is used, numbers that are integers and are in the range $[-2^{53}+1, 2^{53}-1]$ are interoperable in the sense that implementations will agree exactly on their numeric values.

As this range is a subrange of the exactly supported range [`INT64_MIN`, `INT64_MAX`], this class's integer type is interoperable.

[RFC 8259](https://tools.ietf.org/html/rfc8259) states:

> This specification allows implementations to set limits on the range and precision of numbers accepted. Since software that implements IEEE 754-2008 binary64 (double precision) numbers is generally available and widely used, good interoperability can be achieved by implementations that expect no more precision or range than these provide, in the sense that implementations will approximate JSON numbers within the expected precision.

This implementation does exactly follow this approach, as it uses double precision floating-point numbers. Note values smaller than `#!c -1.79769313486232e+308` and values greater than `#!c 1.79769313486232e+308` will be stored as NaN internally and be serialized to `#!json null`.

### Storage

Integer number values, unsigned integer number values, and floating-point number values are stored directly inside a `basic_json` type.


//     __ _____ _____ _____
//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
// |  |  |__   |  |  | | | |  version 3.12.0
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT

#include "doctest_compatibility.h"

// for some reason including this after the json header leads to linker errors with VS 2017...
#include <locale>

#include <nlohmann/json.hpp>
using nlohmann::json;

#include <fstream>
#include <sstream>
#include <iostream>
#include <iomanip>
#include "make_test_data_available.hpp"

// this test suite uses static variables with non-trivial destructors
DOCTEST_CLANG_SUPPRESS_WARNING_PUSH
DOCTEST_CLANG_SUPPRESS_WARNING("-Wexit-time-destructors")

namespace
{
extern size_t calls;
size_t calls = 0;

void check_utf8dump(bool success_expected, int byte1, int byte2, int byte3, int byte4);

void check_utf8dump(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
    static std::string json_string;
    json_string.clear();

    CAPTURE(byte1)
    CAPTURE(byte2)
    CAPTURE(byte3)
    CAPTURE(byte4)

    json_string += std::string(1, static_cast<char>(byte1));

    if (byte2 != -1)
    {
        json_string += std::string(1, static_cast<char>(byte2));
    }

    if (byte3 != -1)
    {
        json_string += std::string(1, static_cast<char>(byte3));
    }

    if (byte4 != -1)
    {
        json_string += std::string(1, static_cast<char>(byte4));
    }

    CAPTURE(json_string)

    // store the string in a JSON value
    static json j;
    static json j2;
    j = json_string;
    j2 = "abc" + json_string + "xyz";

    static std::string s_ignored;
    static std::string s_ignored2;
    static std::string s_ignored_ascii;
    static std::string s_ignored2_ascii;
    static std::string s_replaced;
    static std::string s_replaced2;
    static std::string s_replaced_ascii;
    static std::string s_replaced2_ascii;

    // dumping with ignore/replace must not throw in any case
    s_ignored = j.dump(-1, ' ', false, json::error_handler_t::ignore);
    s_ignored2 = j2.dump(-1, ' ', false, json::error_handler_t::ignore);
    s_ignored_ascii = j.dump(-1, ' ', true, json::error_handler_t::ignore);
    s_ignored2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::ignore);
    s_replaced = j.dump(-1, ' ', false, json::error_handler_t::replace);
    s_replaced2 = j2.dump(-1, ' ', false, json::error_handler_t::replace);
    s_replaced_ascii = j.dump(-1, ' ', true, json::error_handler_t::replace);
    s_replaced2_ascii = j2.dump(-1, ' ', true, json::error_handler_t::replace);

    if (success_expected)
    {
        static std::string s_strict;
        // strict mode must not throw if success is expected
        s_strict = j.dump();
        // all dumps should agree on the string
        CHECK(s_strict == s_ignored);
        CHECK(s_strict == s_replaced);
    }
    else
    {
        // strict mode must throw if success is not expected
        CHECK_THROWS_AS(j.dump(), json::type_error&);
        // ignore and replace must create different dumps
        CHECK(s_ignored != s_replaced);

        // check that replace string contains a replacement character
        CHECK(s_replaced.find("\xEF\xBF\xBD") != std::string::npos);
    }

    // check that prefix and suffix are preserved
    CHECK(s_ignored2.substr(1, 3) == "abc");
    CHECK(s_ignored2.substr(s_ignored2.size() - 4, 3) == "xyz");
    CHECK(s_ignored2_ascii.substr(1, 3) == "abc");
    CHECK(s_ignored2_ascii.substr(s_ignored2_ascii.size() - 4, 3) == "xyz");
    CHECK(s_replaced2.substr(1, 3) == "abc");
    CHECK(s_replaced2.substr(s_replaced2.size() - 4, 3) == "xyz");
    CHECK(s_replaced2_ascii.substr(1, 3) == "abc");
    CHECK(s_replaced2_ascii.substr(s_replaced2_ascii.size() - 4, 3) == "xyz");
}

void check_utf8string(bool success_expected, int byte1, int byte2, int byte3, int byte4);

// create and check a JSON string with up to four UTF-8 bytes
void check_utf8string(bool success_expected, int byte1, int byte2 = -1, int byte3 = -1, int byte4 = -1)
{
    if (++calls % 100000 == 0)
    {
        std::cout << calls << " of 1641521 UTF-8 strings checked" << std::endl; // NOLINT(performance-avoid-endl)
    }

    static std::string json_string;
    json_string = "\"";

    CAPTURE(byte1)
    json_string += std::string(1, static_cast<char>(byte1));

    if (byte2 != -1)
    {
        CAPTURE(byte2)
        json_string += std::string(1, static_cast<char>(byte2));
    }

    if (byte3 != -1)
    {
        CAPTURE(byte3)
        json_string += std::string(1, static_cast<char>(byte3));
    }

    if (byte4 != -1)
    {
        CAPTURE(byte4)
        json_string += std::string(1, static_cast<char>(byte4));
    }

    json_string += "\"";

    CAPTURE(json_string)

    json _;
    if (success_expected)
    {
        CHECK_NOTHROW(_ = json::parse(json_string));
    }
    else
    {
        CHECK_THROWS_AS(_ = json::parse(json_string), json::parse_error&);
    }
}
} // namespace

TEST_CASE("Unicode (3/5)" * doctest::skip())
{
    SECTION("RFC 3629")
    {
        /*
        RFC 3629 describes in Sect. 4 the syntax of UTF-8 byte sequences as
        follows:

            A UTF-8 string is a sequence of octets representing a sequence of UCS
            characters.  An octet sequence is valid UTF-8 only if it matches the
            following syntax, which is derived from the rules for encoding UTF-8
            and is expressed in the ABNF of [RFC2234].

            UTF8-octets = *( UTF8-char )
            UTF8-char   = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4
            UTF8-1      = %x00-7F
            UTF8-2      = %xC2-DF UTF8-tail
            UTF8-3      = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8-tail ) /
                          %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8-tail )
            UTF8-4      = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8-tail ) /
                          %xF4 %x80-8F 2( UTF8-tail )
            UTF8-tail   = %x80-BF
        */

        SECTION("UTF8-4 (xF0 x90-BF UTF8-tail UTF8-tail)")
        {
            SECTION("well-formed")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
                    {
                        for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
                        {
                            for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
                            {
                                check_utf8string(true, byte1, byte2, byte3, byte4);
                                check_utf8dump(true, byte1, byte2, byte3, byte4);
                            }
                        }
                    }
                }
            }

            SECTION("ill-formed: missing second byte")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    check_utf8string(false, byte1);
                    check_utf8dump(false, byte1);
                }
            }

            SECTION("ill-formed: missing third byte")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
                    {
                        check_utf8string(false, byte1, byte2);
                        check_utf8dump(false, byte1, byte2);
                    }
                }
            }

            SECTION("ill-formed: missing fourth byte")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
                    {
                        for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
                        {
                            check_utf8string(false, byte1, byte2, byte3);
                            check_utf8dump(false, byte1, byte2, byte3);
                        }
                    }
                }
            }

            SECTION("ill-formed: wrong second byte")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    for (int byte2 = 0x00; byte2 <= 0xFF; ++byte2)
                    {
                        // skip correct second byte
                        if (0x90 <= byte2 && byte2 <= 0xBF)
                        {
                            continue;
                        }

                        for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
                        {
                            for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
                            {
                                check_utf8string(false, byte1, byte2, byte3, byte4);
                                check_utf8dump(false, byte1, byte2, byte3, byte4);
                            }
                        }
                    }
                }
            }

            SECTION("ill-formed: wrong third byte")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
                    {
                        for (int byte3 = 0x00; byte3 <= 0xFF; ++byte3)
                        {
                            // skip correct third byte
                            if (0x80 <= byte3 && byte3 <= 0xBF)
                            {
                                continue;
                            }

                            for (int byte4 = 0x80; byte4 <= 0xBF; ++byte4)
                            {
                                check_utf8string(false, byte1, byte2, byte3, byte4);
                                check_utf8dump(false, byte1, byte2, byte3, byte4);
                            }
                        }
                    }
                }
            }

            SECTION("ill-formed: wrong fourth byte")
            {
                for (int byte1 = 0xF0; byte1 <= 0xF0; ++byte1)
                {
                    for (int byte2 = 0x90; byte2 <= 0xBF; ++byte2)
                    {
                        for (int byte3 = 0x80; byte3 <= 0xBF; ++byte3)
                        {
                            for (int byte4 = 0x00; byte4 <= 0xFF; ++byte4)
                            {
                                // skip fourth second byte
                                if (0x80 <= byte3 && byte3 <= 0xBF)
                                {
                                    continue;
                                }

                                check_utf8string(false, byte1, byte2, byte3, byte4);
                                check_utf8dump(false, byte1, byte2, byte3, byte4);
                            }
                        }
                    }
                }
            }
        }
    }
}

DOCTEST_CLANG_SUPPRESS_WARNING_POP


//     __ _____ _____ _____
//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
// |  |  |__   |  |  | | | |  version 3.12.0
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest_compatibility.h"


#include <iostream>
#include <nlohmann/json.hpp>

using json = nlohmann::json;

int main()
{
    // create a JSON number
    json value = 17;

    // explicitly getting references
    auto r1 = value.get_ref<const json::number_integer_t&>();
    auto r2 = value.get_ref<json::number_integer_t&>();

    // print the values
    std::cout << r1 << ' ' << r2 << '\n';

    // incompatible type throws exception
    try
    {
        auto r3 = value.get_ref<json::number_float_t&>();
    }
    catch (const json::type_error& ex)
    {
        std::cout << ex.what() << '\n';
    }
}


import { setTimeout } from 'node:timers/promises'
import {
  type BindingClientHmrUpdate,
  type DevEngine,
  dev,
} from 'rolldown/experimental'
import type { RolldownOutput } from 'rolldown'
import colors from 'picocolors'
import getEtag from 'etag'
import { ChunkMetadataMap, resolveRolldownOptions } from '../build'
import { BUNDLED_DEV_CLIENT_FILENAME } from '../constants'
import { getHmrImplementation } from '../plugins/clientInjections'
import { createDebugger, formatAndTruncateFileList } from '../utils'
import type { DevEnvironment } from './environment'
import { type NormalizedHotChannelClient, debugHmr, getShortName } from './hmr'
import { prepareError } from './middlewares/error'

const debug = createDebugger('vite:full-bundle-mode')

type HmrOutput = BindingClientHmrUpdate['update']

type MemoryFile = {
  source: string | Uint8Array
  etag?: string
}

export class MemoryFiles {
  private files = new Map<string, MemoryFile | (() => MemoryFile)>()

  get size(): number {
    return this.files.size
  }

  get(file: string): MemoryFile | undefined {
    const result = this.files.get(file)
    if (result === undefined) {
      return undefined
    }
    if (typeof result === 'function') {
      const content = result()
      this.files.set(file, content)
      return content
    }
    return result
  }

  set(file: string, content: MemoryFile | (() => MemoryFile)): void {
    this.files.set(file, content)
  }

  has(file: string): boolean {
    return this.files.has(file)
  }

  clear(): void {
    this.files.clear()
  }
}

export class BundledDev {
  private _devEngine!: DevEngine
  private viteRuntime?: string
  private initialBuildCompleted = false
  private _closed = false
  private clients = new Clients()
  private debouncedFullReload = debounce(20, () => {
    this.environment.hot.send({ type: 'full-reload', path: '*' })
    this.environment.logger.info(colors.green(`page reload`), {
      timestamp: true,
    })
  })

  private fullReloadPending = false

  private reloadNeededClientIds = new Set<string>()
  private debouncedReloadNeededFlush = debounce(20, () => {
    if (this.lastBuildError || this.reloadNeededClientIds.size === 0) return
    for (const clientId of this.reloadNeededClientIds) {
      this.clients.get(clientId)?.send({ type: 'full-reload', path: '*' })
    }
    this.reloadNeededClientIds.clear()
    this.environment.logger.info(colors.green(`page reload`), {
      timestamp: true,
    })
  })

  private lastBuildError: Error | null = null

  memoryFiles: MemoryFiles = new MemoryFiles()

  constructor(private environment: DevEnvironment) {
    if (environment.name !== 'client') {
      throw new Error(
        'currently full bundle mode is only available for client environment',
      )
    }
  }

  private get devEngine(): DevEngine {
    if (!this._devEngine) {
      throw new Error(`dev engine was not yet initialized`)
    }
    return this._devEngine
  }

  private pendingPayloadFilenames = new Set<string>()

  get hasBuildOutput(): boolean {
    return (
      this.memoryFiles.size > 1 ||
      (this.memoryFiles.size === 1 &&
        !this.memoryFiles.has(BUNDLED_DEV_CLIENT_FILENAME))
    )
  }

  async listen(): Promise<void> {
    this._closed = false
    debug?.('INITIAL: setup bundle options')
    const rolldownOptions = await this.getRolldownOptions()
    // NOTE: only single outputOptions is supported here
    if (
      Array.isArray(rolldownOptions.output) &&
      rolldownOptions.output.length > 1
    ) {
      throw new Error('multiple output options are not supported in dev mode')
    }
    const outputOptions = (
      Array.isArray(rolldownOptions.output)
        ? rolldownOptions.output[0]
        : rolldownOptions.output
    )!

    this.environment.hot.on(
      'vite:client-connected',
      async (payload, client) => {
        this.clients.setupIfNeeded(client, payload.clientId)
        this.devEngine.registerClient(payload.clientId)
      },
    )
    this.environment.hot.on('vite:client:connect', (_payload, client) => {
      // Replay the cached build error to freshly connected clients.
      if (this.lastBuildError) {
        debug?.('REPLAY: replaying last build error to newly connected client')

        client.send({
          type: 'error',
          err: prepareError(this.lastBuildError),
        })
      }
    })
    this.environment.hot.on('vite:client:disconnect', (_payload, client) => {
      const clientId = this.clients.delete(client)
      if (clientId) {
        this.devEngine.removeClient(clientId)
        this.reloadNeededClientIds.delete(clientId)
      }
    })
    this.environment.hot.on(
      'vite:bundled-dev:reload-needed',
      (payload, client) => {
        const clientId = this.clients.getId(client)
        if (!clientId) return
        debug?.(
          `TRIGGER: client ${clientId} requested a page reload (${payload.reason})`,
        )
        this.environment.logger.info(
          colors.green(`bundling for page reload `) +
            colors.dim(payload.reason),
          { clear: true, timestamp: true },
        )
        this.reloadNeededClientIds.add(clientId)
        this.ensureOutputAndFlushReloadNeeded()
      },
    )

    this._devEngine = await dev(rolldownOptions, outputOptions, {
      onHmrUpdates: (result) => {
        if (result instanceof Error) {
          this.environment.logger.error(
            colors.red(`✘ Build error: ${result.message}`),
            {
              error: result,
            },
          )
          // TODO: send to the specific client
          for (const client of this.clients.getAll()) {
            client.send({
              type: 'error',
              err: prepareError(result),
            })
          }
          return
        }
        const { updates, changedFiles } = result
        if (changedFiles.length === 0) {
          return
        }
        if (updates.every((update) => update.update.type === 'Noop')) {
          debug?.(`ignored file change for ${changedFiles.join(', ')}`)
          return
        }
        for (const { clientId, update } of updates) {
          const client = this.clients.get(clientId)
          if (client) {
            this.handleHmrOutput(client, changedFiles, update)
          }
        }
        // an edit requested a reload but its rebuild failed, leaving the page
        // waiting behind the error overlay. The fix produced this successful
        // build — regenerate output now, or the page would never reload.
        if (this.reloadNeededClientIds.size) {
          this.ensureOutputAndFlushReloadNeeded()
        }
      },
      onOutput: (result) => {
        if (result instanceof Error) {
          this.environment.logger.error(
            colors.red(`✘ Build error: ${result.message}`),
            {
              error: result,
            },
          )
          this.lastBuildError = result
          this.fullReloadPending = false
          this.environment.hot.send({
            type: 'error',
            err: prepareError(result),
          })
          return
        }
        this.lastBuildError = null

        this.storeOutputFiles(result.output)

        // Trigger a full reload if there's no error in the result and a reload is pending from HMR.
        if (this.fullReloadPending) {
          this.fullReloadPending = false
          this.debouncedFullReload()
        }
        if (this.reloadNeededClientIds.size) {
          this.debouncedReloadNeededFlush()
        }
      },
      onAdditionalAssets: (result) => {
        this.storeOutputFiles(result.output)
      },
      watch: {
        skipWrite: true,
      },
    })
    debug?.('INITIAL: setup dev engine')
    this.devEngine.run().then(
      () => {
        debug?.('INITIAL: run done')
      },
      (e) => {
        debug?.('INITIAL: run error', e)
      },
    )
    this.viteRuntime = await getHmrImplementation(
      this.environment.getTopLevelConfig(),
    )
    this.storeOutputFiles([])
    this.waitForInitialBuildFinish().then(() => {
      if (this._closed) return
      debug?.('INITIAL: build done')
      this.initialBuildCompleted = true
      if (!this.lastBuildError) {
        this.environment.hot.send({
          type: 'full-reload',
          path: '*',
          ifFallback: true,
        })
      }
    })
  }

  private async waitForInitialBuildFinish(): Promise<void> {
    if (this._closed) return
    await this.devEngine.ensureCurrentBuildFinish()
    if (this._closed) return

    let state = await this.devEngine.getBundleState()
    while (!this.hasBuildOutput && !state.lastBuildErrored) {
      await setTimeout(10)
      if (this._closed) return
      await this.devEngine.ensureCurrentBuildFinish()
      if (this._closed) return
      state = await this.devEngine.getBundleState()
    }
  }

  private ensureOutputAndFlushReloadNeeded(): void {
    this.devEngine.ensureLatestBuildOutput().then(
      () => this.debouncedReloadNeededFlush(),
      () => {},
    )
  }

  async triggerBundleRegenerationIfStale(): Promise<boolean> {
    const bundleState = await this.devEngine.getBundleState()

    // Trigger full build if the HMR errors,
    // this is to make it easier to recover if the HMR generation is broken for some reason.
    if (
      this.initialBuildCompleted &&
      bundleState.lastBuildErrored &&
      bundleState.lastErrorStage === 'Hmr'
    ) {
      debug?.(`TRIGGER: access after HMR-stage failure, forcing full rebuild`)

      this.devEngine.triggerFullBuild()
      this.devEngine.ensureLatestBuildOutput().then(() => {
        this.debouncedFullReload()
      })
      return true
    }

    const shouldTrigger =
      bundleState.hasStaleOutput &&
      !bundleState.lastBuildErrored &&
      this.initialBuildCompleted
    if (shouldTrigger) {
      this.devEngine.ensureLatestBuildOutput().then(() => {
        this.debouncedFullReload()
      })
      debug?.(`TRIGGER: access to stale bundle, triggered bundle re-generation`)
    }
    return shouldTrigger
  }

  async triggerLazyBundling(
    moduleId: string | null,
    clientId: string | null,
  ): Promise<{ code: string; filename: string } | undefined> {
    if (!moduleId || !clientId) {
      return
    }
    debug?.(
      `TRIGGER-LAZY: trigger lazy bundling for module ${moduleId} for client ${clientId}`,
    )
    const result = await this.devEngine.compileEntry(moduleId, clientId)
    this.pendingPayloadFilenames.add(result.filename)
    return result
  }

  /**
   * Called by the serving middlewares when the response for a payload completed.
   * Only delivered payloads are recorded on the server's per-client ship map, so
   * later chunks may omit a module only if the payload carrying it was delivered.
   *
   * Note: the payload filename is unique across all clients.
   */
  markPayloadDelivered(filename: string): void {
    if (this.pendingPayloadFilenames.delete(filename)) {
      this.devEngine.notifyPayloadDelivered(filename)
    }
  }

  async close(): Promise<void> {
    this._closed = true
    this.memoryFiles.clear()
    await this._devEngine?.close()
    this.initialBuildCompleted = false
  }

  private storeOutputFiles(output: RolldownOutput['output'][number][]): void {
    // NOTE: don't clear memoryFiles here as incremental build reuses the files
    if (this.viteRuntime) {
      this.memoryFiles.set(BUNDLED_DEV_CLIENT_FILENAME, {
        source: this.viteRuntime,
        etag: getEtag(Buffer.from(this.viteRuntime), { weak: true }),
      })
    }
    for (const outputFile of output) {
      this.memoryFiles.set(outputFile.fileName, () => {
        const source =
          outputFile.type === 'chunk' ? outputFile.code : outputFile.source
        return {
          source,
          etag: getEtag(Buffer.from(source), { weak: true }),
        }
      })
    }
  }

  private async getRolldownOptions() {
    const chunkMetadataMap = new ChunkMetadataMap()
    const rolldownOptions = resolveRolldownOptions(
      this.environment,
      chunkMetadataMap,
    )
    rolldownOptions.experimental ??= {}
    rolldownOptions.experimental.devMode = {
      lazy: true,
      ...(typeof rolldownOptions.experimental.devMode === 'object'
        ? rolldownOptions.experimental.devMode
        : {}),
      implement: '',
      skipCommonRuntimeInjection: true,
    }

    // disable inlineConst optimization due to a bug in Rolldown
    // https://github.com/vitejs/vite/issues/21843
    rolldownOptions.optimization ??= {}
    rolldownOptions.optimization.inlineConst = false

    // set filenames to make output paths predictable so that `renderChunk` hook does not need to be used
    if (Array.isArray(rolldownOptions.output)) {
      for (const output of rolldownOptions.output) {
        output.entryFileNames = 'assets/[name].js'
        output.chunkFileNames = 'assets/[name]-[hash].js'
        output.assetFileNames = 'assets/[name]-[hash][extname]'
        output.minify = false
        output.sourcemap = true
      }
    } else {
      rolldownOptions.output ??= {}
      rolldownOptions.output.entryFileNames = 'assets/[name].js'
      rolldownOptions.output.chunkFileNames = 'assets/[name]-[hash].js'
      rolldownOptions.output.assetFileNames = 'assets/[name]-[hash][extname]'
      rolldownOptions.output.minify = false
      rolldownOptions.output.sourcemap = true
    }

    return rolldownOptions
  }

  private handleHmrOutput(
    client: NormalizedHotChannelClient,
    files: string[],
    hmrOutput: HmrOutput,
  ) {
    if (hmrOutput.type === 'Noop') return

    const shortFile = files
      .map((file) => getShortName(file, this.environment.config.root))
      .join(', ')
    if (hmrOutput.type === 'FullReload') {
      const reason = hmrOutput.reason
        ? colors.dim(` (${hmrOutput.reason})`)
        : ''
      this.environment.logger.info(
        colors.green(`trigger page reload `) + colors.dim(shortFile) + reason,
        { clear: true, timestamp: true },
      )
      // `import.meta.hot.invalidate()` is fully client-side now, so every server-sent
      // reload comes from a file change: defer it until the `onOutput` callback to
      // avoid error overlay flashes.
      this.fullReloadPending = true
      return
    }

    debug?.(`handle hmr output for ${shortFile}`, {
      ...hmrOutput,
      code: typeof hmrOutput.code === 'string' ? '[code]' : hmrOutput.code,
    })

    this.pendingPayloadFilenames.add(hmrOutput.filename)
    this.memoryFiles.set(hmrOutput.filename, {
      // ensure that the generated hmr patch contains ESM syntax
      // this is to avoid attacks like GHSA-4v9v-hfq4-rm2v
      // https://github.com/webpack/webpack-dev-server/security/advisories/GHSA-4v9v-hfq4-rm2v
      // https://green.sapphi.red/blog/local-server-security-best-practices#_2-using-xssi-and-modifying-the-prototype
      // https://green.sapphi.red/blog/local-server-security-best-practices#properly-check-the-request-origin
      // we can also use `Cross-Origin Resource Policy` header instead of this
      // but we cannot use `Sec-Fetch-*` headers as they are only sent to potentially-trustworthy origins
      source: hmrOutput.code + '\n; export {}',
    })
    if (hmrOutput.sourcemapFilename && hmrOutput.sourcemap) {
      this.memoryFiles.set(hmrOutput.sourcemapFilename, {
        source: hmrOutput.sourcemap,
      })
    }
    client.send({
      type: 'bundled-dev-update',
      changedIds: hmrOutput.changedIds,
      url: hmrOutput.filename,
      seq: hmrOutput.seq,
    })
    const { formatted, truncated } = formatAndTruncateFileList(
      hmrOutput.changedIds,
    )
    if (truncated) debugHmr?.(`hmr update ${hmrOutput.changedIds.join(', ')}`)
    this.environment.logger.info(
      colors.green(`hmr update `) + colors.dim(formatted),
      {
        clear: true,
        timestamp: true,
      },
    )
  }
}

class Clients {
  private clientToId = new Map<NormalizedHotChannelClient, string>()
  private idToClient = new Map<string, NormalizedHotChannelClient>()

  setupIfNeeded(client: NormalizedHotChannelClient, clientId: string) {
    const id = this.clientToId.get(client)
    if (id && id !== clientId) {
      throw new Error(
        'client ID conflict detected. Please restart the dev server.',
      )
    }
    this.clientToId.set(client, clientId)
    this.idToClient.set(clientId, client)
  }

  get(id: string): NormalizedHotChannelClient | undefined {
    return this.idToClient.get(id)
  }

  getId(client: NormalizedHotChannelClient): string | undefined {
    return this.clientToId.get(client)
  }

  getAll(): NormalizedHotChannelClient[] {
    return Array.from(this.idToClient.values())
  }

  delete(client: NormalizedHotChannelClient): string | undefined {
    const id = this.clientToId.get(client)
    if (id) {
      this.clientToId.delete(client)
      this.idToClient.delete(id)
      return id
    }
  }
}

function debounce(time: number, cb: () => void) {
  let timer: ReturnType<typeof globalThis.setTimeout> | null
  return () => {
    if (timer) {
      globalThis.clearTimeout(timer)
      timer = null
    }
    timer = globalThis.setTimeout(cb, time)
  }
}


//     __ _____ _____ _____
//  __|  |   __|     |   | |  JSON for Modern C++ (supporting code)
// |  |  |__   |  |  | | | |  version 3.12.0
// |_____|_____|_____|_|___|  https://github.com/nlohmann/json
//
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
// SPDX-License-Identifier: MIT

#include "doctest_compatibility.h"

#include <nlohmann/json.hpp>
using nlohmann::json;

#include <algorithm>

TEST_CASE("tests on very large JSONs")
{
    SECTION("issue #1419 - Segmentation fault (stack overflow) due to unbounded recursion")
    {
        const auto depth = 5000000;

        std::string s(static_cast<std::size_t>(2 * depth), '[');
        std::fill(s.begin() + depth, s.end(), ']');

        json _;
        CHECK_NOTHROW(_ = nlohmann::json::parse(s));
    }
}


#include <iostream>
#include <nlohmann/json.hpp>

// possible use case: use NLOHMANN_JSON_NAMESPACE instead of nlohmann
using json = NLOHMANN_JSON_NAMESPACE::json;

// macro needed to output the NLOHMANN_JSON_NAMESPACE as string literal
#define Q(x) #x
#define QUOTE(x) Q(x)

int main()
{
    std::cout << QUOTE(NLOHMANN_JSON_NAMESPACE) << std::endl;
}


const { version } = require('vue')
// require('slash5') // cannot require ESM
const slash3 = require('slash3')

document.querySelector('#required-vue-version').textContent = version
document.querySelector('#required-slash3-exists').textContent =
  !!slash3('foo/bar')


import { expect, test } from 'vitest'
import { browserLogs, isBundledDev, page } from '~utils'

test('should have no 404s', () => {
  browserLogs.forEach((msg) => {
    expect(msg).not.toMatch('404')
  })
})

test.skipIf(isBundledDev)('not contain `.mjs`', async () => {
  const appHtml = await page.content()
  expect(appHtml).toMatch('Hello Vite!')
})


# Quality assurance

Ensuring quality is paramount for this project, particularly because [numerous other projects](../home/customers.md)
depend on it. Each commit to the library undergoes rigorous checks against the following requirements, and any
violations will result in a failed build.

## C++ language compliance and compiler compatibility

!!! success "Requirement: Compiler support"

    Any compiler with complete C++11 support can compile the library without warnings.

Note: C++20 modules support may hit compiler-specific issues not covered by the general compiler matrix below. See [Modules](../features/modules.md#known-issues) for known issues and workarounds.

Note: Some modern features (like C++20 ranges or filesystem support) may be disabled on specific broken or incomplete toolchains even when standard feature-test macros indicate support. See [`JSON_HAS_RANGES`](../api/macros/json_has_ranges.md) and [`JSON_HAS_FILESYSTEM`](../api/macros/json_has_filesystem.md) for details on known exclusions.

- [x] The library is compiled with 50+ different C++ compilers with different operating systems and platforms,
  including the oldest versions known to compile the library.

    ??? abstract "Compilers used in continuous integration"

        | Compiler                                     | Architecture | Operating System                  | CI        |
        |----------------------------------------------|--------------|-----------------------------------|-----------|
        | AppleClang 15.0.0.15000040; Xcode 15.0.1     | arm64        | macOS 14.7.2 (Sonoma)             | GitHub    |
        | AppleClang 15.0.0.15000100; Xcode 15.1       | arm64        | macOS 14.7.2 (Sonoma)             | GitHub    |
        | AppleClang 15.0.0.15000100; Xcode 15.2       | arm64        | macOS 14.7.2 (Sonoma)             | GitHub    |
        | AppleClang 15.0.0.15000309; Xcode 15.3       | arm64        | macOS 14.7.2 (Sonoma)             | GitHub    |
        | AppleClang 15.0.0.15000309; Xcode 15.4       | arm64        | macOS 14.7.2 (Sonoma)             | GitHub    |
        | AppleClang 16.0.0.16000026; Xcode 16         | arm64        | macOS 15.2 (Sequoia)              | GitHub    |
        | AppleClang 16.0.0.16000026; Xcode 16.1       | arm64        | macOS 15.2 (Sequoia)              | GitHub    |
        | AppleClang 16.0.0.16000026; Xcode 16.2       | arm64        | macOS 15.2 (Sequoia)              | GitHub    |
        | AppleClang 17.0.0.17000013; Xcode 16.3       | arm64        | macOS 15.5 (Sequoia)              | GitHub    |
        | AppleClang 17.0.0.17000013; Xcode 16.4       | arm64        | macOS 15.5 (Sequoia)              | GitHub    |
        | AppleClang 17.0.0.17000319; Xcode 26.0.1     | arm64        | macOS 15.5 (Sequoia)              | GitHub    |
        | Clang 3.4.2                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 3.5.2                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 3.6.2                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 3.7.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 3.8.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 3.9.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 4.0.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 5.0.2                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 6.0.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 7.1.0                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 8.0.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 9.0.1                                  | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 10.0.1                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 11.0.1 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 11.1.0                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 12.0.1 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 12.0.1                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 13.0.1 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 13.0.1                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 14.0.6                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 14.0.6 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 15.0.7                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 15.0.7 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 16.0.6                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 16.0.6 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 17.0.6                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 18.1.8                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 18.1.8 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 19.1.5 with MSVC-like command-line     | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 19.1.7                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 19.1.7 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 20.1.1                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 20.1.8 with GNU-like command-line      | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | Clang 21.1.8                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | Clang 22.1.8                                 | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | CUDA 11.8.0 (nvcc)                           | x86_64       | Ubuntu 22.04 LTS                  | GitHub    |
        | CUDA 12.1.1 (nvcc)                           | x86_64       | Ubuntu 22.04 LTS                  | GitHub    |
        | CUDA 12.6.3 (nvcc)                           | x86_64       | Ubuntu 22.04 LTS                  | GitHub    |
        | Emscripten 4.0.6                             | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 4.8.5                                    | x86_64       | Ubuntu 20.04 LTS                  | GitHub    |
        | GNU 4.9.3                                    | x86_64       | Ubuntu 20.04 LTS                  | GitHub    |
        | GNU 5.5.0                                    | x86_64       | Ubuntu 20.04 LTS                  | GitHub    |
        | GNU 6.4.0                                    | x86_64       | Ubuntu 20.04 LTS                  | GitHub    |
        | GNU 7.5.0                                    | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 8.5.0                                    | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 9.3.0                                    | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 9.4.0                                    | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 9.5.0                                    | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 10.5.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 11.4.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 11.5.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 12.2.0 (MinGW-W64 i686-ucrt-posix-dwarf) | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | GNU 12.2.0 (MinGW-W64 x86_64-ucrt-posix-seh) | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | GNU 12.4.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 13.3.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 14.2.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 15.1.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 16.1.0                                   | x86_64       | Ubuntu 22.04.1 LTS                | GitHub    |
        | GNU 16.1.0                                   | arm64        | Linux 6.1.100                     | Cirrus CI |
        | icpc (ICC) 2021.10.0 20230609                | x86_64       | Ubuntu 22.04 LTS                  | GitHub    |
        | icpx (Intel oneAPI DPC++/C++) 2025.3.2       | x86_64       | Ubuntu 24.04 LTS                  | GitHub    |
        | nvc++ (NVIDIA HPC SDK) 25.5-0                | x86_64       | Ubuntu 22.04 LTS                  | GitHub    |
        | MSVC 19.0.24241.7                            | x86          | Windows 8.1                       | AppVeyor  |
        | MSVC 19.16.27035.0                           | x86          | Windows-10 (Build 14393)          | AppVeyor  |
        | MSVC 19.29.30157.0                           | x86          | Windows-10 (Build 17763)          | AppVeyor  |
        | MSVC 19.44.35207.0                           | arm64        | Windows 11 (Build 26200)          | GitHub    |
        | MSVC 19.44.35214.0                           | x86          | Windows Server 2022 (Build 20348) | GitHub    |
        | MSVC 19.44.35214.0                           | x86_64       | Windows Server 2022 (Build 20348) | GitHub    |
        | MSVC 19.51.36231.0                           | x86          | Windows Server 2025 (Build 26100) | GitHub    |
        | MSVC 19.51.36231.0                           | x86_64       | Windows Server 2025 (Build 26100) | GitHub    |

- [x] The library is compiled with all C++ language revisions (C++11, C++14, C++17, C++20, C++23, and C++26) to detect
  and fix language deprecations early.
- [x] The library is checked for compiler warnings:
  - On Clang, `-Weverything` is used with 8 exceptions.

    ??? abstract "Clang warnings"

        ```cmake
        --8<-- "../../../cmake/clang_flags.cmake"
        ```

  - On GCC, 300+ warnings are enabled with 8 exceptions.

    ??? abstract "GCC warnings"

        ```cmake
        --8<-- "../../../cmake/gcc_flags.cmake"
        ```

## C++ standard library compliance

!!! success "Requirement: No prerequisites"

    The library has no prerequisites other than the Standard Template Library (STL).

- [x] The library is compiled and tested with both [libc++](https://libcxx.llvm.org) and
  [libstdc++](https://gcc.gnu.org/onlinedocs/libstdc++/) to detect subtle differences or incompatibilities.
- [x] The code checked with [Include What You Use (IWYU)](https://include-what-you-use.org) that all required standard
  headers are included.
- [x] On Windows, the library is compiled with `<Windows.h>` being included to detect and avoid common bugs.
- [x] The library is compiled with exceptions disabled to support alternative means of error handling.

## Stable public API

!!! success "Requirement: Stable public API"

    Any change to the library does not break the public API.

- [x] All public API functions are tested with a variety of arguments.
- [x] The library is compiled and tested with different template arguments for number, string, array, and object types.
- [x] Unit tests cover all lines of the code base.
- [x] Every exception of the library is thrown in the test suite, and the error messages and exception ids are checked.

!!! success "Requirement: Complete documentation"

    The public API is extensively documented.

- [x] Every public API function has a dedicated page in the
  [API reference documentation](https://json.nlohmann.me/api/basic_json/) with a self-contained code example.
- [x] All examples in the documentation are tested, and changes in their output are treated as an error.

## Robust input processing

!!! success "Requirement: Standards compliance"

    The library is compliant to JSON as defined in [RFC 8259](https://datatracker.ietf.org/doc/html/rfc8259).

- [x] The lexer is tested with all valid Unicode code points and all prefixes of all invalid Unicode code points.
- [x] The parser is tested against extensive correctness suites for JSON compliance.
- [x] In addition, the library is continuously fuzz-tested at [OSS-Fuzz](https://google.github.io/oss-fuzz/) where the
  library is checked against billions of inputs.

## Static analysis

!!! success "Requirement: State-of-the-art code analysis"

    The code is checked with state-of-the-art static code analysis tools.

- [x] The code is checked with the latest [Clang-Tidy](https://clang.llvm.org/extra/clang-tidy/).

    ??? abstract "Clang-Tidy configuration (.clang-tidy)"

        ```ini
        --8<-- "../../../.clang-tidy"
        ```

- [x] The code is checked with the latest [Cppcheck](https://cppcheck.sourceforge.io) with all warnings enabled.
- [x] The code is checked with the latest [Clang Static Analyzer](https://clang-analyzer.llvm.org) with 89 enabled
  rules.
- [x] The code is checked with [Infer](https://fbinfer.com).
- [x] The code is checked with [Codacy](https://app.codacy.com/gh/nlohmann/json/dashboard).

## Dynamic analysis

!!! success "Requirement: Correctness"

    The library is checked for memory correctness and absence of undefined behavior.

- [x] The test suite is executed with enabled [runtime assertions](https://json.nlohmann.me/features/assertions/) to
  check invariants and preconditions of functions to detect undefined behavior.
- [x] The test suite is executed with [Valgrind](https://valgrind.org) (Memcheck) to detect memory leaks.
- [x] The test suite is executed with [Sanitizers](https://github.com/google/sanitizers) (address sanitizer, undefined
  behavior sanitizer, integer overflow detection, nullability violations).

## Style check

!!! success "Requirement: Common code style"

    A common code style is used throughout all code files of the library.

- [x] The code is formatted with [Artistic Style](https://astyle.sourceforge.net) (astyle) against a style configuration
  that is also enforced in the CI.

    ??? abstract "Astyle configuration (tools/astyle/.astylerc)"

        ```ini
        --8<-- "../../../tools/astyle/.astylerc"
        ```

- [x] The code style is checked with [cpplint](https://github.com/cpplint/cpplint) with 61 enabled rules.

## Simple integration

!!! success "Requirement: Single header"

    The library can be used by adding a single header to a C++ project.

- [x] An amalgamation script is used to check if the source code is exposed as a self-contained single-header file.
- [x] The test suite is checked against the amalgamated source file as well as the individual source file.

!!! success "Requirement: CMake as primary development tool"

    All library functions are exposed and usable by CMake.

- [x] All library options are exposed as [CMake options](https://json.nlohmann.me/integration/cmake/) and tested.
- [x] The library is tested against relevant CMake versions:
  - CMake 3.5 (the earliest supported)
  - CMake 3.31.6 (the latest 3.x release)
  - CMake 4.0.0 (a very recent release)


import './static.js'

const link = document.head.appendChild(document.createElement('link'))
link.rel = 'preload'
link.as = 'style'
link.href = new URL('./dynamic.css', import.meta.url).href

import('./dynamic.js').then(async ({ lazyLoad }) => {
  await lazyLoad()
})


# <small>nlohmann::basic_json::</small>empty

```cpp
bool empty() const noexcept;
```

Checks if a JSON value has no elements (i.e., whether its [`size()`](size.md) is `0`).

## Return value

The return value depends on the different types and is defined as follows:

| Value type | return value                           |
|------------|----------------------------------------|
| null       | `#!cpp true`                           |
| boolean    | `#!cpp false`                          |
| string     | `#!cpp false`                          |
| number     | `#!cpp false`                          |
| binary     | `#!cpp false`                          |
| object     | result of function `object_t::empty()` |
| array      | result of function `array_t::empty()`  |

## Exception safety

No-throw guarantee: this function never throws exceptions.

## Complexity

Constant, as long as [`array_t`](array_t.md) and [`object_t`](object_t.md) satisfy the
[Container](https://en.cppreference.com/w/cpp/named_req/Container) concept; that is, their `empty()` functions have
constant complexity.

## Possible implementation

```cpp
bool empty() const noexcept
{
    return size() == 0;
}
```

## Notes

This function does not return whether a string stored as JSON value is empty -- it returns whether the JSON container
itself is empty which is `#!cpp false` in the case of a string.

## Examples

??? example

    The following code uses `empty()` to check if a JSON object contains any elements.

    ```cpp
    --8<-- "examples/empty.cpp"
    ```

    Output:

    ```json
    --8<-- "examples/empty.output"
    ```

## Version history

- Added in version 1.0.0.
- Extended to return `#!cpp false` for binary types in version 3.8.0.


<!doctype html>

    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite App</title>


import type {
  ObjectHook,
  MinimalPluginContext as RolldownMinimalPluginContext,
  Plugin as RolldownPlugin,
} from 'rolldown'

export type NonNeverKeys<T> = {
  [K in keyof T]: T[K] extends never ? never : K
}[keyof T]

export type GetHookContextMap<Plugin> = {
  [K in keyof Plugin]-?: Plugin[K] extends ObjectHook<infer T, unknown>
    ? T extends (this: infer This, ...args: any[]) => any
      ? This extends RolldownMinimalPluginContext
        ? This
        : never
      : never
    : never
}

type RollupPluginHooksContext = GetHookContextMap<RolldownPlugin>
export type RollupPluginHooks = NonNeverKeys<RollupPluginHooksContext>

export type RequiredExceptFor<T, K extends keyof T> = Pick<T, K> &
  Required<Omit<T, K>>