"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.stableJSONStringify = exports.testStringOut = exports.testStringSrc = void 0;
const fast_js_1 = require("./fast.js");
const portable_js_1 = require("./portable.js");
// For some signing operations, we need to be able to sign JSON objects in a deterministic and stable way.
// `JSON.stringify` is not guaranteed to produce a stable output, so we provide our own implementation instead.
// In general, there are several reasons why JSON serialization might not be stable:
// * JSON allows insignificant whitespace to be inserted in various places.  `JSON.stringify` does not do that though.
// * The keys in an object can be serialized in any order. `JSON.stringify` is not stable in that regard because it (mostly)
//   respects the order in which the keys were added to the object (with the exception of keys that could be interpreted as
//   indices, which are serialized in numeric order and before all keys).
// * The characters in a string can be escaped in various ways. For example, the character `\n` can be represented as `\n` or
//   as `\u000a` and the character `A` can be represented as `A` or as `\u0041`. The EcmaScript spec does not specify which
//   representation `JSON.stringify` should use. It seems that `JSON.stringify` in modern browsers uses the shortest
//   representation, but that is not guaranteed by the spec.
// * Objects can provide a `toJSON` method that behaves in an arbitrary way.
// * Numbers can be represented in various ways. For example, the number `0` can be represented as `0`, `-0`, `+0`, `0.0`, `-0.0`,
// Our implementation of `stableJSONStringify` addresses most of these issues:
// * It does not insert insignificant whitespace.
// * It sorts the keys in an object in a canonical order defined below.
// * It uses the shortest representation for each character in a string.
// * It respects `toJSON` methods, but it cannot protect against `toJSON` implementations that behave in an arbitrary way. We assume
//   that we will serialize only standard EcmaScript objects such as `Date` which have well-defined `toJSON` methods.
// * It does not attempt to canonicalize numbers, we rely on the fact the `toJSON` method of numbers returns a string that uses the
//   shortest representation.
// Canonical order for object keys:
// * Keys that could be interpreted as indices are sorted by numeric value and before all other keys. See `isIndexKey` for the definition.
// * Other keys are sorted in lexicographic case-insensitive order, based on Unicode code points. See `compareObjectKeys` for the details.
// The rationale for using this order instead of a lexicographical order on all keys is that it allows us to leverage
// `JSON.stringify` to obtain a more performant implementation.
// Shortest representation of characters in strings:
// * We only escape the characters that need to be escaped according to RFC 8259. If a character has a two character escape sequence,
//   we use that escape sequence. All other characters are represented as a numeric \uXXXX escape sequence. In those sequences, we
//   always use lowercase hexadecimal digits.
// * All other characters are represented as unescaped literal characters (i.e. they are represented as themselves).
// We provide two implementations of `stableJSONStringify`:
// * `stableJSONStringify_portable` is a portable but slower implementation.
// * `stableJSONStringify_fast` is a faster implementation that partially relies on the native `JSON.stringify`. This implementation
//   is not guaranteed to produce the same output as `stableJSONStringify_portable` under all JavaScript engines. Then reason for that is
//   that `JSON.stringify` is not guaranteed to produce the shortest representation for strings,
//   but it should do that under most modern JavaScript engines. So we only use this implementation after testing that it
//   handles strings correctly. This should be the case under all modern JavaScript engines.
// Some test strings that we use to test that the fast implementation produces the correct output
// They are broken down into categories of characters
const testStrings = [
    {
        // All C0 control characters: they should represented as \uXXXX numeric escapes, except for \b, \f, \n, \r, \t, which should be represented as \b, \f, \n, \r, \t
        src: `\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f`,
        out: `\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f`
    },
    {
        // All C1 control characters: they should be represented literally
        src: `\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f`,
        out: `\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f`
    },
    {
        // Other characters: they should be represented literally. We test:
        // * Some ASCII characters
        // * Some non-ASCII characters in the Basic Multilingual Plane
        // * Some characters outside the Basic Multilingual Plane
        src: `aπ€ℤ🍔`,
        out: `aπ€ℤ🍔`
    }
];
// export so that it can be used in tests
exports.testStringSrc = testStrings.map((x) => x.src).join('');
exports.testStringOut = '"' + testStrings.map((x) => x.out).join('') + '"';
// TODO: send a log event if the portable but slower implementation is used
const canUseFast = (0, fast_js_1.stableJSONStringify_fast)(exports.testStringSrc) === exports.testStringOut;
exports.stableJSONStringify = canUseFast ? fast_js_1.stableJSONStringify_fast : portable_js_1.stableJSONStringify_portable;
//# sourceMappingURL=index.js.map