outbox method

Future<Map<String, Object?>> outbox()

The Outbox panel's rows. With a storage session, its queued writes are the truth and the event mirror adds recent replays; without one, the mirror alone. wired says whether replay and discard are available.

Only ids, operation names, states and a reduced failure leave: never a write's arguments or idempotency key. While the cache is changing principal, once the inspector is disposed, and when either happens while the session is being read, the answer is empty and stale is true.

Implementation

Future<Map<String, Object?>> outbox() async {
  if (!_answerable()) return _staleOutbox();

  final epoch = _session;
  final wired = outboxInspector != null;
  final session = _dev.session;

  if (session == null) {
    return {
      'wired': wired,
      'source': 'events',
      'entries': [for (final row in _outbox.entries()) row.toJson()],
    };
  }

  final List<PendingMutationRecord> records;

  try {
    records = await session.readOutbox();
  } on Object {
    // A session closing under the read is the principal changing.
    if (_moved(epoch)) return _staleOutbox();
    rethrow;
  }

  if (_moved(epoch)) return _staleOutbox();

  final ids = {for (final record in records) record.id};

  // Writes the event mirror knows and the session no longer holds: a replay
  // that finished, or a failure whose record is gone. They carry the same
  // body-free failure text as the stored ones.
  final remembered = [
    for (final row in _outbox.entries())
      if (!ids.contains(row.id) &&
          (row.state == 'replayed' || row.state == 'failed'))
        row,
  ];
  final total = records.length + remembered.length;

  final rows = <Map<String, Object?>>[
    for (final record in records.take(_outboxLimit))
      if (outboxStateOf(record.stateJson ?? _queuedState) case (
        :final state,
        :final failure,
        :final since,
      ))
        {
          'id': record.id,
          'operation': shortMessage(record.operationId),
          'createdAt': record.createdAt.millisecondsSinceEpoch,
          'state': state,
          'failure': failure,
          'since': since,
          'at': null,
        },
    for (final row in remembered.take(
      _outboxLimit - records.length.clamp(0, _outboxLimit),
    ))
      row.toJson(),
  ];

  return {
    'wired': wired,
    'source': 'session',
    // A backlog is capped like every other list the devtools send, and each
    // row is bounded, so the response cannot grow with the queue.
    // The id is an identifier: the panel sends it back to replay or discard
    // the write, so it is kept whole. Everything else is bounded.
    'entries': [
      for (final row in rows)
        {
          'id': row['id'],
          ...bounded(<String, Object?>{...row}..remove('id'), _outboxWidth)!
              as Map<String, Object?>,
        },
    ],
    'total': total,
    'truncated': total > rows.length,
  };
}