encodeFunctionJson function

Uint8List encodeFunctionJson(
  1. Object? value, {
  2. int maxBytes = 2097152,
  3. int maxDepth = 12,
})

Encodes finite plain JSON only; never invokes a user object's toJson hook.

The bytes are exactly those the TypeScript encodeFunctionJson (JSON.stringify) writes for the same value, so the emulator and the gateway re-encode a result identically (functions/02 ยง8): numbers follow functionJsonNumber, and an object's array-index keys (canonical integers up to 2^32 - 2) come first in ascending order, then its other keys in insertion order, as ECMAScript orders own properties. Strings keep jsonEncode's escaping, which JSON.stringify shares.

Implementation

Uint8List encodeFunctionJson(
  Object? value, {
  int maxBytes = 2097152,
  int maxDepth = 12,
}) {
  if (maxDepth < 1 || maxDepth > 64) {
    throw ArgumentError.value(maxDepth, 'maxDepth');
  }
  var nodes = 0;
  final ancestors = <Object>{};
  void visit(Object? value, int depth) {
    if (++nodes > 100000 || depth > maxDepth) {
      throw const FormatException('JSON nesting or node limit exceeded.');
    }
    if (value == null || value is bool) return;
    if (value is String && _wellFormed(value)) return;
    if (value is num &&
        value.isFinite &&
        _decimalIdentity(value.toString()) ==
            _decimalIdentity(value.toDouble().toString())) {
      return;
    }
    if (value is Map || value is List) {
      if (!ancestors.add(value)) {
        throw const FormatException('JSON must not contain cycles.');
      }
      if (value is List) {
        for (final item in value) {
          visit(item, depth + 1);
        }
      } else {
        for (final entry in (value as Map).entries) {
          if (entry.key is! String || !_wellFormed(entry.key as String)) {
            throw const FormatException('Invalid JSON key.');
          }
          visit(entry.value, depth + 1);
        }
      }
      ancestors.remove(value);
      return;
    }
    throw const FormatException('Value is not finite scalar JSON.');
  }

  visit(value, 0);
  final text = StringBuffer();
  _writeFunctionJson(text, value);
  final bytes = Uint8List.fromList(utf8.encode(text.toString()));
  parseFunctionJson(bytes, maxBytes: maxBytes, maxDepth: maxDepth);
  return bytes;
}