Index: imaps-frontend/node_modules/@discoveryjs/json-ext/src/index.js
===================================================================
--- imaps-frontend/node_modules/@discoveryjs/json-ext/src/index.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/@discoveryjs/json-ext/src/index.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,4 @@
+export { parseChunked } from './parse-chunked.js';
+export { stringifyChunked } from './stringify-chunked.js';
+export { stringifyInfo } from './stringify-info.js';
+export { createStringifyWebStream, parseFromWebStream } from './web-streams.js';
Index: imaps-frontend/node_modules/@discoveryjs/json-ext/src/parse-chunked.js
===================================================================
--- imaps-frontend/node_modules/@discoveryjs/json-ext/src/parse-chunked.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/@discoveryjs/json-ext/src/parse-chunked.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,352 @@
+import { isIterable } from './utils.js';
+
+const STACK_OBJECT = 1;
+const STACK_ARRAY = 2;
+const decoder = new TextDecoder();
+
+function adjustPosition(error, parser) {
+    if (error.name === 'SyntaxError' && parser.jsonParseOffset) {
+        error.message = error.message.replace(/at position (\d+)/, (_, pos) =>
+            'at position ' + (Number(pos) + parser.jsonParseOffset)
+        );
+    }
+
+    return error;
+}
+
+function append(array, elements) {
+    // Note: Avoid to use array.push(...elements) since it may lead to
+    // "RangeError: Maximum call stack size exceeded" for a long arrays
+    const initialLength = array.length;
+    array.length += elements.length;
+
+    for (let i = 0; i < elements.length; i++) {
+        array[initialLength + i] = elements[i];
+    }
+}
+
+export async function parseChunked(chunkEmitter) {
+    const iterable = typeof chunkEmitter === 'function'
+        ? chunkEmitter()
+        : chunkEmitter;
+
+    if (isIterable(iterable)) {
+        let parser = new ChunkParser();
+
+        try {
+            for await (const chunk of iterable) {
+                if (typeof chunk !== 'string' && !ArrayBuffer.isView(chunk)) {
+                    throw new TypeError('Invalid chunk: Expected string, TypedArray or Buffer');
+                }
+
+                parser.push(chunk);
+            }
+
+            return parser.finish();
+        } catch (e) {
+            throw adjustPosition(e, parser);
+        }
+    }
+
+    throw new TypeError(
+        'Invalid chunk emitter: Expected an Iterable, AsyncIterable, generator, ' +
+        'async generator, or a function returning an Iterable or AsyncIterable'
+    );
+};
+
+class ChunkParser {
+    constructor() {
+        this.value = undefined;
+        this.valueStack = null;
+
+        this.stack = new Array(100);
+        this.lastFlushDepth = 0;
+        this.flushDepth = 0;
+        this.stateString = false;
+        this.stateStringEscape = false;
+        this.pendingByteSeq = null;
+        this.pendingChunk = null;
+        this.chunkOffset = 0;
+        this.jsonParseOffset = 0;
+    }
+
+    parseAndAppend(fragment, wrap) {
+        // Append new entries or elements
+        if (this.stack[this.lastFlushDepth - 1] === STACK_OBJECT) {
+            if (wrap) {
+                this.jsonParseOffset--;
+                fragment = '{' + fragment + '}';
+            }
+
+            Object.assign(this.valueStack.value, JSON.parse(fragment));
+        } else {
+            if (wrap) {
+                this.jsonParseOffset--;
+                fragment = '[' + fragment + ']';
+            }
+
+            append(this.valueStack.value, JSON.parse(fragment));
+        }
+    }
+
+    prepareAddition(fragment) {
+        const { value } = this.valueStack;
+        const expectComma = Array.isArray(value)
+            ? value.length !== 0
+            : Object.keys(value).length !== 0;
+
+        if (expectComma) {
+            // Skip a comma at the beginning of fragment, otherwise it would
+            // fail to parse
+            if (fragment[0] === ',') {
+                this.jsonParseOffset++;
+                return fragment.slice(1);
+            }
+
+            // When value (an object or array) is not empty and a fragment
+            // doesn't start with a comma, a single valid fragment starting
+            // is a closing bracket. If it's not, a prefix is adding to fail
+            // parsing. Otherwise, the sequence of chunks can be successfully
+            // parsed, although it should not, e.g. ["[{}", "{}]"]
+            if (fragment[0] !== '}' && fragment[0] !== ']') {
+                this.jsonParseOffset -= 3;
+                return '[[]' + fragment;
+            }
+        }
+
+        return fragment;
+    }
+
+    flush(chunk, start, end) {
+        let fragment = chunk.slice(start, end);
+
+        // Save position correction an error in JSON.parse() if any
+        this.jsonParseOffset = this.chunkOffset + start;
+
+        // Prepend pending chunk if any
+        if (this.pendingChunk !== null) {
+            fragment = this.pendingChunk + fragment;
+            this.jsonParseOffset -= this.pendingChunk.length;
+            this.pendingChunk = null;
+        }
+
+        if (this.flushDepth === this.lastFlushDepth) {
+            // Depth didn't changed, so it's a root value or entry/element set
+            if (this.flushDepth > 0) {
+                this.parseAndAppend(this.prepareAddition(fragment), true);
+            } else {
+                // That's an entire value on a top level
+                this.value = JSON.parse(fragment);
+                this.valueStack = {
+                    value: this.value,
+                    prev: null
+                };
+            }
+        } else if (this.flushDepth > this.lastFlushDepth) {
+            // Add missed closing brackets/parentheses
+            for (let i = this.flushDepth - 1; i >= this.lastFlushDepth; i--) {
+                fragment += this.stack[i] === STACK_OBJECT ? '}' : ']';
+            }
+
+            if (this.lastFlushDepth === 0) {
+                // That's a root value
+                this.value = JSON.parse(fragment);
+                this.valueStack = {
+                    value: this.value,
+                    prev: null
+                };
+            } else {
+                this.parseAndAppend(this.prepareAddition(fragment), true);
+            }
+
+            // Move down to the depths to the last object/array, which is current now
+            for (let i = this.lastFlushDepth || 1; i < this.flushDepth; i++) {
+                let value = this.valueStack.value;
+
+                if (this.stack[i - 1] === STACK_OBJECT) {
+                    // find last entry
+                    let key;
+                    // eslint-disable-next-line curly
+                    for (key in value);
+                    value = value[key];
+                } else {
+                    // last element
+                    value = value[value.length - 1];
+                }
+
+                this.valueStack = {
+                    value,
+                    prev: this.valueStack
+                };
+            }
+        } else /* this.flushDepth < this.lastFlushDepth */ {
+            fragment = this.prepareAddition(fragment);
+
+            // Add missed opening brackets/parentheses
+            for (let i = this.lastFlushDepth - 1; i >= this.flushDepth; i--) {
+                this.jsonParseOffset--;
+                fragment = (this.stack[i] === STACK_OBJECT ? '{' : '[') + fragment;
+            }
+
+            this.parseAndAppend(fragment, false);
+
+            for (let i = this.lastFlushDepth - 1; i >= this.flushDepth; i--) {
+                this.valueStack = this.valueStack.prev;
+            }
+        }
+
+        this.lastFlushDepth = this.flushDepth;
+    }
+
+    push(chunk) {
+        if (typeof chunk !== 'string') {
+            // Suppose chunk is Buffer or Uint8Array
+
+            // Prepend uncompleted byte sequence if any
+            if (this.pendingByteSeq !== null) {
+                const origRawChunk = chunk;
+                chunk = new Uint8Array(this.pendingByteSeq.length + origRawChunk.length);
+                chunk.set(this.pendingByteSeq);
+                chunk.set(origRawChunk, this.pendingByteSeq.length);
+                this.pendingByteSeq = null;
+            }
+
+            // In case Buffer/Uint8Array, an input is encoded in UTF8
+            // Seek for parts of uncompleted UTF8 symbol on the ending
+            // This makes sense only if we expect more chunks and last char is not multi-bytes
+            if (chunk[chunk.length - 1] > 127) {
+                for (let seqLength = 0; seqLength < chunk.length; seqLength++) {
+                    const byte = chunk[chunk.length - 1 - seqLength];
+
+                    // 10xxxxxx - 2nd, 3rd or 4th byte
+                    // 110xxxxx – first byte of 2-byte sequence
+                    // 1110xxxx - first byte of 3-byte sequence
+                    // 11110xxx - first byte of 4-byte sequence
+                    if (byte >> 6 === 3) {
+                        seqLength++;
+
+                        // If the sequence is really incomplete, then preserve it
+                        // for the future chunk and cut off it from the current chunk
+                        if ((seqLength !== 4 && byte >> 3 === 0b11110) ||
+                            (seqLength !== 3 && byte >> 4 === 0b1110) ||
+                            (seqLength !== 2 && byte >> 5 === 0b110)) {
+                            this.pendingByteSeq = chunk.slice(chunk.length - seqLength);
+                            chunk = chunk.slice(0, -seqLength);
+                        }
+
+                        break;
+                    }
+                }
+            }
+
+            // Convert chunk to a string, since single decode per chunk
+            // is much effective than decode multiple small substrings
+            chunk = decoder.decode(chunk);
+        }
+
+        const chunkLength = chunk.length;
+        let lastFlushPoint = 0;
+        let flushPoint = 0;
+
+        // Main scan loop
+        scan: for (let i = 0; i < chunkLength; i++) {
+            if (this.stateString) {
+                for (; i < chunkLength; i++) {
+                    if (this.stateStringEscape) {
+                        this.stateStringEscape = false;
+                    } else {
+                        switch (chunk.charCodeAt(i)) {
+                            case 0x22: /* " */
+                                this.stateString = false;
+                                continue scan;
+
+                            case 0x5C: /* \ */
+                                this.stateStringEscape = true;
+                        }
+                    }
+                }
+
+                break;
+            }
+
+            switch (chunk.charCodeAt(i)) {
+                case 0x22: /* " */
+                    this.stateString = true;
+                    this.stateStringEscape = false;
+                    break;
+
+                case 0x2C: /* , */
+                    flushPoint = i;
+                    break;
+
+                case 0x7B: /* { */
+                    // Open an object
+                    flushPoint = i + 1;
+                    this.stack[this.flushDepth++] = STACK_OBJECT;
+                    break;
+
+                case 0x5B: /* [ */
+                    // Open an array
+                    flushPoint = i + 1;
+                    this.stack[this.flushDepth++] = STACK_ARRAY;
+                    break;
+
+                case 0x5D: /* ] */
+                case 0x7D: /* } */
+                    // Close an object or array
+                    flushPoint = i + 1;
+                    this.flushDepth--;
+
+                    if (this.flushDepth < this.lastFlushDepth) {
+                        this.flush(chunk, lastFlushPoint, flushPoint);
+                        lastFlushPoint = flushPoint;
+                    }
+
+                    break;
+
+                case 0x09: /* \t */
+                case 0x0A: /* \n */
+                case 0x0D: /* \r */
+                case 0x20: /* space */
+                    // Move points forward when they points on current position and it's a whitespace
+                    if (lastFlushPoint === i) {
+                        lastFlushPoint++;
+                    }
+
+                    if (flushPoint === i) {
+                        flushPoint++;
+                    }
+
+                    break;
+            }
+        }
+
+        if (flushPoint > lastFlushPoint) {
+            this.flush(chunk, lastFlushPoint, flushPoint);
+        }
+
+        // Produce pendingChunk if something left
+        if (flushPoint < chunkLength) {
+            if (this.pendingChunk !== null) {
+                // When there is already a pending chunk then no flush happened,
+                // appending entire chunk to pending one
+                this.pendingChunk += chunk;
+            } else {
+                // Create a pending chunk, it will start with non-whitespace since
+                // flushPoint was moved forward away from whitespaces on scan
+                this.pendingChunk = chunk.slice(flushPoint, chunkLength);
+            }
+        }
+
+        this.chunkOffset += chunkLength;
+    }
+
+    finish() {
+        if (this.pendingChunk !== null) {
+            this.flush('', 0, 0);
+            this.pendingChunk = null;
+        }
+
+        return this.value;
+    }
+};
Index: imaps-frontend/node_modules/@discoveryjs/json-ext/src/stringify-chunked.js
===================================================================
--- imaps-frontend/node_modules/@discoveryjs/json-ext/src/stringify-chunked.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/@discoveryjs/json-ext/src/stringify-chunked.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,171 @@
+import { normalizeStringifyOptions, replaceValue } from './utils.js';
+
+function encodeString(value) {
+    if (/[^\x20\x21\x23-\x5B\x5D-\uD799]/.test(value)) { // [^\x20-\uD799]|[\x22\x5c]
+        return JSON.stringify(value);
+    }
+
+    return '"' + value + '"';
+}
+
+export function* stringifyChunked(value, ...args) {
+    const { replacer, getKeys, space, ...options } = normalizeStringifyOptions(...args);
+    const highWaterMark = Number(options.highWaterMark) || 0x4000; // 16kb by default
+
+    const keyStrings = new Map();
+    const stack = [];
+    const rootValue = { '': value };
+    let prevState = null;
+    let state = () => printEntry('', value);
+    let stateValue = rootValue;
+    let stateEmpty = true;
+    let stateKeys = [''];
+    let stateIndex = 0;
+    let buffer = '';
+
+    while (true) {
+        state();
+
+        if (buffer.length >= highWaterMark || prevState === null) {
+            // flush buffer
+            yield buffer;
+            buffer = '';
+
+            if (prevState === null) {
+                break;
+            }
+        }
+    }
+
+    function printObject() {
+        if (stateIndex === 0) {
+            stateKeys = getKeys(stateValue);
+            buffer += '{';
+        }
+
+        // when no keys left
+        if (stateIndex === stateKeys.length) {
+            buffer += space && !stateEmpty
+                ? `\n${space.repeat(stack.length - 1)}}`
+                : '}';
+
+            popState();
+            return;
+        }
+
+        const key = stateKeys[stateIndex++];
+        printEntry(key, stateValue[key]);
+    }
+
+    function printArray() {
+        if (stateIndex === 0) {
+            buffer += '[';
+        }
+
+        if (stateIndex === stateValue.length) {
+            buffer += space && !stateEmpty
+                ? `\n${space.repeat(stack.length - 1)}]`
+                : ']';
+
+            popState();
+            return;
+        }
+
+        printEntry(stateIndex, stateValue[stateIndex++]);
+    }
+
+    function printEntryPrelude(key) {
+        if (stateEmpty) {
+            stateEmpty = false;
+        } else {
+            buffer += ',';
+        }
+
+        if (space && prevState !== null) {
+            buffer += `\n${space.repeat(stack.length)}`;
+        }
+
+        if (state === printObject) {
+            let keyString = keyStrings.get(key);
+
+            if (keyString === undefined) {
+                keyStrings.set(key, keyString = encodeString(key) + (space ? ': ' : ':'));
+            }
+
+            buffer += keyString;
+        }
+    }
+
+    function printEntry(key, value) {
+        value = replaceValue(stateValue, key, value, replacer);
+
+        if (value === null || typeof value !== 'object') {
+            // primitive
+            if (state !== printObject || value !== undefined) {
+                printEntryPrelude(key);
+                pushPrimitive(value);
+            }
+        } else {
+            // If the visited set does not change after adding a value, then it is already in the set
+            if (stack.includes(value)) {
+                throw new TypeError('Converting circular structure to JSON');
+            }
+
+            printEntryPrelude(key);
+            stack.push(value);
+
+            pushState();
+            state = Array.isArray(value) ? printArray : printObject;
+            stateValue = value;
+            stateEmpty = true;
+            stateIndex = 0;
+        }
+    }
+
+    function pushPrimitive(value) {
+        switch (typeof value) {
+            case 'string':
+                buffer += encodeString(value);
+                break;
+
+            case 'number':
+                buffer += Number.isFinite(value) ? String(value) : 'null';
+                break;
+
+            case 'boolean':
+                buffer += value ? 'true' : 'false';
+                break;
+
+            case 'undefined':
+            case 'object': // typeof null === 'object'
+                buffer += 'null';
+                break;
+
+            default:
+                throw new TypeError(`Do not know how to serialize a ${value.constructor?.name || typeof value}`);
+        }
+    }
+
+    function pushState() {
+        prevState = {
+            keys: stateKeys,
+            index: stateIndex,
+            prev: prevState
+        };
+    }
+
+    function popState() {
+        stack.pop();
+        const value = stack.length > 0 ? stack[stack.length - 1] : rootValue;
+
+        // restore state
+        state = Array.isArray(value) ? printArray : printObject;
+        stateValue = value;
+        stateEmpty = false;
+        stateKeys = prevState.keys;
+        stateIndex = prevState.index;
+
+        // pop state
+        prevState = prevState.prev;
+    }
+};
Index: imaps-frontend/node_modules/@discoveryjs/json-ext/src/stringify-info.js
===================================================================
--- imaps-frontend/node_modules/@discoveryjs/json-ext/src/stringify-info.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/@discoveryjs/json-ext/src/stringify-info.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,247 @@
+import { normalizeStringifyOptions, replaceValue } from './utils.js';
+
+const hasOwn = typeof Object.hasOwn === 'function'
+    ? Object.hasOwn
+    : (object, key) => Object.hasOwnProperty.call(object, key);
+
+// https://tc39.es/ecma262/#table-json-single-character-escapes
+const escapableCharCodeSubstitution = { // JSON Single Character Escape Sequences
+    0x08: '\\b',
+    0x09: '\\t',
+    0x0a: '\\n',
+    0x0c: '\\f',
+    0x0d: '\\r',
+    0x22: '\\\"',
+    0x5c: '\\\\'
+};
+
+const charLength2048 = Uint8Array.from({ length: 2048 }, (_, code) => {
+    if (hasOwn(escapableCharCodeSubstitution, code)) {
+        return 2; // \X
+    }
+
+    if (code < 0x20) {
+        return 6; // \uXXXX
+    }
+
+    return code < 128 ? 1 : 2; // UTF8 bytes
+});
+
+function isLeadingSurrogate(code) {
+    return code >= 0xD800 && code <= 0xDBFF;
+}
+
+function isTrailingSurrogate(code) {
+    return code >= 0xDC00 && code <= 0xDFFF;
+}
+
+function stringLength(str) {
+    // Fast path to compute length when a string contains only characters encoded as single bytes
+    if (!/[^\x20\x21\x23-\x5B\x5D-\x7F]/.test(str)) {
+        return str.length + 2;
+    }
+
+    let len = 0;
+    let prevLeadingSurrogate = false;
+
+    for (let i = 0; i < str.length; i++) {
+        const code = str.charCodeAt(i);
+
+        if (code < 2048) {
+            len += charLength2048[code];
+        } else if (isLeadingSurrogate(code)) {
+            len += 6; // \uXXXX since no pair with trailing surrogate yet
+            prevLeadingSurrogate = true;
+            continue;
+        } else if (isTrailingSurrogate(code)) {
+            len = prevLeadingSurrogate
+                ? len - 2  // surrogate pair (4 bytes), since we calculate prev leading surrogate as 6 bytes, substruct 2 bytes
+                : len + 6; // \uXXXX
+        } else {
+            len += 3; // code >= 2048 is 3 bytes length for UTF8
+        }
+
+        prevLeadingSurrogate = false;
+    }
+
+    return len + 2; // +2 for quotes
+}
+
+// avoid producing a string from a number
+function intLength(num) {
+    let len = 0;
+
+    if (num < 0) {
+        len = 1;
+        num = -num;
+    }
+
+    if (num >= 1e9) {
+        len += 9;
+        num = (num - num % 1e9) / 1e9;
+    }
+
+    if (num >= 1e4) {
+        if (num >= 1e6) {
+            return len + (num >= 1e8
+                ? 9
+                : num >= 1e7 ? 8 : 7
+            );
+        }
+        return len + (num >= 1e5 ? 6 : 5);
+    }
+
+    return len + (num >= 1e2
+        ? num >= 1e3 ? 4 : 3
+        : num >= 10 ? 2 : 1
+    );
+};
+
+function primitiveLength(value) {
+    switch (typeof value) {
+        case 'string':
+            return stringLength(value);
+
+        case 'number':
+            return Number.isFinite(value)
+                ? Number.isInteger(value)
+                    ? intLength(value)
+                    : String(value).length
+                : 4 /* null */;
+
+        case 'boolean':
+            return value ? 4 /* true */ : 5 /* false */;
+
+        case 'undefined':
+        case 'object':
+            return 4; /* null */
+
+        default:
+            return 0;
+    }
+}
+
+export function stringifyInfo(value, ...args) {
+    const { replacer, getKeys, ...options } = normalizeStringifyOptions(...args);
+    const continueOnCircular = Boolean(options.continueOnCircular);
+    const space = options.space?.length || 0;
+
+    const keysLength = new Map();
+    const visited = new Map();
+    const circular = new Set();
+    const stack = [];
+    const root = { '': value };
+    let stop = false;
+    let bytes = 0;
+    let spaceBytes = 0;
+    let objects = 0;
+
+    walk(root, '', value);
+
+    // when value is undefined or replaced for undefined
+    if (bytes === 0) {
+        bytes += 9; // FIXME: that's the length of undefined, should we normalize behaviour to convert it to null?
+    }
+
+    return {
+        bytes: isNaN(bytes) ? Infinity : bytes + spaceBytes,
+        spaceBytes: space > 0 && isNaN(bytes) ? Infinity : spaceBytes,
+        circular: [...circular]
+    };
+
+    function walk(holder, key, value) {
+        if (stop) {
+            return;
+        }
+
+        value = replaceValue(holder, key, value, replacer);
+
+        if (value === null || typeof value !== 'object') {
+            // primitive
+            if (value !== undefined || Array.isArray(holder)) {
+                bytes += primitiveLength(value);
+            }
+        } else {
+            // check for circular references
+            if (stack.includes(value)) {
+                circular.add(value);
+                bytes += 4; // treat as null
+
+                if (!continueOnCircular) {
+                    stop = true;
+                }
+
+                return;
+            }
+
+            // Using 'visited' allows avoiding hang-ups in cases of highly interconnected object graphs;
+            // for example, a list of git commits with references to parents can lead to N^2 complexity for traversal,
+            // and N when 'visited' is used
+            if (visited.has(value)) {
+                bytes += visited.get(value);
+
+                return;
+            }
+
+            objects++;
+
+            const prevObjects = objects;
+            const valueBytes = bytes;
+            let valueLength = 0;
+
+            stack.push(value);
+
+            if (Array.isArray(value)) {
+                // array
+                valueLength = value.length;
+
+                for (let i = 0; i < valueLength; i++) {
+                    walk(value, i, value[i]);
+                }
+            } else {
+                // object
+                let prevLength = bytes;
+
+                for (const key of getKeys(value)) {
+                    walk(value, key, value[key]);
+
+                    if (prevLength !== bytes) {
+                        let keyLen = keysLength.get(key);
+
+                        if (keyLen === undefined) {
+                            keysLength.set(key, keyLen = stringLength(key) + 1); // "key":
+                        }
+
+                        // value is printed
+                        bytes += keyLen;
+                        valueLength++;
+                        prevLength = bytes;
+                    }
+                }
+            }
+
+            bytes += valueLength === 0
+                ? 2 // {} or []
+                : 1 + valueLength; // {} or [] + commas
+
+            if (space > 0 && valueLength > 0) {
+                spaceBytes +=
+                    // a space between ":" and a value for each object entry
+                    (Array.isArray(value) ? 0 : valueLength) +
+                    // the formula results from folding the following components:
+                    // - for each key-value or element: ident + newline
+                    //   (1 + stack.length * space) * valueLength
+                    // - ident (one space less) before "}" or "]" + newline
+                    //   (stack.length - 1) * space + 1
+                    (1 + stack.length * space) * (valueLength + 1) - space;
+            }
+
+            stack.pop();
+
+            // add to 'visited' only objects that contain nested objects
+            if (prevObjects !== objects) {
+                visited.set(value, bytes - valueBytes);
+            }
+        }
+    }
+};
Index: imaps-frontend/node_modules/@discoveryjs/json-ext/src/utils.js
===================================================================
--- imaps-frontend/node_modules/@discoveryjs/json-ext/src/utils.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/@discoveryjs/json-ext/src/utils.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,100 @@
+export function isIterable(value) {
+    return (
+        typeof value === 'object' &&
+        value !== null &&
+        (
+            typeof value[Symbol.iterator] === 'function' ||
+            typeof value[Symbol.asyncIterator] === 'function'
+        )
+    );
+}
+
+export function replaceValue(holder, key, value, replacer) {
+    if (value && typeof value.toJSON === 'function') {
+        value = value.toJSON();
+    }
+
+    if (replacer !== null) {
+        value = replacer.call(holder, String(key), value);
+    }
+
+    switch (typeof value) {
+        case 'function':
+        case 'symbol':
+            value = undefined;
+            break;
+
+        case 'object':
+            if (value !== null) {
+                const cls = value.constructor;
+                if (cls === String || cls === Number || cls === Boolean) {
+                    value = value.valueOf();
+                }
+            }
+            break;
+    }
+
+    return value;
+}
+
+export function normalizeReplacer(replacer) {
+    if (typeof replacer === 'function') {
+        return replacer;
+    }
+
+    if (Array.isArray(replacer)) {
+        const allowlist = new Set(replacer
+            .map(item => {
+                const cls = item && item.constructor;
+                return cls === String || cls === Number ? String(item) : null;
+            })
+            .filter(item => typeof item === 'string')
+        );
+
+        return [...allowlist];
+    }
+
+    return null;
+}
+
+export function normalizeSpace(space) {
+    if (typeof space === 'number') {
+        if (!Number.isFinite(space) || space < 1) {
+            return false;
+        }
+
+        return ' '.repeat(Math.min(space, 10));
+    }
+
+    if (typeof space === 'string') {
+        return space.slice(0, 10) || false;
+    }
+
+    return false;
+}
+
+export function normalizeStringifyOptions(optionsOrReplacer, space) {
+    if (optionsOrReplacer === null || Array.isArray(optionsOrReplacer) || typeof optionsOrReplacer !== 'object') {
+        optionsOrReplacer = {
+            replacer: optionsOrReplacer,
+            space
+        };
+    }
+
+    let replacer = normalizeReplacer(optionsOrReplacer.replacer);
+    let getKeys = Object.keys;
+
+    if (Array.isArray(replacer)) {
+        const allowlist = replacer;
+
+        getKeys = () => allowlist;
+        replacer = null;
+    }
+
+    return {
+        ...optionsOrReplacer,
+        replacer,
+        getKeys,
+        space: normalizeSpace(optionsOrReplacer.space)
+    };
+}
Index: imaps-frontend/node_modules/@discoveryjs/json-ext/src/web-streams.js
===================================================================
--- imaps-frontend/node_modules/@discoveryjs/json-ext/src/web-streams.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
+++ imaps-frontend/node_modules/@discoveryjs/json-ext/src/web-streams.js	(revision 79a03170cdde4e801d12257ee86723774970427b)
@@ -0,0 +1,54 @@
+/* eslint-env browser */
+import { parseChunked } from './parse-chunked.js';
+import { stringifyChunked } from './stringify-chunked.js';
+import { isIterable } from './utils.js';
+
+export function parseFromWebStream(stream) {
+    // 2024/6/17: currently, an @@asyncIterator on a ReadableStream is not widely supported,
+    // therefore use a fallback using a reader
+    // https://caniuse.com/mdn-api_readablestream_--asynciterator
+    return parseChunked(isIterable(stream) ? stream : async function*() {
+        const reader = stream.getReader();
+
+        try {
+            while (true) {
+                const { value, done } = await reader.read();
+
+                if (done) {
+                    break;
+                }
+
+                yield value;
+            }
+        } finally {
+            reader.releaseLock();
+        }
+    });
+}
+
+export function createStringifyWebStream(value, replacer, space) {
+    // 2024/6/17: the ReadableStream.from() static method is supported
+    // in Node.js 20.6+ and Firefox only
+    if (typeof ReadableStream.from === 'function') {
+        return ReadableStream.from(stringifyChunked(value, replacer, space));
+    }
+
+    // emulate ReadableStream.from()
+    return new ReadableStream({
+        start() {
+            this.generator = stringifyChunked(value, replacer, space);
+        },
+        pull(controller) {
+            const { value, done } = this.generator.next();
+
+            if (done) {
+                controller.close();
+            } else {
+                controller.enqueue(value);
+            }
+        },
+        cancel() {
+            this.generator = null;
+        }
+    });
+};
