capture method
Creates a snapshot by collecting state from all registered contributors.
This method:
- Gets all registered keys in sorted order (deterministic)
- Calls each exporter function to collect state
- Creates a Snapshot with timestamp, appVersion, schemaVersion, and states
Parameters:
appVersion: Version string of the application (e.g., "1.2.3")metadata: Optional custom metadata to attach to the snapshotkeys: Optional list of keys to capture. If provided, only the specified state contributors will be included. Ifnull, all registered contributors are captured.
Returns: A Snapshot containing all captured state from registered contributors.
Throws:
- StateError if no state contributors are registered
- ArgumentError if
keyscontains a key that is not registered - Any exception thrown by an exporter function (propagated)
Deterministic Order: State is collected in alphabetical order of keys, ensuring deterministic snapshot creation regardless of registration order.
Implementation
Snapshot capture(
String appVersion, {
Map<String, dynamic>? metadata,
List<String>? keys,
}) {
final registry = SnapshotRegistry.instance;
final allKeys = registry.getAllKeys();
if (allKeys.isEmpty) {
throw StateError(
'No state contributors registered. Register at least one state '
'contributor before capturing a snapshot.',
);
}
// Determine which keys to capture
final keysToCapture = keys != null
? (keys.toList()..sort())
: allKeys;
// Validate that all requested keys are registered
for (final key in keysToCapture) {
if (!registry.isRegistered(key)) {
throw ArgumentError.value(
key,
'keys',
'Key "$key" is not registered. Cannot capture unregistered state.',
);
}
}
if (keysToCapture.isEmpty) {
throw StateError(
'No keys to capture. Provide at least one registered key.',
);
}
final states = <String, dynamic>{};
// Collect state from specified contributors in deterministic order
for (final key in keysToCapture) {
final exporter = registry.getExporter(key);
if (exporter == null) {
// This shouldn't happen if registry is consistent, but handle it
continue;
}
try {
final state = exporter();
// Validate that the exported state is JSON-serializable
try {
StateValidator.validate(state, key);
} catch (validationError) {
throw StateError(
'State validation failed for key "$key": $validationError\n'
'Exported state must be JSON-serializable. '
'See StateValidator documentation for restrictions.',
);
}
states[key] = state;
} catch (e) {
// If it's already a StateError, rethrow as-is
if (e is StateError) {
rethrow;
}
// Propagate errors from exporter functions
// This ensures developers know if their exporter has issues
throw StateError('Failed to capture state for key "$key": $e');
}
}
// Validate metadata if provided
if (metadata != null) {
try {
StateValidator.validate(metadata, 'metadata');
} catch (validationError) {
throw StateError(
'Metadata validation failed: $validationError\n'
'Metadata must be JSON-serializable.',
);
}
}
return Snapshot(
timestamp: DateTime.now().toUtc(),
appVersion: appVersion,
schemaVersion: schemaVersion,
states: states,
metadata: metadata,
);
}