RemoteContentPack<T> class

A typed, signed remote content slot — live-ops content (a level definition, a shop catalog page, an event schedule) pushed from a server without an app-store review cycle, built on the same 3 pieces RemoteConfigService, VersionedJsonStore and save_integrity.dart already provide separately (IDEA-37): asset-fallback-then-fetch, schema-version migration, and HMAC signing.

load always resolves from the bundled asset first — never network- gated — so current has something usable the instant load returns. If fetchRemote is given, a fetch is kicked off in the background and current is swapped in-place once it resolves; callers that care about that exact moment (mostly tests) can await refreshed.

A fetched envelope is rejected — silently, keeping whatever current already was — instead of applied when: the signature doesn't verify (or contentSecret wasn't supplied at all, in which case an envelope carrying a signature can never be trusted since there's no key configured to check it against), its schemaVersion is newer than this pack's own (a downgraded app being handed a shape it can't read), or fromJson throws on it (a right-shaped-but-wrong-typed field). Never crashes and never applies unverified content.

Constructors

RemoteContentPack({required String assetPath, required int schemaVersion, required T fromJson(Map<String, Object?> json), Map<String, Object?> migrate(int fromVersion, Map<String, Object?> json)?, String? contentSecret, Future<Map<String, Object?>> fetchRemote()?, AssetBundle? bundle})
Legacy/source-compatible constructor — cache disabled, byte-for-byte the same public signature and behavior as before ENH-94. Use RemoteContentPack.withCache to opt into persistence.
RemoteContentPack.withCache({required String assetPath, required int schemaVersion, required T fromJson(Map<String, Object?> json), required StorageService? storage, required String? cacheKey, Map<String, Object?> migrate(int fromVersion, Map<String, Object?> json)?, String? contentSecret, Future<Map<String, Object?>> fetchRemote()?, Duration? maxCacheAge, AssetBundle? bundle})
ENH-94: same signed/typed content pack with a durable verified cache. Kept as a NAMED constructor (instead of changing RemoteContentPack's existing signature by adding optional params) so every pre-existing consumer remains 100% API-gate compatible — additive only, no major version bump/BREAKING changelog required.
RemoteContentPack.withHistory({required String assetPath, required int schemaVersion, required T fromJson(Map<String, Object?> json), required StorageService storage, required String cacheKey, required String contentSecret, Map<String, Object?> migrate(int fromVersion, Map<String, Object?> json)?, Future<Map<String, Object?>> fetchRemote()?, Duration? maxCacheAge, int? historyCapacity = 5, int contentVersionResolver(Map<String, Object?> json)?, AssetBundle? bundle})
ENH-96: adds a bounded, persisted history of previously-applied VERIFIED revisions on top of withCache's single-slot cache, plus downgrade rejection and explicit rollbackToChecksum/ rollbackToVersion — a consumer that doesn't need rollback/audit history keeps using withCache unchanged; this is a strict superset, additive new constructor (same "new named constructor, never change an existing one" convention withCache's own doc comment explains).

Properties

assetPath → String
final
cacheKey → String?
Storage key the cache is written/read under. Required whenever storage is provided — see the constructor's ArgumentError. Not a named StorageKeys constant (unlike most storage keys in this repo) because a caller may construct several packs (different content types/asset paths) that each need their own cache slot; the caller picks a unique key the same way SaveSlotManager/CheckpointCoordinator let callers supply their own scoped key.
final
contentSecret → String?
Shared secret used to verify a fetched envelope's _checksum (see save_integrity.dart). null means "don't trust any remote content for this pack" — a fetched envelope is rejected outright regardless of what it contains, since there'd be no key to check its signature against.
final
contentVersionResolver → int Function(Map<String, Object?> json)?
ENH-96 (withHistory only): resolves a fetched envelope's content revision number — defaults to _defaultContentVersionResolver (contentVersion, then version, then 0). Used to reject a downgrade (an incoming revision numbered lower than currentContentVersion) without touching _current/the cache/ history at all.
final
current → T?
The latest applied content: the asset fallback until (if ever) a verified fetch replaces it. null only if the asset itself was missing/invalid AND no fetch has applied anything yet.
no setter
currentChecksum → String?
The checksum of whatever current holds right now — null before any verified fetch has ever been applied, or for a constructor that doesn't track history.
no setter
currentContentVersion → int
The content revision number of whatever current holds right now — 0 before any verified fetch has ever been applied (withHistory only; always 0 for the other 2 constructors).
no setter
fetchRemote → Future<Map<String, Object?>> Function()?
final
fromJson → T Function(Map<String, Object?> json)
final
hashCode → int
The hash code for this object.
no setterinherited
history → List<ContentPackHistoryEntry>
Every verified revision currently retained, oldest first, capped at historyCapacity — metadata only (see ContentPackHistoryEntry's doc), never the content body. Empty for RemoteContentPack/ RemoteContentPack.withCache.
no setter
historyCapacity → int?
ENH-96 (withHistory only): max verified revisions kept in history, oldest evicted first once exceeded. null for RemoteContentPack/ RemoteContentPack.withCache — those constructors keep no history at all.
final
maxCacheAge → Duration?
Optional TTL: a cache entry older than this (by nowMsClamped) is treated as invalid and load falls back to the bundled asset instead. null (default) means the cache never expires by age — only rejected for being tampered/malformed/a future schema version.
final
migrate → Map<String, Object?> Function(int fromVersion, Map<String, Object?> json)?
Upgrades a JSON map saved under an older fromVersion to a shape fromJson can read. Defaults to identity (no-op) — a pack that never changes shape doesn't need to supply one.
final
refreshed → Future<void>
Resolves once the background fetch kicked off by the most recent load call has settled (applied or rejected). null if no fetchRemote was ever supplied. Production callers don't need this — current updates in place — it exists for tests that need to wait deterministically past the background refresh.
no setter
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
schemaVersion → int
final
storage → StorageService?
ENH-94: durable cache seam. null (default) disables the cache entirely — load/_refreshFromRemote behave byte-for-byte as before this feature existed.
final

Methods

diagnosticsSummary() → Map<String, Object?>
ENH-96 (withHistory only): metadata-only snapshot safe to paste into a support ticket / log line / DiagnosticsExportBundle section — NEVER the content body (a level layout, a shop catalog, whatever T represents), matching this package's existing default-deny diagnostics convention (DiagnosticsExportBundle.build's configAllowedKeys).
load() → Future<T?>
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
rollbackToChecksum(String checksum) → Future<SdkResult<T>>
ENH-96 (withHistory only): re-activates the verified revision whose checksum is checksum — re-verifies it was indeed in history (never trusts an caller-supplied checksum blindly), persists it as the current cache entry, appends a fresh history entry recording WHEN the rollback itself happened (so the audit trail shows the rollback as its own event, not a silent rewrite of the original entry's timestamp), and only then swaps _current. Returns SdkFailure (no mutation at all) if no history entry matches.
rollbackToVersion(int contentVersion) → Future<SdkResult<T>>
ENH-96 (withHistory only): re-activates contentVersion — if more than one history entry shares that version number (same version re-applied more than once), the most-recently-applied one wins, same "latest wins" convention currentContentVersion itself uses. Returns SdkFailure (no mutation) if no history entry matches.
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited