@diffenderfer-games/hub 2.0.0-next.4 → 2.0.0-next.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # @diffenderfer-games/hub
2
2
 
3
+ ## 2.0.0-next.5
4
+
5
+ ### Patch Changes
6
+
7
+ - 7585522: Saves offline no longer risk the cloud copy. Offline, `getSave(slot)` rejects with a `HubError` whose `code` is `save_unknown` (new export `SAVE_UNKNOWN_CODE`; `isOffline` is `true`) when the device has never seen the hub's copy of that slot, instead of answering `save: null`; it still answers from the device's copy after a snapshot (`prefetchAll`), an online read, a landed write, or for a slot the device wrote and has queued. A `putSave` replayed from the offline queue now sends `base`, the slot's `updatedAt` on the hub as the device last saw it, and the hub refuses it with 409 `save_conflict` when another device saved since (dropped and reported in the `hub:sync` conflicts); writes sent at once while online are unchanged. The client also remembers the signed-in user from `login`, `signup`, `claim` and `ensureGuest` replies, and forgets it on `logout`. Contract: `writeSave` takes an optional `base` and may answer 409; new error code `save_conflict`. Older clients (no `base`) behave as before. See docs/hub.md "Saves offline".
8
+ - Updated dependencies [7585522]
9
+ - @diffenderfer-games/hub-contract@2.0.0-next.5
10
+
3
11
  ## 2.0.0-next.4
4
12
 
5
13
  ### Minor Changes
package/dist/auto.d.ts CHANGED
@@ -8,7 +8,7 @@ import { a as RaceHistoryQuery, c as RaceRuntimeApi, i as RaceEvents, l as RaceS
8
8
  import { f as raceForwarder } from "./shared/race.js";
9
9
  import { n as roomsForwarder } from "./shared/rooms.js";
10
10
  import { $ as HubNotifyApi, A as RacerState, B as SuggestionStatus, C as RaceRecord, D as RaceStats, E as RaceState, F as ReportReason, G as REALTIME_READY_EVENT, H as isRealtimeApi, I as RoomInfo, J as RealtimeHubLink, K as REALTIME_SINGLETON_API, L as SocialConfig, M as Relation, N as ReportInput, O as Racer, P as ReportKind, Q as socialForwarder, R as SocialMe, S as RaceParams, T as RaceStatValue, U as REALTIME_FAILED_EVENT, V as findRealtimeApi, W as REALTIME_FEATURE_VERSION, X as notifyForwarder, Y as linkRealtimeToHub, Z as presenceForwarder, _ as PlayerLite, a as ErrorBody, at as SocialCounts, b as PresenceKind, c as JoinInfo, d as Message, et as HubPresenceApi, f as MessagesPage, g as PlayerCard, h as Party, i as Counts, it as PlayerRef, j as RecentPlayer, k as RacerResult, l as Launch, m as NotifyPrefs, n as ChatMode, nt as OpenInviteOptions, o as FriendEntry, ot as SocialEvents, p as NotifyChange, q as RealtimeApi, r as Conversation, rt as PlayOnlineMethods, s as Invite, t as Audience, tt as HubSocialApi, u as LaunchTicket, v as Presence, w as RaceResult, x as RaceOutcome, y as PresenceInput, z as Suggestion } from "./shared/social.js";
11
- import { $ as GameLeaderboards, A as SyncConflict, B as OfflineHandler, C as TradeBundle, Ct as EndpointReply, D as AuthResponse, E as TradeNoteOptions, F as OfflineEventName, G as QueueOp, H as ReplayContext, I as OfflineListener, J as BufferedEvent, K as QueueOpInput, L as OfflineOp, M as OfflineApi, N as OUTBOX_FULL_CODE, O as ProfileUpdate, P as OfflineEventDetails, Q as GameInfo, R as OUTBOX_MAX_CHARS, S as GrantBundle, St as EndpointName, T as TradeListOptions, U as CLIENT_VERSION_HEADER, V as OfflineKindDefinition, W as ProfilePatch, X as BoardWithTop, Y as Board, Z as GameAnalytics, _ as ResumeLoadOptions, _t as Stats, a as HubErrorDetails, at as InventorySnapshot, b as ResumeSaveInput, bt as User, c as Hub, ct as LeaderboardResult, d as ClientSupport, dt as Profile, et as GameRecords, f as HubResume, ft as ProfileSummary, g as ResumeEntry, gt as SortDir, h as ResumeBlocker, ht as SnapshotResponse, i as HubErrorCode, it as Inventory, j as SyncResult, k as StatResult, l as createHub, lt as MeResponse, m as RESUME_MAX_STATE_BYTES, mt as SaveMeta, n as ContentRejectedError, nt as HubOptions, o as OfflineError, ot as Json, p as RESUME_DEBOUNCE_MS, pt as Save, q as QueuedScoreOptions, r as HubError, rt as HubStatus, s as UNSUPPORTED_BY_HUB, st as LeaderboardEntry, t as HUB_CLIENT_VERSION, tt as GlobalAnalytics, u as hub, ut as Metrics, v as ResumeProgress, vt as SubmitScoreOptions, w as TradeId, wt as GamePayload, x as HubMenuControl, xt as EndpointFields, y as ResumeRun, yt as SubmitScoreResult, z as OUTBOX_MAX_ENTRIES } from "./shared/index.js";
11
+ import { $ as GameInfo, A as StatResult, B as OUTBOX_MAX_ENTRIES, C as GrantBundle, Ct as EndpointName, D as TradeNoteOptions, E as TradeListOptions, F as OfflineEventDetails, G as ProfilePatch, H as OfflineKindDefinition, I as OfflineEventName, J as QueuedScoreOptions, K as QueueOp, L as OfflineListener, M as SyncResult, N as OfflineApi, O as AuthResponse, P as OUTBOX_FULL_CODE, Q as GameAnalytics, R as OfflineOp, S as HubMenuControl, St as EndpointFields, T as TradeId, Tt as GamePayload, U as ReplayContext, V as OfflineHandler, W as CLIENT_VERSION_HEADER, X as Board, Y as BufferedEvent, Z as BoardWithTop, _ as ResumeEntry, _t as SortDir, a as HubErrorDetails, at as Inventory, b as ResumeRun, bt as SubmitScoreResult, c as UNSUPPORTED_BY_HUB, ct as LeaderboardEntry, d as hub, dt as Metrics, et as GameLeaderboards, f as ClientSupport, ft as Profile, g as ResumeBlocker, gt as SnapshotResponse, h as RESUME_MAX_STATE_BYTES, ht as SaveMeta, i as HubErrorCode, it as HubStatus, j as SyncConflict, k as ProfileUpdate, l as Hub, lt as LeaderboardResult, m as RESUME_DEBOUNCE_MS, mt as Save, n as ContentRejectedError, nt as GlobalAnalytics, o as OfflineError, ot as InventorySnapshot, p as HubResume, pt as ProfileSummary, q as QueueOpInput, r as HubError, rt as HubOptions, s as SAVE_UNKNOWN_CODE, st as Json, t as HUB_CLIENT_VERSION, tt as GameRecords, u as createHub, ut as MeResponse, v as ResumeLoadOptions, vt as Stats, w as TradeBundle, wt as EndpointReply, x as ResumeSaveInput, xt as User, y as ResumeProgress, yt as SubmitScoreOptions, z as OUTBOX_MAX_CHARS } from "./shared/index.js";
12
12
  //#region src/auto.d.ts
13
13
  /** What `window.HubSDK` holds. */
14
14
  export interface HubSdkGlobal {
@@ -32,5 +32,5 @@ declare global {
32
32
  }
33
33
  }
34
34
  //#endregion
35
- export { type Anchor, type Audience, type AuthResponse, type AutoPauseOptions, type AxisDef, type AxisSide, type Board, type BoardWithTop, type BufferedEvent, type BusyOptions, CLIENT_VERSION_HEADER, type ChatMode, type ClientSupport, ContentRejectedError, type Conversation, type Counts, DAILY_SINGLETON_API, DAILY_START_EVENT, type DailyAll, type DailyChallenge, type DailyCompleteResponse, type DailyCompletion, type DailyCompletionMeta, type DailyCompletions, type DailyDefinition, type DailyEventDetail, type DailyGameStatus, type DailyHost, type DailyStartResponse, type DailyStatus, DailySystem, type DraggedSpot, type EdgeOffsets, type EndpointFields, type EndpointName, type EndpointReply, type ErrorBody, FOCUS_RING_SHADOW, type FriendEntry, type GameAnalytics, type GameInfo, type GameLeaderboards, type GamePayload, type GameRecords, type GamepadMapping, type GlobalAnalytics, type GrantBundle, type GroupDef, HUB_CLIENT_VERSION, Hub, HubError, type HubErrorCode, type HubErrorDetails, type HubMenuControl, type HubMpApi, type HubNotifyApi, type HubOptions, type HubPresenceApi, type HubRaceApi, HubResume, type HubRoom, type HubRoomsApi, type HubSocialApi, type HubStatus, INPUT_SINGLETON_API, type InputConfig, type InputDef, type InputEvents, type InputHost, type InputModality, type InputOptions, type InputOverrides, type InputReading, type InputSource, InputSystem, type InputSystemOptions, type Insets, type Inventory, type InventorySnapshot, type Invite, type InviteCreated, type InviteLink, type InviteLinkMethods, type InviteOptions, type InviteResult, type JoinInfo, type JoinInfoEvent, type JoinOptions, type Json, type Launch, type LaunchTicket, type LayoutBox, type LayoutItem, type LayoutOptions, type LeaderboardEntry, type LeaderboardResult, MODALITY_ATTRIBUTE, type MeResponse, type MenuBridge, type Message, type MessagesPage, type Metrics, type MouseMapping, type NavBlurEvent, type NavElement, type NavElementKind, type NavFocusEvent, type NavTarget, type NavigableDef, type Notice, type NotifyChange, type NotifyPrefs, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, type OfflineEventDetails, type OfflineEventName, type OfflineHandler, type OfflineKindDefinition, type OfflineListener, type OfflineOp, type OpenInviteOptions, Overlay, type OverlayEdge, type OverlayEvent, type OverlayListener, type OverlayReason, type PageInputControl, type Party, type PlayOnlineMethods, type PlayerCard, type PlayerLite, type PlayerRef, type Presence, type PresenceInput, type PresenceKind, type Profile, type ProfilePatch, type ProfileSummary, type ProfileUpdate, type QueueOp, type QueueOpInput, type QueuedScoreOptions, type REALTIME_FAILED_EVENT, type REALTIME_FEATURE_VERSION, type REALTIME_READY_EVENT, type REALTIME_SINGLETON_API, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, type RaceCreateOptions, type RaceDefinition, type RaceEvents, type RaceHistory, type RaceHistoryQuery, type RaceHistoryViewOptions, type RaceHudPlace, type RaceOutcome, type RaceParams, type RaceRecord, type RaceRecordPlayer, type RaceResult, type RaceResultPlayer, type RaceRuntimeApi, type RaceStart, type RaceStatValue, type RaceState, type RaceStats, type RaceStatusEvent, type Racer, type RacerResult, type RacerState, type RealtimeApi, type RealtimeErrorShape, type RealtimeHubLink, type RecentPlayer, type Rect, type Relation, type ReplayContext, type ReportInput, type ReportKind, type ReportReason, type ResumeBlocker, type ResumeEntry, type ResumeLoadOptions, type ResumeProgress, type ResumeRun, type ResumeSaveInput, type RoomChatLine, type RoomCreateOptions, type RoomEvents, type RoomInfo, type RoomListOptions, type RoomMessage, type RoomReclaimMethods, type RoomSeat, type RoomSeatChange, type RoomSelf, type RoomSendOptions, type RoomState, type RoomStateChange, type RoomStateOptions, type Save, type SaveMeta, type SchemaOutput, type ServerEvents, type Snapshot, type SnapshotResponse, type SocialConfig, type SocialCounts, type SocialEvents, type SocialMe, type SortDir, type StatResult, type Stats, type SubmitScoreOptions, type SubmitScoreResult, type Suggestion, type SuggestionStatus, type SyncConflict, type SyncResult, type TouchMapping, type TradeBundle, type TradeId, type TradeListOptions, type TradeNoteOptions, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, type UiStack, type UiStackHandoff, type User, type Vector, type VirtualControlType, type VirtualDef, type VirtualInputs, type VirtualOverride, boxesTouch, controlSize, createHub, type findRealtimeApi, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, type isRealtimeApi, isUiStack, layoutControls, type linkRealtimeToHub, type multiplayerForwarder as mp, type notifyForwarder as notify, parseInputOverrides, type presenceForwarder as presence, type raceForwarder as race, type roomsForwarder as rooms, type socialForwarder as social, trackInputModality };
35
+ export { type Anchor, type Audience, type AuthResponse, type AutoPauseOptions, type AxisDef, type AxisSide, type Board, type BoardWithTop, type BufferedEvent, type BusyOptions, CLIENT_VERSION_HEADER, type ChatMode, type ClientSupport, ContentRejectedError, type Conversation, type Counts, DAILY_SINGLETON_API, DAILY_START_EVENT, type DailyAll, type DailyChallenge, type DailyCompleteResponse, type DailyCompletion, type DailyCompletionMeta, type DailyCompletions, type DailyDefinition, type DailyEventDetail, type DailyGameStatus, type DailyHost, type DailyStartResponse, type DailyStatus, DailySystem, type DraggedSpot, type EdgeOffsets, type EndpointFields, type EndpointName, type EndpointReply, type ErrorBody, FOCUS_RING_SHADOW, type FriendEntry, type GameAnalytics, type GameInfo, type GameLeaderboards, type GamePayload, type GameRecords, type GamepadMapping, type GlobalAnalytics, type GrantBundle, type GroupDef, HUB_CLIENT_VERSION, Hub, HubError, type HubErrorCode, type HubErrorDetails, type HubMenuControl, type HubMpApi, type HubNotifyApi, type HubOptions, type HubPresenceApi, type HubRaceApi, HubResume, type HubRoom, type HubRoomsApi, type HubSocialApi, type HubStatus, INPUT_SINGLETON_API, type InputConfig, type InputDef, type InputEvents, type InputHost, type InputModality, type InputOptions, type InputOverrides, type InputReading, type InputSource, InputSystem, type InputSystemOptions, type Insets, type Inventory, type InventorySnapshot, type Invite, type InviteCreated, type InviteLink, type InviteLinkMethods, type InviteOptions, type InviteResult, type JoinInfo, type JoinInfoEvent, type JoinOptions, type Json, type Launch, type LaunchTicket, type LayoutBox, type LayoutItem, type LayoutOptions, type LeaderboardEntry, type LeaderboardResult, MODALITY_ATTRIBUTE, type MeResponse, type MenuBridge, type Message, type MessagesPage, type Metrics, type MouseMapping, type NavBlurEvent, type NavElement, type NavElementKind, type NavFocusEvent, type NavTarget, type NavigableDef, type Notice, type NotifyChange, type NotifyPrefs, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, type OfflineEventDetails, type OfflineEventName, type OfflineHandler, type OfflineKindDefinition, type OfflineListener, type OfflineOp, type OpenInviteOptions, Overlay, type OverlayEdge, type OverlayEvent, type OverlayListener, type OverlayReason, type PageInputControl, type Party, type PlayOnlineMethods, type PlayerCard, type PlayerLite, type PlayerRef, type Presence, type PresenceInput, type PresenceKind, type Profile, type ProfilePatch, type ProfileSummary, type ProfileUpdate, type QueueOp, type QueueOpInput, type QueuedScoreOptions, type REALTIME_FAILED_EVENT, type REALTIME_FEATURE_VERSION, type REALTIME_READY_EVENT, type REALTIME_SINGLETON_API, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, type RaceCreateOptions, type RaceDefinition, type RaceEvents, type RaceHistory, type RaceHistoryQuery, type RaceHistoryViewOptions, type RaceHudPlace, type RaceOutcome, type RaceParams, type RaceRecord, type RaceRecordPlayer, type RaceResult, type RaceResultPlayer, type RaceRuntimeApi, type RaceStart, type RaceStatValue, type RaceState, type RaceStats, type RaceStatusEvent, type Racer, type RacerResult, type RacerState, type RealtimeApi, type RealtimeErrorShape, type RealtimeHubLink, type RecentPlayer, type Rect, type Relation, type ReplayContext, type ReportInput, type ReportKind, type ReportReason, type ResumeBlocker, type ResumeEntry, type ResumeLoadOptions, type ResumeProgress, type ResumeRun, type ResumeSaveInput, type RoomChatLine, type RoomCreateOptions, type RoomEvents, type RoomInfo, type RoomListOptions, type RoomMessage, type RoomReclaimMethods, type RoomSeat, type RoomSeatChange, type RoomSelf, type RoomSendOptions, type RoomState, type RoomStateChange, type RoomStateOptions, SAVE_UNKNOWN_CODE, type Save, type SaveMeta, type SchemaOutput, type ServerEvents, type Snapshot, type SnapshotResponse, type SocialConfig, type SocialCounts, type SocialEvents, type SocialMe, type SortDir, type StatResult, type Stats, type SubmitScoreOptions, type SubmitScoreResult, type Suggestion, type SuggestionStatus, type SyncConflict, type SyncResult, type TouchMapping, type TradeBundle, type TradeId, type TradeListOptions, type TradeNoteOptions, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, type UiStack, type UiStackHandoff, type User, type Vector, type VirtualControlType, type VirtualDef, type VirtualInputs, type VirtualOverride, boxesTouch, controlSize, createHub, type findRealtimeApi, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, type isRealtimeApi, isUiStack, layoutControls, type linkRealtimeToHub, type multiplayerForwarder as mp, type notifyForwarder as notify, parseInputOverrides, type presenceForwarder as presence, type raceForwarder as race, type roomsForwarder as rooms, type socialForwarder as social, trackInputModality };
36
36
  //# sourceMappingURL=auto.d.ts.map
package/dist/auto.js CHANGED
@@ -2,7 +2,7 @@ import { a as DailySystem, i as DAILY_START_EVENT, n as getDaily, r as isDailySy
2
2
  import { t as HUB_CLIENT_VERSION } from "./shared/client-version.gen.js";
3
3
  import { a as isPageInputControl, c as layoutControls, d as MODALITY_ATTRIBUTE, f as trackInputModality, i as parseInputOverrides, l as boxesTouch, n as getInput, o as InputSystem, p as FOCUS_RING_SHADOW, r as isInputSystem, s as Overlay, t as INPUT_SINGLETON_API, u as controlSize } from "./shared/input.js";
4
4
  import { n as isUiStack, r as UI_STACK_VERSION, t as getUiStack } from "./shared/page-stack.js";
5
- import { a as RESUME_DEBOUNCE_MS, c as OUTBOX_FULL_CODE, d as OfflineApi, f as ContentRejectedError, h as UNSUPPORTED_BY_HUB, i as HubResume, l as OUTBOX_MAX_CHARS, m as OfflineError, n as createHub, o as RESUME_MAX_STATE_BYTES, p as HubError, r as hub, s as CLIENT_VERSION_HEADER, t as Hub, u as OUTBOX_MAX_ENTRIES } from "./shared/src.js";
5
+ import { a as RESUME_DEBOUNCE_MS, c as OUTBOX_FULL_CODE, d as OfflineApi, f as ContentRejectedError, g as UNSUPPORTED_BY_HUB, h as SAVE_UNKNOWN_CODE, i as HubResume, l as OUTBOX_MAX_CHARS, m as OfflineError, n as createHub, o as RESUME_MAX_STATE_BYTES, p as HubError, r as hub, s as CLIENT_VERSION_HEADER, t as Hub, u as OUTBOX_MAX_ENTRIES } from "./shared/src.js";
6
6
  //#region src/auto.ts
7
7
  /**
8
8
  * `@diffenderfer-games/hub/auto`: everything the main entry exports, plus a
@@ -22,6 +22,6 @@ if (typeof window !== "undefined") window.HubSDK = {
22
22
  ContentRejectedError
23
23
  };
24
24
  //#endregion
25
- export { CLIENT_VERSION_HEADER, ContentRejectedError, DAILY_SINGLETON_API, DAILY_START_EVENT, DailySystem, FOCUS_RING_SHADOW, HUB_CLIENT_VERSION, Hub, HubError, HubResume, INPUT_SINGLETON_API, InputSystem, MODALITY_ATTRIBUTE, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, Overlay, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, boxesTouch, controlSize, createHub, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, isUiStack, layoutControls, parseInputOverrides, trackInputModality };
25
+ export { CLIENT_VERSION_HEADER, ContentRejectedError, DAILY_SINGLETON_API, DAILY_START_EVENT, DailySystem, FOCUS_RING_SHADOW, HUB_CLIENT_VERSION, Hub, HubError, HubResume, INPUT_SINGLETON_API, InputSystem, MODALITY_ATTRIBUTE, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, Overlay, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, SAVE_UNKNOWN_CODE, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, boxesTouch, controlSize, createHub, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, isUiStack, layoutControls, parseInputOverrides, trackInputModality };
26
26
 
27
27
  //# sourceMappingURL=auto.js.map
@@ -5,7 +5,7 @@ import { parseArgs } from "node:util";
5
5
  import { GAME_LABELS, GamePackageJson, HubVersion, splitGameLabels } from "@diffenderfer-games/hub-contract";
6
6
  //#region src/core/version/client-version.gen.ts
7
7
  /** This client's package version, sent as `x-hub-client` on REST calls and `client` in `hello`. */
8
- const HUB_CLIENT_VERSION = "2.0.0-next.4";
8
+ const HUB_CLIENT_VERSION = "2.0.0-next.5";
9
9
  //#endregion
10
10
  //#region src/core/version/semver.ts
11
11
  const VERSION_PATTERN = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([0-9A-Za-z.-]+))?(?:\+[0-9A-Za-z.-]+)?$/;
package/dist/index.d.ts CHANGED
@@ -8,5 +8,5 @@ import { a as RaceHistoryQuery, c as RaceRuntimeApi, i as RaceEvents, l as RaceS
8
8
  import { f as raceForwarder } from "./shared/race.js";
9
9
  import { n as roomsForwarder } from "./shared/rooms.js";
10
10
  import { $ as HubNotifyApi, A as RacerState, B as SuggestionStatus, C as RaceRecord, D as RaceStats, E as RaceState, F as ReportReason, G as REALTIME_READY_EVENT, H as isRealtimeApi, I as RoomInfo, J as RealtimeHubLink, K as REALTIME_SINGLETON_API, L as SocialConfig, M as Relation, N as ReportInput, O as Racer, P as ReportKind, Q as socialForwarder, R as SocialMe, S as RaceParams, T as RaceStatValue, U as REALTIME_FAILED_EVENT, V as findRealtimeApi, W as REALTIME_FEATURE_VERSION, X as notifyForwarder, Y as linkRealtimeToHub, Z as presenceForwarder, _ as PlayerLite, a as ErrorBody, at as SocialCounts, b as PresenceKind, c as JoinInfo, d as Message, et as HubPresenceApi, f as MessagesPage, g as PlayerCard, h as Party, i as Counts, it as PlayerRef, j as RecentPlayer, k as RacerResult, l as Launch, m as NotifyPrefs, n as ChatMode, nt as OpenInviteOptions, o as FriendEntry, ot as SocialEvents, p as NotifyChange, q as RealtimeApi, r as Conversation, rt as PlayOnlineMethods, s as Invite, t as Audience, tt as HubSocialApi, u as LaunchTicket, v as Presence, w as RaceResult, x as RaceOutcome, y as PresenceInput, z as Suggestion } from "./shared/social.js";
11
- import { $ as GameLeaderboards, A as SyncConflict, B as OfflineHandler, C as TradeBundle, Ct as EndpointReply, D as AuthResponse, E as TradeNoteOptions, F as OfflineEventName, G as QueueOp, H as ReplayContext, I as OfflineListener, J as BufferedEvent, K as QueueOpInput, L as OfflineOp, M as OfflineApi, N as OUTBOX_FULL_CODE, O as ProfileUpdate, P as OfflineEventDetails, Q as GameInfo, R as OUTBOX_MAX_CHARS, S as GrantBundle, St as EndpointName, T as TradeListOptions, U as CLIENT_VERSION_HEADER, V as OfflineKindDefinition, W as ProfilePatch, X as BoardWithTop, Y as Board, Z as GameAnalytics, _ as ResumeLoadOptions, _t as Stats, a as HubErrorDetails, at as InventorySnapshot, b as ResumeSaveInput, bt as User, c as Hub, ct as LeaderboardResult, d as ClientSupport, dt as Profile, et as GameRecords, f as HubResume, ft as ProfileSummary, g as ResumeEntry, gt as SortDir, h as ResumeBlocker, ht as SnapshotResponse, i as HubErrorCode, it as Inventory, j as SyncResult, k as StatResult, l as createHub, lt as MeResponse, m as RESUME_MAX_STATE_BYTES, mt as SaveMeta, n as ContentRejectedError, nt as HubOptions, o as OfflineError, ot as Json, p as RESUME_DEBOUNCE_MS, pt as Save, q as QueuedScoreOptions, r as HubError, rt as HubStatus, s as UNSUPPORTED_BY_HUB, st as LeaderboardEntry, t as HUB_CLIENT_VERSION, tt as GlobalAnalytics, u as hub, ut as Metrics, v as ResumeProgress, vt as SubmitScoreOptions, w as TradeId, wt as GamePayload, x as HubMenuControl, xt as EndpointFields, y as ResumeRun, yt as SubmitScoreResult, z as OUTBOX_MAX_ENTRIES } from "./shared/index.js";
12
- export { type Anchor, type Audience, type AuthResponse, type AutoPauseOptions, type AxisDef, type AxisSide, type Board, type BoardWithTop, type BufferedEvent, type BusyOptions, CLIENT_VERSION_HEADER, type ChatMode, type ClientSupport, ContentRejectedError, type Conversation, type Counts, DAILY_SINGLETON_API, DAILY_START_EVENT, type DailyAll, type DailyChallenge, type DailyCompleteResponse, type DailyCompletion, type DailyCompletionMeta, type DailyCompletions, type DailyDefinition, type DailyEventDetail, type DailyGameStatus, type DailyHost, type DailyStartResponse, type DailyStatus, DailySystem, type DraggedSpot, type EdgeOffsets, type EndpointFields, type EndpointName, type EndpointReply, type ErrorBody, FOCUS_RING_SHADOW, type FriendEntry, type GameAnalytics, type GameInfo, type GameLeaderboards, type GamePayload, type GameRecords, type GamepadMapping, type GlobalAnalytics, type GrantBundle, type GroupDef, HUB_CLIENT_VERSION, Hub, HubError, type HubErrorCode, type HubErrorDetails, type HubMenuControl, type HubMpApi, type HubNotifyApi, type HubOptions, type HubPresenceApi, type HubRaceApi, HubResume, type HubRoom, type HubRoomsApi, type HubSocialApi, type HubStatus, INPUT_SINGLETON_API, type InputConfig, type InputDef, type InputEvents, type InputHost, type InputModality, type InputOptions, type InputOverrides, type InputReading, type InputSource, InputSystem, type InputSystemOptions, type Insets, type Inventory, type InventorySnapshot, type Invite, type InviteCreated, type InviteLink, type InviteLinkMethods, type InviteOptions, type InviteResult, type JoinInfo, type JoinInfoEvent, type JoinOptions, type Json, type Launch, type LaunchTicket, type LayoutBox, type LayoutItem, type LayoutOptions, type LeaderboardEntry, type LeaderboardResult, MODALITY_ATTRIBUTE, type MeResponse, type MenuBridge, type Message, type MessagesPage, type Metrics, type MouseMapping, type NavBlurEvent, type NavElement, type NavElementKind, type NavFocusEvent, type NavTarget, type NavigableDef, type Notice, type NotifyChange, type NotifyPrefs, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, type OfflineEventDetails, type OfflineEventName, type OfflineHandler, type OfflineKindDefinition, type OfflineListener, type OfflineOp, type OpenInviteOptions, Overlay, type OverlayEdge, type OverlayEvent, type OverlayListener, type OverlayReason, type PageInputControl, type Party, type PlayOnlineMethods, type PlayerCard, type PlayerLite, type PlayerRef, type Presence, type PresenceInput, type PresenceKind, type Profile, type ProfilePatch, type ProfileSummary, type ProfileUpdate, type QueueOp, type QueueOpInput, type QueuedScoreOptions, type REALTIME_FAILED_EVENT, type REALTIME_FEATURE_VERSION, type REALTIME_READY_EVENT, type REALTIME_SINGLETON_API, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, type RaceCreateOptions, type RaceDefinition, type RaceEvents, type RaceHistory, type RaceHistoryQuery, type RaceHistoryViewOptions, type RaceHudPlace, type RaceOutcome, type RaceParams, type RaceRecord, type RaceRecordPlayer, type RaceResult, type RaceResultPlayer, type RaceRuntimeApi, type RaceStart, type RaceStatValue, type RaceState, type RaceStats, type RaceStatusEvent, type Racer, type RacerResult, type RacerState, type RealtimeApi, type RealtimeErrorShape, type RealtimeHubLink, type RecentPlayer, type Rect, type Relation, type ReplayContext, type ReportInput, type ReportKind, type ReportReason, type ResumeBlocker, type ResumeEntry, type ResumeLoadOptions, type ResumeProgress, type ResumeRun, type ResumeSaveInput, type RoomChatLine, type RoomCreateOptions, type RoomEvents, type RoomInfo, type RoomListOptions, type RoomMessage, type RoomReclaimMethods, type RoomSeat, type RoomSeatChange, type RoomSelf, type RoomSendOptions, type RoomState, type RoomStateChange, type RoomStateOptions, type Save, type SaveMeta, type SchemaOutput, type ServerEvents, type Snapshot, type SnapshotResponse, type SocialConfig, type SocialCounts, type SocialEvents, type SocialMe, type SortDir, type StatResult, type Stats, type SubmitScoreOptions, type SubmitScoreResult, type Suggestion, type SuggestionStatus, type SyncConflict, type SyncResult, type TouchMapping, type TradeBundle, type TradeId, type TradeListOptions, type TradeNoteOptions, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, type UiStack, type UiStackHandoff, type User, type Vector, type VirtualControlType, type VirtualDef, type VirtualInputs, type VirtualOverride, boxesTouch, controlSize, createHub, type findRealtimeApi, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, type isRealtimeApi, isUiStack, layoutControls, type linkRealtimeToHub, type multiplayerForwarder as mp, type notifyForwarder as notify, parseInputOverrides, type presenceForwarder as presence, type raceForwarder as race, type roomsForwarder as rooms, type socialForwarder as social, trackInputModality };
11
+ import { $ as GameInfo, A as StatResult, B as OUTBOX_MAX_ENTRIES, C as GrantBundle, Ct as EndpointName, D as TradeNoteOptions, E as TradeListOptions, F as OfflineEventDetails, G as ProfilePatch, H as OfflineKindDefinition, I as OfflineEventName, J as QueuedScoreOptions, K as QueueOp, L as OfflineListener, M as SyncResult, N as OfflineApi, O as AuthResponse, P as OUTBOX_FULL_CODE, Q as GameAnalytics, R as OfflineOp, S as HubMenuControl, St as EndpointFields, T as TradeId, Tt as GamePayload, U as ReplayContext, V as OfflineHandler, W as CLIENT_VERSION_HEADER, X as Board, Y as BufferedEvent, Z as BoardWithTop, _ as ResumeEntry, _t as SortDir, a as HubErrorDetails, at as Inventory, b as ResumeRun, bt as SubmitScoreResult, c as UNSUPPORTED_BY_HUB, ct as LeaderboardEntry, d as hub, dt as Metrics, et as GameLeaderboards, f as ClientSupport, ft as Profile, g as ResumeBlocker, gt as SnapshotResponse, h as RESUME_MAX_STATE_BYTES, ht as SaveMeta, i as HubErrorCode, it as HubStatus, j as SyncConflict, k as ProfileUpdate, l as Hub, lt as LeaderboardResult, m as RESUME_DEBOUNCE_MS, mt as Save, n as ContentRejectedError, nt as GlobalAnalytics, o as OfflineError, ot as InventorySnapshot, p as HubResume, pt as ProfileSummary, q as QueueOpInput, r as HubError, rt as HubOptions, s as SAVE_UNKNOWN_CODE, st as Json, t as HUB_CLIENT_VERSION, tt as GameRecords, u as createHub, ut as MeResponse, v as ResumeLoadOptions, vt as Stats, w as TradeBundle, wt as EndpointReply, x as ResumeSaveInput, xt as User, y as ResumeProgress, yt as SubmitScoreOptions, z as OUTBOX_MAX_CHARS } from "./shared/index.js";
12
+ export { type Anchor, type Audience, type AuthResponse, type AutoPauseOptions, type AxisDef, type AxisSide, type Board, type BoardWithTop, type BufferedEvent, type BusyOptions, CLIENT_VERSION_HEADER, type ChatMode, type ClientSupport, ContentRejectedError, type Conversation, type Counts, DAILY_SINGLETON_API, DAILY_START_EVENT, type DailyAll, type DailyChallenge, type DailyCompleteResponse, type DailyCompletion, type DailyCompletionMeta, type DailyCompletions, type DailyDefinition, type DailyEventDetail, type DailyGameStatus, type DailyHost, type DailyStartResponse, type DailyStatus, DailySystem, type DraggedSpot, type EdgeOffsets, type EndpointFields, type EndpointName, type EndpointReply, type ErrorBody, FOCUS_RING_SHADOW, type FriendEntry, type GameAnalytics, type GameInfo, type GameLeaderboards, type GamePayload, type GameRecords, type GamepadMapping, type GlobalAnalytics, type GrantBundle, type GroupDef, HUB_CLIENT_VERSION, Hub, HubError, type HubErrorCode, type HubErrorDetails, type HubMenuControl, type HubMpApi, type HubNotifyApi, type HubOptions, type HubPresenceApi, type HubRaceApi, HubResume, type HubRoom, type HubRoomsApi, type HubSocialApi, type HubStatus, INPUT_SINGLETON_API, type InputConfig, type InputDef, type InputEvents, type InputHost, type InputModality, type InputOptions, type InputOverrides, type InputReading, type InputSource, InputSystem, type InputSystemOptions, type Insets, type Inventory, type InventorySnapshot, type Invite, type InviteCreated, type InviteLink, type InviteLinkMethods, type InviteOptions, type InviteResult, type JoinInfo, type JoinInfoEvent, type JoinOptions, type Json, type Launch, type LaunchTicket, type LayoutBox, type LayoutItem, type LayoutOptions, type LeaderboardEntry, type LeaderboardResult, MODALITY_ATTRIBUTE, type MeResponse, type MenuBridge, type Message, type MessagesPage, type Metrics, type MouseMapping, type NavBlurEvent, type NavElement, type NavElementKind, type NavFocusEvent, type NavTarget, type NavigableDef, type Notice, type NotifyChange, type NotifyPrefs, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, type OfflineEventDetails, type OfflineEventName, type OfflineHandler, type OfflineKindDefinition, type OfflineListener, type OfflineOp, type OpenInviteOptions, Overlay, type OverlayEdge, type OverlayEvent, type OverlayListener, type OverlayReason, type PageInputControl, type Party, type PlayOnlineMethods, type PlayerCard, type PlayerLite, type PlayerRef, type Presence, type PresenceInput, type PresenceKind, type Profile, type ProfilePatch, type ProfileSummary, type ProfileUpdate, type QueueOp, type QueueOpInput, type QueuedScoreOptions, type REALTIME_FAILED_EVENT, type REALTIME_FEATURE_VERSION, type REALTIME_READY_EVENT, type REALTIME_SINGLETON_API, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, type RaceCreateOptions, type RaceDefinition, type RaceEvents, type RaceHistory, type RaceHistoryQuery, type RaceHistoryViewOptions, type RaceHudPlace, type RaceOutcome, type RaceParams, type RaceRecord, type RaceRecordPlayer, type RaceResult, type RaceResultPlayer, type RaceRuntimeApi, type RaceStart, type RaceStatValue, type RaceState, type RaceStats, type RaceStatusEvent, type Racer, type RacerResult, type RacerState, type RealtimeApi, type RealtimeErrorShape, type RealtimeHubLink, type RecentPlayer, type Rect, type Relation, type ReplayContext, type ReportInput, type ReportKind, type ReportReason, type ResumeBlocker, type ResumeEntry, type ResumeLoadOptions, type ResumeProgress, type ResumeRun, type ResumeSaveInput, type RoomChatLine, type RoomCreateOptions, type RoomEvents, type RoomInfo, type RoomListOptions, type RoomMessage, type RoomReclaimMethods, type RoomSeat, type RoomSeatChange, type RoomSelf, type RoomSendOptions, type RoomState, type RoomStateChange, type RoomStateOptions, SAVE_UNKNOWN_CODE, type Save, type SaveMeta, type SchemaOutput, type ServerEvents, type Snapshot, type SnapshotResponse, type SocialConfig, type SocialCounts, type SocialEvents, type SocialMe, type SortDir, type StatResult, type Stats, type SubmitScoreOptions, type SubmitScoreResult, type Suggestion, type SuggestionStatus, type SyncConflict, type SyncResult, type TouchMapping, type TradeBundle, type TradeId, type TradeListOptions, type TradeNoteOptions, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, type UiStack, type UiStackHandoff, type User, type Vector, type VirtualControlType, type VirtualDef, type VirtualInputs, type VirtualOverride, boxesTouch, controlSize, createHub, type findRealtimeApi, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, type isRealtimeApi, isUiStack, layoutControls, type linkRealtimeToHub, type multiplayerForwarder as mp, type notifyForwarder as notify, parseInputOverrides, type presenceForwarder as presence, type raceForwarder as race, type roomsForwarder as rooms, type socialForwarder as social, trackInputModality };
package/dist/index.js CHANGED
@@ -2,5 +2,5 @@ import { a as DailySystem, i as DAILY_START_EVENT, n as getDaily, r as isDailySy
2
2
  import { t as HUB_CLIENT_VERSION } from "./shared/client-version.gen.js";
3
3
  import { a as isPageInputControl, c as layoutControls, d as MODALITY_ATTRIBUTE, f as trackInputModality, i as parseInputOverrides, l as boxesTouch, n as getInput, o as InputSystem, p as FOCUS_RING_SHADOW, r as isInputSystem, s as Overlay, t as INPUT_SINGLETON_API, u as controlSize } from "./shared/input.js";
4
4
  import { n as isUiStack, r as UI_STACK_VERSION, t as getUiStack } from "./shared/page-stack.js";
5
- import { a as RESUME_DEBOUNCE_MS, c as OUTBOX_FULL_CODE, d as OfflineApi, f as ContentRejectedError, h as UNSUPPORTED_BY_HUB, i as HubResume, l as OUTBOX_MAX_CHARS, m as OfflineError, n as createHub, o as RESUME_MAX_STATE_BYTES, p as HubError, r as hub, s as CLIENT_VERSION_HEADER, t as Hub, u as OUTBOX_MAX_ENTRIES } from "./shared/src.js";
6
- export { CLIENT_VERSION_HEADER, ContentRejectedError, DAILY_SINGLETON_API, DAILY_START_EVENT, DailySystem, FOCUS_RING_SHADOW, HUB_CLIENT_VERSION, Hub, HubError, HubResume, INPUT_SINGLETON_API, InputSystem, MODALITY_ATTRIBUTE, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, Overlay, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, boxesTouch, controlSize, createHub, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, isUiStack, layoutControls, parseInputOverrides, trackInputModality };
5
+ import { a as RESUME_DEBOUNCE_MS, c as OUTBOX_FULL_CODE, d as OfflineApi, f as ContentRejectedError, g as UNSUPPORTED_BY_HUB, h as SAVE_UNKNOWN_CODE, i as HubResume, l as OUTBOX_MAX_CHARS, m as OfflineError, n as createHub, o as RESUME_MAX_STATE_BYTES, p as HubError, r as hub, s as CLIENT_VERSION_HEADER, t as Hub, u as OUTBOX_MAX_ENTRIES } from "./shared/src.js";
6
+ export { CLIENT_VERSION_HEADER, ContentRejectedError, DAILY_SINGLETON_API, DAILY_START_EVENT, DailySystem, FOCUS_RING_SHADOW, HUB_CLIENT_VERSION, Hub, HubError, HubResume, INPUT_SINGLETON_API, InputSystem, MODALITY_ATTRIBUTE, OUTBOX_FULL_CODE, OUTBOX_MAX_CHARS, OUTBOX_MAX_ENTRIES, OfflineApi, OfflineError, Overlay, RESUME_DEBOUNCE_MS, RESUME_MAX_STATE_BYTES, SAVE_UNKNOWN_CODE, UI_STACK_VERSION, UNSUPPORTED_BY_HUB, boxesTouch, controlSize, createHub, getDaily, getInput, getUiStack, hub, isDailySystem, isInputSystem, isPageInputControl, isUiStack, layoutControls, parseInputOverrides, trackInputModality };
@@ -1,6 +1,6 @@
1
1
  //#region src/core/version/client-version.gen.ts
2
2
  /** This client's package version, sent as `x-hub-client` on REST calls and `client` in `hello`. */
3
- const HUB_CLIENT_VERSION = "2.0.0-next.4";
3
+ const HUB_CLIENT_VERSION = "2.0.0-next.5";
4
4
  //#endregion
5
5
  export { HUB_CLIENT_VERSION as t };
6
6
 
@@ -197,7 +197,7 @@ let claimedDaily = null;
197
197
  function getDaily(host) {
198
198
  claimedDaily ?? (claimedDaily = claimSingleton({
199
199
  name: "daily",
200
- version: "2.0.0-next.4",
200
+ version: "2.0.0-next.5",
201
201
  api: 1,
202
202
  create: () => new DailySystem(host),
203
203
  isInstance: isDailySystem
@@ -256,6 +256,16 @@ declare class LocalStore {
256
256
  putSave(slot: string, saveData: Json, label: string | null): number;
257
257
  /** Forget a save. */
258
258
  deleteSave(slot: string): void;
259
+ /**
260
+ * The hub's `updatedAt` for `slot` as the current user last saw it (`null`:
261
+ * the slot was empty), or `undefined` when this device doesn't know.
262
+ */
263
+ cloudSave(slot: string): number | null | undefined;
264
+ /** Note the hub's copy of `slot` (just read or written) for the current user: its `updatedAt`, or `null` when empty. */
265
+ rememberCloudSave(slot: string, updatedAt: number | null): void;
266
+ /** Cache a slot as read from the hub (forgetting it when the hub has none), and note the hub's copy. */
267
+ cacheCloudSave(slot: string, save: Save | null): void;
268
+ private readCloudSaves;
259
269
  /** This game's cached stats. */
260
270
  getStats(): Stats;
261
271
  /** Apply one stat update and return the stat's new value. */
@@ -278,7 +288,12 @@ declare class LocalStore {
278
288
  setEvents(events: readonly BufferedEvent[]): void;
279
289
  /** Buffer one event (capped; heartbeats are dropped first). */
280
290
  bufferEvent(type: string): void;
281
- /** Fan a `/me/snapshot` reply out across every game's cache keys. */
291
+ /**
292
+ * Fan a `/me/snapshot` reply out across every game's cache keys. It lists
293
+ * every save slot of every game the player has played, so each game's
294
+ * slots on the hub become known (and this game's are all empty when it
295
+ * isn't listed).
296
+ */
282
297
  hydrate(snapshot: SnapshotResponse): void;
283
298
  }
284
299
  //#endregion
@@ -740,6 +755,8 @@ declare class HubProgress extends HubConnection {
740
755
  ok: true;
741
756
  }>;
742
757
  private signInLocally;
758
+ /** Remember who the hub signed in, so the cache's notes about the hub's copies are read for the right player. */
759
+ private signedIn;
743
760
  /** This game's profile for the current user, or `profile: null`. `TData` is the game's own data type. */
744
761
  getProfile<TData = Json>(): Promise<{
745
762
  profile: Profile<TData> | null;
@@ -752,10 +769,18 @@ declare class HubProgress extends HubConnection {
752
769
  listSaves(): Promise<{
753
770
  saves: SaveMeta[];
754
771
  }>;
755
- /** One slot's contents, or `save: null` when the slot is empty. `TData` is the game's own save type. */
772
+ /**
773
+ * One slot's contents, or `save: null` when the slot is empty. `TData` is
774
+ * the game's own save type. Offline it answers from the device's copy, but
775
+ * only when the device knows the hub's copy of the slot; otherwise (a fresh
776
+ * device or cleared browser) it rejects with a {@link HubError} whose
777
+ * `code` is `save_unknown` (`isOffline` is `true`), never a false "empty".
778
+ */
756
779
  getSave<TData = Json>(slot: string): Promise<{
757
780
  save: Save<TData> | null;
758
781
  }>;
782
+ /** The device's copy of `slot`, when it can be trusted offline (see {@link getSave}). */
783
+ private offlineSave;
759
784
  /** Create or overwrite a save slot. `saveData` is any JSON up to 64 KB. */
760
785
  putSave<TData extends GamePayload = Json>(slot: string, saveData: TData, label?: string): Promise<{
761
786
  ok: true;
@@ -1119,12 +1144,18 @@ declare function createHub(options?: HubOptions): Hub;
1119
1144
  declare const UNSUPPORTED_BY_HUB = "unsupported_by_hub";
1120
1145
  /** The code the social runtime gives a call it could not send (no connection). */
1121
1146
  declare const OFFLINE_CODE = "offline";
1147
+ /**
1148
+ * The code `getSave` rejects with when the hub can't be reached and this
1149
+ * device has no copy of the slot it can trust (a fresh device or cleared
1150
+ * browser): the slot may well hold a save, so it is not reported empty.
1151
+ */
1152
+ declare const SAVE_UNKNOWN_CODE = "save_unknown";
1122
1153
  /**
1123
1154
  * A machine-readable error code: one of the contract's public codes
1124
1155
  * (`content_rejected`, `rate_limited`, `suspended`, `claim_required`, ...),
1125
1156
  * {@link UNSUPPORTED_BY_HUB}, or a code a newer hub added.
1126
1157
  */
1127
- type HubErrorCode = ErrorCode | typeof UNSUPPORTED_BY_HUB | typeof OFFLINE_CODE | (string & Record<never, never>);
1158
+ type HubErrorCode = ErrorCode | typeof UNSUPPORTED_BY_HUB | typeof OFFLINE_CODE | typeof SAVE_UNKNOWN_CODE | (string & Record<never, never>);
1128
1159
  /** The machine-readable extras the hub attaches to an error body. */
1129
1160
  interface HubErrorDetails {
1130
1161
  /** A stable error code, for code to branch on. */
@@ -1179,7 +1210,7 @@ declare class ContentRejectedError extends HubError {
1179
1210
  //#endregion
1180
1211
  //#region src/core/version/client-version.gen.d.ts
1181
1212
  /** This client's package version, sent as `x-hub-client` on REST calls and `client` in `hello`. */
1182
- declare const HUB_CLIENT_VERSION = "2.0.0-next.4";
1213
+ declare const HUB_CLIENT_VERSION = "2.0.0-next.5";
1183
1214
  //#endregion
1184
- export { GameLeaderboards as $, SyncConflict as A, OfflineHandler as B, TradeBundle as C, EndpointReply as Ct, AuthResponse as D, TradeNoteOptions as E, OfflineEventName as F, QueueOp as G, ReplayContext as H, OfflineListener as I, BufferedEvent as J, QueueOpInput as K, OfflineOp as L, OfflineApi as M, OUTBOX_FULL_CODE as N, ProfileUpdate as O, OfflineEventDetails as P, GameInfo$1 as Q, OUTBOX_MAX_CHARS as R, GrantBundle as S, EndpointName as St, TradeListOptions as T, CLIENT_VERSION_HEADER as U, OfflineKindDefinition as V, ProfilePatch as W, BoardWithTop as X, Board$1 as Y, GameAnalytics as Z, ResumeLoadOptions as _, Stats$1 as _t, HubErrorDetails as a, InventorySnapshot as at, ResumeSaveInput as b, User as bt, Hub as c, LeaderboardResult as ct, ClientSupport as d, Profile as dt, GameRecords as et, HubResume as f, ProfileSummary as ft, ResumeEntry as g, SortDir$1 as gt, ResumeBlocker as h, SnapshotResponse as ht, HubErrorCode as i, Inventory$1 as it, SyncResult as j, StatResult as k, createHub as l, MeResponse as lt, RESUME_MAX_STATE_BYTES as m, SaveMeta as mt, ContentRejectedError as n, HubOptions as nt, OfflineError as o, Json$1 as ot, RESUME_DEBOUNCE_MS as p, Save as pt, QueuedScoreOptions as q, HubError as r, HubStatus as rt, UNSUPPORTED_BY_HUB as s, LeaderboardEntry as st, HUB_CLIENT_VERSION as t, GlobalAnalytics as tt, hub as u, Metrics$1 as ut, ResumeProgress as v, SubmitScoreOptions as vt, TradeId as w, GamePayload as wt, HubMenuControl as x, EndpointFields as xt, ResumeRun as y, SubmitScoreResult as yt, OUTBOX_MAX_ENTRIES as z };
1215
+ export { GameInfo$1 as $, StatResult as A, OUTBOX_MAX_ENTRIES as B, GrantBundle as C, EndpointName as Ct, TradeNoteOptions as D, TradeListOptions as E, OfflineEventDetails as F, ProfilePatch as G, OfflineKindDefinition as H, OfflineEventName as I, QueuedScoreOptions as J, QueueOp as K, OfflineListener as L, SyncResult as M, OfflineApi as N, AuthResponse as O, OUTBOX_FULL_CODE as P, GameAnalytics as Q, OfflineOp as R, HubMenuControl as S, EndpointFields as St, TradeId as T, GamePayload as Tt, ReplayContext as U, OfflineHandler as V, CLIENT_VERSION_HEADER as W, Board$1 as X, BufferedEvent as Y, BoardWithTop as Z, ResumeEntry as _, SortDir$1 as _t, HubErrorDetails as a, Inventory$1 as at, ResumeRun as b, SubmitScoreResult as bt, UNSUPPORTED_BY_HUB as c, LeaderboardEntry as ct, hub as d, Metrics$1 as dt, GameLeaderboards as et, ClientSupport as f, Profile as ft, ResumeBlocker as g, SnapshotResponse as gt, RESUME_MAX_STATE_BYTES as h, SaveMeta as ht, HubErrorCode as i, HubStatus as it, SyncConflict as j, ProfileUpdate as k, Hub as l, LeaderboardResult as lt, RESUME_DEBOUNCE_MS as m, Save as mt, ContentRejectedError as n, GlobalAnalytics as nt, OfflineError as o, InventorySnapshot as ot, HubResume as p, ProfileSummary as pt, QueueOpInput as q, HubError as r, HubOptions as rt, SAVE_UNKNOWN_CODE as s, Json$1 as st, HUB_CLIENT_VERSION as t, GameRecords as tt, createHub as u, MeResponse as ut, ResumeLoadOptions as v, Stats$1 as vt, TradeBundle as w, EndpointReply as wt, ResumeSaveInput as x, User as xt, ResumeProgress as y, SubmitScoreOptions as yt, OUTBOX_MAX_CHARS as z };
1185
1216
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":["GameInfo","ContractGameInfo","GameInfo","presence","social","mp","rooms","notify","race"],"sources":["../../src/core/storage/key-value.ts","../../src/core/game-data.ts","../../src/core/rest/endpoint-types.ts","../../src/core/types.ts","../../src/core/offline/events.ts","../../src/core/offline/queue.ts","../../src/core/offline/local-store.ts","../../src/core/rest/rest-client.ts","../../src/core/offline/outbox-entry.ts","../../src/core/offline/outbox-kinds.ts","../../src/core/offline/outbox-store.ts","../../src/core/offline/outbox-replayer.ts","../../src/core/offline/outbox.ts","../../src/core/offline/offline-api.ts","../../src/core/resume/local-resume.ts","../../src/core/connection.ts","../../src/core/hub-progress.ts","../../src/core/hub-economy.ts","../../src/core/menu-control.ts","../../src/core/offline/hub-kinds/resume-kind.ts","../../src/core/resume/resume-types.ts","../../src/core/resume/hub-resume.ts","../../src/core/version/hub-version.ts","../../src/core/hub.ts","../../src/core/errors.ts","../../src/core/version/client-version.gen.ts"],"mappings":";;;;;;;;;;;;;;;;UAQiB;;EAEf,KAAK;;EAEL,MAAM,aAAa;;EAEnB,OAAO;;EAEP;;;;;KCLU;;;;KCFA,qBAAqB;;KAGrB,eAAe,UAAU,gBAAgB,WAAW,gBAAgB;;;;;KAM3E,UAAU,SAAS;WAAyB;WAAyB,YAAY;IAAS,OAAO;;KAG1F,cAAc,UAAU,gBAAgB,UAAU,QAAQ,WAAW,gBAAgB;;;;KCNrF,OAAO,KAAK,kCAAkC,QAAQ,KAAK;;UAGtD;;WAEN;;WAEA;;;UAIM;;WAEN,MAAM;;WAEN,UAAU;;;UAIJ,QAAQ,QAAQ;;WAEtB;;WAEA;;WAEA,MAAM;;WAEN;;;UAIM;;WAEN;;WAEA;;WAEA;;;UAIM,KAAK,QAAQ,cAAc;;WAEjC,MAAM;;;UAIA,mBAAmB,cAAc,cAAc;;WAErD;;WAEA,OAAO;;WAEP,OAAO;;;UAID;;WAEN,OAAO;;WAEP;;WAEA;;;KAIC,oBAAoB;;KAGpB,eAAe;;KAGf,gBAAgB;;UAGX;;WAEN,QAAQ;;WAER,QAAQ;aAAqB;aAAuB;;;;KAInDA,aAAWC;;KAGX,mBAAmB;;KAGnB,oBAAoB;;UAGf;;WAEN;;WAEA;;WAEA;;;UAIM;;WAEN;;WAEA;;WAEA,eAAe;;;;;;;;WAQf;;;;;UCzHM;;WAEN;;WAEA;;;;;UCJM;;WAEN;;WAEA,OAAO;;WAEP,OAAO;;;UAID;;EAEf;;EAEA,OAAO;;;UAIC;;WAEC;;WAEA;;;KAIC;WACG;WAA0B;WAAuB,MAAM;WAAe;;WACtE;WAA6B;;WAC7B;WAA0B;WAAsB;;WAChD;WAA0B;WAAsB;;WAChD;WAA2B;WAAsB;;WACjD;WAA8B;WAAsB;WAAwB,MAAM;;WAClF;WAA6B,OAAO;;;KAGvC,UAAU,eAAe;;;;UC3BpB;;WAEN,OAAO;;WAEP;;;KAIC;;cAyDC;mBACM;mBACA;;EAGL,YAAA,cAAc,QAAO;UAMzB;UAMA;;EAKR,WAAW;;EAKX,QAAQ,MAAM;;EAKd;;EAKA,cAAc;;EAKd,WAAW,cAAc,4BAA4B,aAAa,OAAO;;EAOzE,aAAa;;EAWb,QAAQ,eAAe;;EAKvB,QAAQ,cAAc,UAAU,MAAM;;EAOtC,WAAW;;EAKX,YAAY;;EAMZ,WAAW,aAAa,gBAAgB,MAAM;;EAS9C,cAAc;;EAUd,SAAS,cAAc;;EAKvB,YAAY,aAAa,eAAe,SAAS,qBAAqB;;EAUtE,mBAAmB;;EAOnB,gBAAgB;;EAKhB,aAAa,WAAW,YAAY;;EAMpC,aAAa;;EAKb,UAAU,iBAAiB;;EAK3B,YAAY;;EAKZ,QAAQ,UAAU;;;;;cC7NP;;KAgBD,cAAc,UAAU,gBAAgB,QAAQ,eAAe,YAAY,eAAe,MACjG,SAAS,eAAe,OACxB,QAAQ,eAAe;;UAGX;;WAEN;;EAET,KAAK,UAAU,cAAc,MAAM,MAAM,gBAAgB,cAAc,KAAK,QAAQ,cAAc;;;;;;EAMlG,SAAS,UAAU,cAAc,eAAe,MAAM,MAAM,gBAAgB,cAAc,KAAK,QAAQ,cAAc;;;;;UCnCtG;;WAEN;;WAEA;;WAEA,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;;;;UCdM;;WAEN;;WAEA;;WAEA;;;;;;;;EAQT,KAAK,UAAU,cAAc,MAAM,MAAM,gBAAgB,cAAc,KAAK,QAAQ,cAAc;;EAElG,IAAI;;;KAIM,eAAe,YAAY,SAAS,SAAS,SAAS,kBAAkB,QAAQ,oBAAoB;;UAG/F,sBAAsB;;WAE5B,KAAK,eAAe;;;;;WAKpB,YAAY,SAAS;;;;;WAKrB,SAAS,SAAS,SAAS,UAAU,YAAY;;WAEjD;;;UAIM;;WAEN,MAAM,SAAS,MAAM,SAAS,kBAAkB,QAAQ,oBAAoB;;WAE5E,UAAU,SAAS;WACnB,YAAY,SAAS;WACrB,SAAS,SAAS,MAAM,UAAU,SAAS;WAC3C;;;cAOE;mBACM;;EAGjB,SAAS,cAAc,YAAY;;EAKnC,IAAI,eAAe;;;;;cC/DR;;cAGA;;cAGA;;WAEF;mBACQ;;EAGL,YAAA,cAAc,OAAO;;EAMjC,QAAQ;;EAeR;;EAKA,IAAI,aAAa;;EAMjB,KAAK;;EAUL,IAAI,OAAO;;EAOX,OAAO;;EAKP,KAAK;UAIG;;;;;UChEO;;WAEN,OAAO;;WAEP;;;KAIC;;UAGK;;WAEN,UAAU;;WAEV,QAAQ;;WAER,YAAY;;;;;KCdX,YAAY;;UAGP;;EAEf;aAAmB,IAAI;;;EAEvB;aAAqB,IAAI;aAAoB,SAAS;;;EAEtD;aAAmB,IAAI;aAAoB;;;;KAIjC,yBAAyB;;KAGzB,gBAAgB,aAAa,qBAAqB,QAAQ,oBAAoB;;cAG7E;;UAMI;;WAEN;;WAEA,OAAO;;WAEP,MAAM;;WAEN,OAAO,cAAc,QAAQ,oBAAoB;;WAEjD;;WAEA;;WAEA;WACA;;;cAIE;;WAEF,OAAK;;WAEL,SAAS;mBACD;mBACA;mBACA;UACT;UACA;UACA;mBACS;;EAKL,YAAA,SAAS;;EAwBrB,OAAO,cAAc,YAAY;;EAKjC,MAAM,cAAc,SAAS,OAAO;;;;;;EAWpC,IAAI,IAAI;;EAkBF,OAAO,IAAI,cAAc,QAAQ;;EASjC,OAAO,oBAAkB,QAAQ;;EAYvC,WAAW;;EAKX;;EAKA,GAAG,aAAa,kBAAkB,MAAM,MAAM,UAAU,gBAAgB;UAQhE;UAYA;UAgBA;UAcA;;;;;UC9MO;;WAEN,QAAQ;;EAEjB;;;cAIW;mBACM;;EAGL,YAAA,MAAM;;;;;;EASlB,OAAO,gBAAgB,aAAa,cAAc,YAAY,eAAe,WAAW,sBAAsB;;;;;;;EAc9G,QAAQ,cAAc,SAAS,cAAc;;EAQ7C,WAAW;;EAKX,GAAG,aAAa,kBAAkB,MAAM,MAAM,UAAU,gBAAgB;;;;;UCzCzD;WACN;WACA;;WAEA;WACA;WACA;WACA;WACA;;WAEA;WACA,OAAO;WACP;;;UAIM;WACN;WACA;WACA;WACA;;;KAIC,cAAc,WAAW;;cAmCxB;mBACM;EAEL,YAAA,OAAO;;EAKnB,KAAK,eAAe;;EAMpB,MAAM,cAAc,QAAQ;;EAO5B,OAAO;;EAKP,WAAW,cAAc;;EAazB,OAAO,YAAY;;;;;UC/FJ;;WAEN,IAAI;;WAEJ;;;UAIM;;WAEN;;WAEA,WAAW;;;UAaL;;WAEN;;;cAIE;;WAEF;;WAEA;;qBAEU,MAAM;;qBAEN,OAAO;;qBAEP,QAAQ;;qBAER,aAAa;mBACf;UACT;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;;EAGL,YAAA,UAAS;;MA8CjB;;MAKA;;;;;MAQA,WAAW;;EAaf;;EAKA,SAAS,WAAW,QAAQ;;YAQlB;;YAaA,aAAa;;YAcb,cAAc;;;;;;YAYR,aAAa,OAAO,mBAAmB,QAAQ,QAAQ,qBAAqB,QAAQ,QAAQ;;;;;;;;;;YAwB5F,cAAc,OAAO,cAAc,SAAS,MAAM,mBAAmB,OAAO,eAAe,QAAQ,qBAAqB,oBAAoB,QAAQ;;;;;;EAkCpK,QAAQ,QAAQ;;UAKR;;;;;;UAUA;UAkBA;UAUM;;UAgBA;;UAgBN;;UAiBA;;;;;;EAwBF,YAAY,qBAAmB,QAAQ;;;;;UC3W9B;;WAEN,MAAM;;;UAIA;;WAEN;;WAEA;;;UAIM,cAAc,cAAc,cAAc;;WAEhD;;WAEA,OAAO;;;cAcL,oBAAoB;;EAE/B,MAAM,QAAQ;;;;;;EAYd,eAAe,QAAQ;;;;;;EAiBvB,MAAM,kBAAkB,mBAAmB,QAAQ;;EAKnD,OAAO,kBAAkB,mBAAmB,QAAQ;;EAKpD,MAAM,kBAAkB,mBAAmB,QAAQ;;EAKnD,UAAU;IAAU;;UAOZ;;EAMR,WAAW,QAAQ,SAAS;IAAU,SAAS,QAAQ;;;EAQvD,WAAW,cAAc,cAAc,MAAM,OAAO,cAAc,SAAS;IAAU,SAAS,QAAQ;;;EAYtG,aAAa;IAAU,OAAO;;;EAK9B,QAAQ,QAAQ,MAAM,eAAe;IAAU,MAAM,KAAK;;;EAS1D,QAAQ,cAAc,cAAc,MAAM,cAAc,UAAU,OAAO,iBAAiB;IAAU;IAAU;;;EAM9G,WAAW,eAAe;IAAU;;;EAQpC,SAAS,kBAAkB;IAAU,OAAO;;;EAQ5C,QAAQ,aAAa,gBAAgB,QAAQ;;EAK7C,SAAS,aAAa,kBAAa,QAAQ;;EAK3C,QAAQ,aAAa,gBAAgB,QAAQ;;EAK7C,YAAY,cAAc,cAAc,MAAM,kBAAkB,eAAe,OAAM,mBAAmB,SAAc,QAAQ;;EAU9H,eAAe,kBAAkB,iBAAa,QAAQ;;EAYtD,oBAAoB;IAAU,QAAQ;;;;;;KCpL5B,cAAc,KAAK;;KAGnB,cAAc;;KAGd;;UAGK;;WAEN;;;KAgBC,mBAAmB,KAAK;;cAGvB,mBAAmB;;EAE9B,cAAc;;EAKd,aAAa;;EAKP,MAAM,eAAe;;EAoB3B,aAAa,QAAQ;;EAKrB,mBAAmB,QAAQ;;EAK3B,aAAa;IAAU,OAAOC;;;EAK9B,gBAAgB,eAAU,QAAQ;;;;;EAQlC,aAAa;IAAU;;;EASvB,YAAY,cAAc,gBAAgB;IAAU;;;EAUpD,UAAU,mBAAmB,QAAQ;;EAQrC,gBAAgB,QAAQ;;EAKxB,gBAAgB,iBAAiB,QAAQ;;;;;EAQzC,MAAM,QAAQ,cAAc,QAAQ;;;;;;EAUpC,SAAS,OAAO,aAAa,OAAO,cAAc,QAAQ;;EAe1D,KAAK,OAAO,wBAAwB,gBAAgB,QAAQ;;EAK5D,IAAI,eAAe,OAAO,yBAAyB,QAAQ;;EAK3D,aAAa;IAAU;;;EAKvB,eAAe,QAAQ;;EAKvB,WAAW,OAAM,mBAAwB,QAAQ;;EAKjD,YAAY,yBAAyB,OAAO,aAAa,OAAO,aAAa,OAAM,mBAAwB,QAAQ;;EAKnH,YAAY,IAAI,UAAU,QAAQ;;EAKlC,aAAa,IAAI,UAAU,QAAQ;;EAKnC,YAAY,IAAI,UAAU,QAAQ;;EAKlC,aAAa,IAAI,SAAS,OAAO,aAAa,OAAO,aAAa,OAAM,mBAAwB,QAAQ;;;;QCvMlG;YACI;;IAER;;;;UAKa;;;;;;;;EAQf,WAAW;;EAEX;;EAEA;;WAES;;;;;KCZC;WACD;WACA;WACA;WACA;WACA;WACA;WACA,QAAQ;;;KAIP;WAAgC;WAA0B;;;KAG1D,gBAAgB,oBAAoB;;;;UC9B/B;;WAEN;;WAEA;;;UAIM,gBAAgB,eAAe;;WAErC,OAAO;WACP,UAAU;;WAEV;;WAEA;;;UAIM,UAAU;WAChB,OAAO;WACP;aAAqB;aAAwB;;WAC7C;WACA;;WAEA;;;UAIM;;;;;;WAMN;;;UAIM;;WAEN;WACA;;WAEA;;WAEA;WACA;aAAqB;aAAwB;;WAC7C;;WAEA;;WAEA;;;KAIC;;;;cCpCC;;cAGA;;UAaI;;WAEN;WACA,OAAO;;EAEhB;;EAEA;;EAEA,WAAW;;EAEX,KAAK,SAAS,gBAAgB;;EAE9B,aAAa,QAAQ;;EAErB,aAAa,QAAQ;;EAErB,WAAW,eAAe;;;cAyBf;mBACM;UACT;UACA;UACA;mBACS;EAEL,YAAA,MAAM;;;;;;MASd;;MAKA,aAAa;;;;;;;EAUjB,KAAK,eAAe,aAAa,KAAK,gBAAgB;;EA6BhD,SAAS;;EAaT,SAAS;;EAaT,IAAI,SAAS,aAAa,UAAS,oBAAyB,QAAQ,UAAU;;EAuB9E,KAAK,SAAS,aAAa,UAAS,oBAAyB,QAAQ;;EAKrE,QAAQ,QAAQ;;EAKhB,QAAQ,eAAe;;UAMrB;UASA;UAOA;;UAUA;;;;;KChOE;;;;;;;;;;;;cCgBC,YAAY;;WAEd;UAED;;;;;EAMR,cAAc,QAAQ;;EAKhB,iBAAiB,QAAQ;;;;;;MAS3B,SAAS;;MAUT,WAAW,kBAAkB;;MAK7B,QAAQ;;;;;;MASR,SAAS;;MAKT,mBAAmBC;;MAKnB,iBAAiBC;;MAKjB,aAAaC;;MAKb,gBAAgBC;;MAKhB,iBAAiBC;;MAKjB,eAAeC;;;;;;MASf,UAAU;;UAMN;;UAiCA;;;cAqBG,KAAK;;iBAMF,UAAU,UAAS,aAAkB;;;;;;;cCzKxC;;cAGA;;;;;;KAOD,eAAe,mBAAmB,4BAA4B,yBAAyB;;UAGlF;;WAEN,OAAO;;WAEP,SAAS,yBAAyB;;WAElC;;WAEA;;WAEA;;;;;;;;cASE,iBAAiB;;WAEnB;;WAEA,MAAM;;WAEN;;WAEA;;WAEA;;WAEA;;EAGG,YAAA,iBAAiB,iBAAiB,UAAS;;MAYnD;;;cAMO,qBAAqB;;EAEpB,YAAA;;;;;;;cAWD,6BAA6B;;EAE5B,YAAA,iBAAiB,4BAA4B,SAAS;;;;;cC1FvD"}
1
+ {"version":3,"file":"index.d.ts","names":["GameInfo","ContractGameInfo","GameInfo","presence","social","mp","rooms","notify","race"],"sources":["../../src/core/storage/key-value.ts","../../src/core/game-data.ts","../../src/core/rest/endpoint-types.ts","../../src/core/types.ts","../../src/core/offline/events.ts","../../src/core/offline/queue.ts","../../src/core/offline/local-store.ts","../../src/core/rest/rest-client.ts","../../src/core/offline/outbox-entry.ts","../../src/core/offline/outbox-kinds.ts","../../src/core/offline/outbox-store.ts","../../src/core/offline/outbox-replayer.ts","../../src/core/offline/outbox.ts","../../src/core/offline/offline-api.ts","../../src/core/resume/local-resume.ts","../../src/core/connection.ts","../../src/core/hub-progress.ts","../../src/core/hub-economy.ts","../../src/core/menu-control.ts","../../src/core/offline/hub-kinds/resume-kind.ts","../../src/core/resume/resume-types.ts","../../src/core/resume/hub-resume.ts","../../src/core/version/hub-version.ts","../../src/core/hub.ts","../../src/core/errors.ts","../../src/core/version/client-version.gen.ts"],"mappings":";;;;;;;;;;;;;;;;UAQiB;;EAEf,KAAK;;EAEL,MAAM,aAAa;;EAEnB,OAAO;;EAEP;;;;;KCLU;;;;KCFA,qBAAqB;;KAGrB,eAAe,UAAU,gBAAgB,WAAW,gBAAgB;;;;;KAM3E,UAAU,SAAS;WAAyB;WAAyB,YAAY;IAAS,OAAO;;KAG1F,cAAc,UAAU,gBAAgB,UAAU,QAAQ,WAAW,gBAAgB;;;;KCNrF,OAAO,KAAK,kCAAkC,QAAQ,KAAK;;UAGtD;;WAEN;;WAEA;;;UAIM;;WAEN,MAAM;;WAEN,UAAU;;;UAIJ,QAAQ,QAAQ;;WAEtB;;WAEA;;WAEA,MAAM;;WAEN;;;UAIM;;WAEN;;WAEA;;WAEA;;;UAIM,KAAK,QAAQ,cAAc;;WAEjC,MAAM;;;UAIA,mBAAmB,cAAc,cAAc;;WAErD;;WAEA,OAAO;;WAEP,OAAO;;;UAID;;WAEN,OAAO;;WAEP;;WAEA;;;KAIC,oBAAoB;;KAGpB,eAAe;;KAGf,gBAAgB;;UAGX;;WAEN,QAAQ;;WAER,QAAQ;aAAqB;aAAuB;;;;KAInDA,aAAWC;;KAGX,mBAAmB;;KAGnB,oBAAoB;;UAGf;;WAEN;;WAEA;;WAEA;;;UAIM;;WAEN;;WAEA;;WAEA,eAAe;;;;;;;;WAQf;;;;;UCzHM;;WAEN;;WAEA;;;;;UCJM;;WAEN;;WAEA,OAAO;;WAEP,OAAO;;;UAID;;EAEf;;EAEA,OAAO;;;UAIC;;WAEC;;WAEA;;;KAIC;WACG;WAA0B;WAAuB,MAAM;WAAe;;WACtE;WAA6B;;WAC7B;WAA0B;WAAsB;;WAChD;WAA0B;WAAsB;;WAChD;WAA2B;WAAsB;;WACjD;WAA8B;WAAsB;WAAwB,MAAM;;WAClF;WAA6B,OAAO;;;KAGvC,UAAU,eAAe;;;;UC1BpB;;WAEN,OAAO;;WAEP;;;KAIC;;cAyDC;mBACM;mBACA;;EAGL,YAAA,cAAc,QAAO;UAMzB;UAMA;;EAKR,WAAW;;EAKX,QAAQ,MAAM;;EAKd;;EAKA,cAAc;;EAKd,WAAW,cAAc,4BAA4B,aAAa,OAAO;;EAOzE,aAAa;;EAWb,QAAQ,eAAe;;EAKvB,QAAQ,cAAc,UAAU,MAAM;;EAOtC,WAAW;;;;;EAQX,UAAU;;EAMV,kBAAkB,cAAc;;EAQhC,eAAe,cAAc,MAAM;UAS3B;;EAKR,YAAY;;EAMZ,WAAW,aAAa,gBAAgB,MAAM;;EAS9C,cAAc;;EAUd,SAAS,cAAc;;EAKvB,YAAY,aAAa,eAAe,SAAS,qBAAqB;;EAUtE,mBAAmB;;EAOnB,gBAAgB;;EAKhB,aAAa,WAAW,YAAY;;EAMpC,aAAa;;EAKb,UAAU,iBAAiB;;EAK3B,YAAY;;;;;;;EAUZ,QAAQ,UAAU;;;;;cClQP;;KAgBD,cAAc,UAAU,gBAAgB,QAAQ,eAAe,YAAY,eAAe,MACjG,SAAS,eAAe,OACxB,QAAQ,eAAe;;UAGX;;WAEN;;EAET,KAAK,UAAU,cAAc,MAAM,MAAM,gBAAgB,cAAc,KAAK,QAAQ,cAAc;;;;;;EAMlG,SAAS,UAAU,cAAc,eAAe,MAAM,MAAM,gBAAgB,cAAc,KAAK,QAAQ,cAAc;;;;;UCnCtG;;WAEN;;WAEA;;WAEA,SAAS;;WAET;;WAEA;;WAEA;;WAEA;;;;;UCdM;;WAEN;;WAEA;;WAEA;;;;;;;;EAQT,KAAK,UAAU,cAAc,MAAM,MAAM,gBAAgB,cAAc,KAAK,QAAQ,cAAc;;EAElG,IAAI;;;KAIM,eAAe,YAAY,SAAS,SAAS,SAAS,kBAAkB,QAAQ,oBAAoB;;UAG/F,sBAAsB;;WAE5B,KAAK,eAAe;;;;;WAKpB,YAAY,SAAS;;;;;WAKrB,SAAS,SAAS,SAAS,UAAU,YAAY;;WAEjD;;;UAIM;;WAEN,MAAM,SAAS,MAAM,SAAS,kBAAkB,QAAQ,oBAAoB;;WAE5E,UAAU,SAAS;WACnB,YAAY,SAAS;WACrB,SAAS,SAAS,MAAM,UAAU,SAAS;WAC3C;;;cAOE;mBACM;;EAGjB,SAAS,cAAc,YAAY;;EAKnC,IAAI,eAAe;;;;;cC/DR;;cAGA;;cAGA;;WAEF;mBACQ;;EAGL,YAAA,cAAc,OAAO;;EAMjC,QAAQ;;EAeR;;EAKA,IAAI,aAAa;;EAMjB,KAAK;;EAUL,IAAI,OAAO;;EAOX,OAAO;;EAKP,KAAK;UAIG;;;;;UChEO;;WAEN,OAAO;;WAEP;;;KAIC;;UAGK;;WAEN,UAAU;;WAEV,QAAQ;;WAER,YAAY;;;;;KCdX,YAAY;;UAGP;;EAEf;aAAmB,IAAI;;;EAEvB;aAAqB,IAAI;aAAoB,SAAS;;;EAEtD;aAAmB,IAAI;aAAoB;;;;KAIjC,yBAAyB;;KAGzB,gBAAgB,aAAa,qBAAqB,QAAQ,oBAAoB;;cAG7E;;UAMI;;WAEN;;WAEA,OAAO;;WAEP,MAAM;;WAEN,OAAO,cAAc,QAAQ,oBAAoB;;WAEjD;;WAEA;;WAEA;WACA;;;cAIE;;WAEF,OAAK;;WAEL,SAAS;mBACD;mBACA;mBACA;UACT;UACA;UACA;mBACS;;EAKL,YAAA,SAAS;;EAwBrB,OAAO,cAAc,YAAY;;EAKjC,MAAM,cAAc,SAAS,OAAO;;;;;;EAWpC,IAAI,IAAI;;EAkBF,OAAO,IAAI,cAAc,QAAQ;;EASjC,OAAO,oBAAkB,QAAQ;;EAYvC,WAAW;;EAKX;;EAKA,GAAG,aAAa,kBAAkB,MAAM,MAAM,UAAU,gBAAgB;UAQhE;UAYA;UAgBA;UAcA;;;;;UC9MO;;WAEN,QAAQ;;EAEjB;;;cAIW;mBACM;;EAGL,YAAA,MAAM;;;;;;EASlB,OAAO,gBAAgB,aAAa,cAAc,YAAY,eAAe,WAAW,sBAAsB;;;;;;;EAc9G,QAAQ,cAAc,SAAS,cAAc;;EAQ7C,WAAW;;EAKX,GAAG,aAAa,kBAAkB,MAAM,MAAM,UAAU,gBAAgB;;;;;UCzCzD;WACN;WACA;;WAEA;WACA;WACA;WACA;WACA;;WAEA;WACA,OAAO;WACP;;;UAIM;WACN;WACA;WACA;WACA;;;KAIC,cAAc,WAAW;;cAmCxB;mBACM;EAEL,YAAA,OAAO;;EAKnB,KAAK,eAAe;;EAMpB,MAAM,cAAc,QAAQ;;EAO5B,OAAO;;EAKP,WAAW,cAAc;;EAazB,OAAO,YAAY;;;;;UC/FJ;;WAEN,IAAI;;WAEJ;;;UAIM;;WAEN;;WAEA,WAAW;;;UAaL;;WAEN;;;cAIE;;WAEF;;WAEA;;qBAEU,MAAM;;qBAEN,OAAO;;qBAEP,QAAQ;;qBAER,aAAa;mBACf;UACT;UACA;UACA;UACA;UACA;UACA;UACA;mBACS;;EAGL,YAAA,UAAS;;MA+CjB;;MAKA;;;;;MAQA,WAAW;;EAaf;;EAKA,SAAS,WAAW,QAAQ;;YAQlB;;YAaA,aAAa;;YAcb,cAAc;;;;;;YAYR,aAAa,OAAO,mBAAmB,QAAQ,QAAQ,qBAAqB,QAAQ,QAAQ;;;;;;;;;;YAwB5F,cAAc,OAAO,cAAc,SAAS,MAAM,mBAAmB,OAAO,eAAe,QAAQ,qBAAqB,oBAAoB,QAAQ;;;;;;EAkCpK,QAAQ,QAAQ;;UAKR;;;;;;UAUA;UAkBA;UAUM;;UAgBA;;UAgBN;;UAiBA;;;;;;EAwBF,YAAY,qBAAmB,QAAQ;;;;;UC1W9B;;WAEN,MAAM;;;UAIA;;WAEN;;WAEA;;;UAIM,cAAc,cAAc,cAAc;;WAEhD;;WAEA,OAAO;;;cAcL,oBAAoB;;EAE/B,MAAM,QAAQ;;;;;;EAYd,eAAe,QAAQ;;;;;;EAiBvB,MAAM,kBAAkB,mBAAmB,QAAQ;;EAKnD,OAAO,kBAAkB,mBAAmB,QAAQ;;EAKpD,MAAM,kBAAkB,mBAAmB,QAAQ;;EAKnD,UAAU;IAAU;;UAWZ;;UAMA;;EAMR,WAAW,QAAQ,SAAS;IAAU,SAAS,QAAQ;;;EAQvD,WAAW,cAAc,cAAc,MAAM,OAAO,cAAc,SAAS;IAAU,SAAS,QAAQ;;;EAYtG,aAAa;IAAU,OAAO;;;;;;;;;EAW9B,QAAQ,QAAQ,MAAM,eAAe;IAAU,MAAM,KAAK;;;UAclD;;EAaR,QAAQ,cAAc,cAAc,MAAM,cAAc,UAAU,OAAO,iBAAiB;IAAU;IAAU;;;EAM9G,WAAW,eAAe;IAAU;;;EAQpC,SAAS,kBAAkB;IAAU,OAAO;;;EAQ5C,QAAQ,aAAa,gBAAgB,QAAQ;;EAK7C,SAAS,aAAa,kBAAa,QAAQ;;EAK3C,QAAQ,aAAa,gBAAgB,QAAQ;;EAK7C,YAAY,cAAc,cAAc,MAAM,kBAAkB,eAAe,OAAM,mBAAmB,SAAc,QAAQ;;EAU9H,eAAe,kBAAkB,iBAAa,QAAQ;;EAYtD,oBAAoB;IAAU,QAAQ;;;;;;KCxN5B,cAAc,KAAK;;KAGnB,cAAc;;KAGd;;UAGK;;WAEN;;;KAgBC,mBAAmB,KAAK;;cAGvB,mBAAmB;;EAE9B,cAAc;;EAKd,aAAa;;EAKP,MAAM,eAAe;;EAoB3B,aAAa,QAAQ;;EAKrB,mBAAmB,QAAQ;;EAK3B,aAAa;IAAU,OAAOC;;;EAK9B,gBAAgB,eAAU,QAAQ;;;;;EAQlC,aAAa;IAAU;;;EASvB,YAAY,cAAc,gBAAgB;IAAU;;;EAUpD,UAAU,mBAAmB,QAAQ;;EAQrC,gBAAgB,QAAQ;;EAKxB,gBAAgB,iBAAiB,QAAQ;;;;;EAQzC,MAAM,QAAQ,cAAc,QAAQ;;;;;;EAUpC,SAAS,OAAO,aAAa,OAAO,cAAc,QAAQ;;EAe1D,KAAK,OAAO,wBAAwB,gBAAgB,QAAQ;;EAK5D,IAAI,eAAe,OAAO,yBAAyB,QAAQ;;EAK3D,aAAa;IAAU;;;EAKvB,eAAe,QAAQ;;EAKvB,WAAW,OAAM,mBAAwB,QAAQ;;EAKjD,YAAY,yBAAyB,OAAO,aAAa,OAAO,aAAa,OAAM,mBAAwB,QAAQ;;EAKnH,YAAY,IAAI,UAAU,QAAQ;;EAKlC,aAAa,IAAI,UAAU,QAAQ;;EAKnC,YAAY,IAAI,UAAU,QAAQ;;EAKlC,aAAa,IAAI,SAAS,OAAO,aAAa,OAAO,aAAa,OAAM,mBAAwB,QAAQ;;;;QCvMlG;YACI;;IAER;;;;UAKa;;;;;;;;EAQf,WAAW;;EAEX;;EAEA;;WAES;;;;;KCZC;WACD;WACA;WACA;WACA;WACA;WACA;WACA,QAAQ;;;KAIP;WAAgC;WAA0B;;;KAG1D,gBAAgB,oBAAoB;;;;UC9B/B;;WAEN;;WAEA;;;UAIM,gBAAgB,eAAe;;WAErC,OAAO;WACP,UAAU;;WAEV;;WAEA;;;UAIM,UAAU;WAChB,OAAO;WACP;aAAqB;aAAwB;;WAC7C;WACA;;WAEA;;;UAIM;;;;;;WAMN;;;UAIM;;WAEN;WACA;;WAEA;;WAEA;WACA;aAAqB;aAAwB;;WAC7C;;WAEA;;WAEA;;;KAIC;;;;cCpCC;;cAGA;;UAaI;;WAEN;WACA,OAAO;;EAEhB;;EAEA;;EAEA,WAAW;;EAEX,KAAK,SAAS,gBAAgB;;EAE9B,aAAa,QAAQ;;EAErB,aAAa,QAAQ;;EAErB,WAAW,eAAe;;;cAyBf;mBACM;UACT;UACA;UACA;mBACS;EAEL,YAAA,MAAM;;;;;;MASd;;MAKA,aAAa;;;;;;;EAUjB,KAAK,eAAe,aAAa,KAAK,gBAAgB;;EA6BhD,SAAS;;EAaT,SAAS;;EAaT,IAAI,SAAS,aAAa,UAAS,oBAAyB,QAAQ,UAAU;;EAuB9E,KAAK,SAAS,aAAa,UAAS,oBAAyB,QAAQ;;EAKrE,QAAQ,QAAQ;;EAKhB,QAAQ,eAAe;;UAMrB;UASA;UAOA;;UAUA;;;;;KChOE;;;;;;;;;;;;cCgBC,YAAY;;WAEd;UAED;;;;;EAMR,cAAc,QAAQ;;EAKhB,iBAAiB,QAAQ;;;;;;MAS3B,SAAS;;MAUT,WAAW,kBAAkB;;MAK7B,QAAQ;;;;;;MASR,SAAS;;MAKT,mBAAmBC;;MAKnB,iBAAiBC;;MAKjB,aAAaC;;MAKb,gBAAgBC;;MAKhB,iBAAiBC;;MAKjB,eAAeC;;;;;;MASf,UAAU;;UAMN;;UAiCA;;;cAqBG,KAAK;;iBAMF,UAAU,UAAS,aAAkB;;;;;;;cCzKxC;;cAGA;;;;;;cAOA;;;;;;KAOD,eAAe,mBAAmB,4BAA4B,sBAAsB,8BAA8B;;UAG7G;;WAEN,OAAO;;WAEP,SAAS,yBAAyB;;WAElC;;WAEA;;WAEA;;;;;;;;cASE,iBAAiB;;WAEnB;;WAEA,MAAM;;WAEN;;WAEA;;WAEA;;WAEA;;EAGG,YAAA,iBAAiB,iBAAiB,UAAS;;MAYnD;;;cAMO,qBAAqB;;EAEpB,YAAA;;;;;;;cAWD,6BAA6B;;EAE5B,YAAA,iBAAiB,4BAA4B,SAAS;;;;;cCjGvD"}
@@ -60,6 +60,12 @@ function isJsonObject(candidate) {
60
60
  */
61
61
  const UNSUPPORTED_BY_HUB = "unsupported_by_hub";
62
62
  /**
63
+ * The code `getSave` rejects with when the hub can't be reached and this
64
+ * device has no copy of the slot it can trust (a fresh device or cleared
65
+ * browser): the slot may well hold a save, so it is not reported empty.
66
+ */
67
+ const SAVE_UNKNOWN_CODE = "save_unknown";
68
+ /**
63
69
  * Thrown by every hub call that fails. `status` is the HTTP status when the
64
70
  * hub answered (401 = not signed in, 404 = unknown game or board, 413 = too
65
71
  * large), or `undefined` for a transport failure (offline, no host). Social
@@ -555,23 +561,36 @@ function mergeStats(waiting, incoming) {
555
561
  case "set": return incoming;
556
562
  }
557
563
  }
558
- /** The progress kinds, by name. */
559
- function progressKinds(game) {
564
+ /**
565
+ * The progress kinds, by name. A save write replayed from the queue sends
566
+ * the hub's `updatedAt` this device last saw for the slot (`null` when it
567
+ * saw it empty or never saw it), so the hub refuses it (409 `save_conflict`,
568
+ * dropped and reported as a conflict) when another device saved since. A
569
+ * write sent at once, online, always lands, as before.
570
+ */
571
+ function progressKinds(game, cloudSaves) {
560
572
  return {
561
573
  [PROGRESS_KINDS.save]: checkedKind(readSave$1, {
562
574
  mergeKey: (save) => save.slot,
563
575
  replaces: true,
564
576
  run: async (save, context) => {
565
- if ("deleted" in save) await context.call("deleteSave", {
566
- ...game(),
567
- slot: save.slot
568
- });
569
- else await context.call("writeSave", {
577
+ if ("deleted" in save) {
578
+ await context.call("deleteSave", {
579
+ ...game(),
580
+ slot: save.slot
581
+ });
582
+ cloudSaves.rememberCloudSave(save.slot, null);
583
+ return;
584
+ }
585
+ const base = context.isReplay ? { base: cloudSaves.cloudSave(save.slot) ?? null } : {};
586
+ const written = await context.call("writeSave", {
570
587
  ...game(),
571
588
  slot: save.slot,
572
589
  data: save.data,
573
- label: save.label
590
+ label: save.label,
591
+ ...base
574
592
  });
593
+ cloudSaves.rememberCloudSave(save.slot, written.updatedAt);
575
594
  }
576
595
  }),
577
596
  [PROGRESS_KINDS.stat]: checkedKind(readStat, {
@@ -774,13 +793,68 @@ function legacyOperation(op) {
774
793
  /** Register every hub kind on `outbox`. */
775
794
  function registerHubKinds(outbox, hooks) {
776
795
  const kinds = {
777
- ...progressKinds(hooks.game),
796
+ ...progressKinds(hooks.game, hooks.cloudSaves),
778
797
  ...dailyKinds(hooks.game, hooks.onDailyComplete),
779
798
  ...economyKinds(hooks.game, writeLocalFavorites),
780
799
  ...resumeKinds(hooks.game, hooks.resume)
781
800
  };
782
801
  for (const [kind, definition] of Object.entries(kinds)) outbox.define(kind, definition);
783
802
  }
803
+ //#endregion
804
+ //#region src/core/offline/cloud-saves.ts
805
+ /**
806
+ * What this device last saw of a game's save slots on the hub, per player:
807
+ * each slot's `updatedAt` there, or `null` for a slot known to be empty. It
808
+ * decides two things offline. `getSave` answers from the cache only when
809
+ * the device knows the hub's copy of that slot (so a fresh device or cleared
810
+ * browser never reports "no save" for a slot it simply hasn't seen), and a
811
+ * queued save write sends the hub's `updatedAt` it was built on, so the hub
812
+ * refuses it when the slot has changed since.
813
+ *
814
+ * A snapshot gives the full list (`complete`: any other slot is empty); a
815
+ * single read or write adds that slot to a partial list.
816
+ */
817
+ /** A stored index, or `null` when missing or malformed. */
818
+ function readCloudSaveIndex(stored) {
819
+ if (!isRecord(stored) || typeof stored.complete !== "boolean" || !isRecord(stored.saves)) return null;
820
+ const userId = optionalNumber(stored.userId);
821
+ if (userId === void 0) return null;
822
+ const saves = {};
823
+ for (const [slot, updatedAt] of Object.entries(stored.saves)) {
824
+ const known = updatedAt === null ? null : optionalNumber(updatedAt);
825
+ if (known === void 0) return null;
826
+ saves[slot] = known;
827
+ }
828
+ return {
829
+ userId,
830
+ complete: stored.complete,
831
+ saves
832
+ };
833
+ }
834
+ /**
835
+ * The hub's `updatedAt` for `slot` as `userId` last saw it: a number, `null`
836
+ * when the slot was empty, or `undefined` when this device doesn't know.
837
+ */
838
+ function cloudSaveOf(index, userId, slot) {
839
+ if (index?.userId !== userId) return;
840
+ const updatedAt = index.saves[slot];
841
+ return updatedAt === void 0 && index.complete ? null : updatedAt;
842
+ }
843
+ /** `index` with `slot` seen at `updatedAt` (`null`: empty) by `userId`; another player's index starts over. */
844
+ function withCloudSave(index, userId, slot, updatedAt) {
845
+ const base = index?.userId === userId ? index : {
846
+ userId,
847
+ complete: false,
848
+ saves: {}
849
+ };
850
+ return {
851
+ ...base,
852
+ saves: {
853
+ ...base.saves,
854
+ [slot]: updatedAt
855
+ }
856
+ };
857
+ }
784
858
  /** The well-formed events of a stored buffer. */
785
859
  function readEvents(stored) {
786
860
  if (!Array.isArray(stored)) return [];
@@ -994,6 +1068,28 @@ var LocalStore = class {
994
1068
  deleteSave(slot) {
995
1069
  this.store.remove(`${this.prefix}save:${slot}`);
996
1070
  }
1071
+ /**
1072
+ * The hub's `updatedAt` for `slot` as the current user last saw it (`null`:
1073
+ * the slot was empty), or `undefined` when this device doesn't know.
1074
+ */
1075
+ cloudSave(slot) {
1076
+ const user = this.getUser();
1077
+ return user === null ? void 0 : cloudSaveOf(this.readCloudSaves(this.prefix), user.id, slot);
1078
+ }
1079
+ /** Note the hub's copy of `slot` (just read or written) for the current user: its `updatedAt`, or `null` when empty. */
1080
+ rememberCloudSave(slot, updatedAt) {
1081
+ const user = this.getUser();
1082
+ if (user !== null) this.writeStored(`${this.prefix}cloud-saves`, withCloudSave(this.readCloudSaves(this.prefix), user.id, slot, updatedAt));
1083
+ }
1084
+ /** Cache a slot as read from the hub (forgetting it when the hub has none), and note the hub's copy. */
1085
+ cacheCloudSave(slot, save) {
1086
+ if (save === null) this.deleteSave(slot);
1087
+ else this.writeStored(`${this.prefix}save:${slot}`, save);
1088
+ this.rememberCloudSave(slot, save?.updatedAt ?? null);
1089
+ }
1090
+ readCloudSaves(gamePrefix) {
1091
+ return readCloudSaveIndex(this.readStored(`${gamePrefix}cloud-saves`));
1092
+ }
997
1093
  /** This game's cached stats. */
998
1094
  getStats() {
999
1095
  const stored = this.readStored(`${this.prefix}stats`);
@@ -1070,13 +1166,32 @@ var LocalStore = class {
1070
1166
  ts: Date.now()
1071
1167
  }));
1072
1168
  }
1073
- /** Fan a `/me/snapshot` reply out across every game's cache keys. */
1169
+ /**
1170
+ * Fan a `/me/snapshot` reply out across every game's cache keys. It lists
1171
+ * every save slot of every game the player has played, so each game's
1172
+ * slots on the hub become known (and this game's are all empty when it
1173
+ * isn't listed).
1174
+ */
1074
1175
  hydrate(snapshot) {
1075
1176
  if (snapshot.user !== null) this.writeStored(USER_KEY, snapshot.user);
1177
+ const userId = snapshot.user?.id;
1178
+ if (userId !== void 0 && !Object.keys(snapshot.games).some((slug) => `hub:${slug}:` === this.prefix)) this.writeStored(`${this.prefix}cloud-saves`, {
1179
+ userId,
1180
+ complete: true,
1181
+ saves: {}
1182
+ });
1076
1183
  for (const [slug, game] of Object.entries(snapshot.games)) {
1077
1184
  const gamePrefix = `hub:${slug}:`;
1078
1185
  if (game.profile !== null) this.writeStored(`${gamePrefix}profile`, game.profile);
1079
1186
  for (const save of game.saves) this.writeStored(`${gamePrefix}save:${save.slot}`, save);
1187
+ if (userId !== void 0) {
1188
+ const saves = Object.fromEntries(game.saves.map((save) => [save.slot, save.updatedAt]));
1189
+ this.writeStored(`${gamePrefix}cloud-saves`, {
1190
+ userId,
1191
+ complete: true,
1192
+ saves
1193
+ });
1194
+ }
1080
1195
  this.writeStored(`${gamePrefix}stats`, game.stats);
1081
1196
  this.writeStored(`${gamePrefix}inventory`, game.inventory);
1082
1197
  }
@@ -2612,7 +2727,8 @@ var HubConnection = class {
2612
2727
  meta: payload.meta
2613
2728
  });
2614
2729
  },
2615
- resume: this.resumeStore
2730
+ resume: this.resumeStore,
2731
+ cloudSaves: this.store
2616
2732
  });
2617
2733
  this.importLegacyQueue();
2618
2734
  if (typeof navigator !== "undefined" && "onLine" in navigator) this.isReachable = navigator.onLine;
@@ -2880,7 +2996,7 @@ var HubProgress = class extends HubConnection {
2880
2996
  * sign-in. Idempotent. Offline, a local guest is used.
2881
2997
  */
2882
2998
  ensureGuest() {
2883
- return this.withFallback(() => this.rest.call("ensureGuestSession"), () => {
2999
+ return this.withFallback(async () => this.signedIn(await this.rest.call("ensureGuestSession")), () => {
2884
3000
  const known = this.store.getUser();
2885
3001
  if (known !== null) return { user: known };
2886
3002
  const guest = {
@@ -2898,10 +3014,10 @@ var HubProgress = class extends HubConnection {
2898
3014
  * the name is taken; the player should `login` instead.
2899
3015
  */
2900
3016
  claim(username, password) {
2901
- return this.withFallback(() => this.rest.call("claimGuestAccount", {
3017
+ return this.withFallback(async () => this.signedIn(await this.rest.call("claimGuestAccount", {
2902
3018
  username,
2903
3019
  password
2904
- }), () => this.signInLocally({
3020
+ })), () => this.signInLocally({
2905
3021
  id: 0,
2906
3022
  username,
2907
3023
  guest: false
@@ -2909,27 +3025,31 @@ var HubProgress = class extends HubConnection {
2909
3025
  }
2910
3026
  /** Create an account and sign in. (Offline: a local user is stored.) */
2911
3027
  signup(username, password) {
2912
- return this.withFallback(() => this.rest.call("signUpWithPassword", {
3028
+ return this.withFallback(async () => this.signedIn(await this.rest.call("signUpWithPassword", {
2913
3029
  username,
2914
3030
  password
2915
- }), () => this.signInLocally({
3031
+ })), () => this.signInLocally({
2916
3032
  id: 0,
2917
3033
  username
2918
3034
  }));
2919
3035
  }
2920
3036
  /** Sign in. (Offline: a local user is stored so signed-in flows work.) */
2921
3037
  login(username, password) {
2922
- return this.withFallback(() => this.rest.call("logInWithPassword", {
3038
+ return this.withFallback(async () => this.signedIn(await this.rest.call("logInWithPassword", {
2923
3039
  username,
2924
3040
  password
2925
- }), () => this.signInLocally({
3041
+ })), () => this.signInLocally({
2926
3042
  id: 0,
2927
3043
  username
2928
3044
  }));
2929
3045
  }
2930
3046
  /** Sign out (ends the session shared by every game). */
2931
3047
  logout() {
2932
- return this.withFallback(() => this.rest.call("logOut"), () => {
3048
+ return this.withFallback(async () => {
3049
+ const reply = await this.rest.call("logOut");
3050
+ this.store.clearUser();
3051
+ return reply;
3052
+ }, () => {
2933
3053
  this.store.clearUser();
2934
3054
  return { ok: true };
2935
3055
  });
@@ -2938,6 +3058,11 @@ var HubProgress = class extends HubConnection {
2938
3058
  this.store.setUser(user);
2939
3059
  return { user };
2940
3060
  }
3061
+ /** Remember who the hub signed in, so the cache's notes about the hub's copies are read for the right player. */
3062
+ signedIn(reply) {
3063
+ this.store.setUser(reply.user);
3064
+ return reply;
3065
+ }
2941
3066
  /** This game's profile for the current user, or `profile: null`. `TData` is the game's own data type. */
2942
3067
  getProfile() {
2943
3068
  return this.withFallback(async () => ({ profile: typedProfile((await this.rest.call("getGameProfile", this.gameFields())).profile) }), () => ({ profile: typedProfile(this.store.getProfile()) }));
@@ -2960,12 +3085,29 @@ var HubProgress = class extends HubConnection {
2960
3085
  listSaves() {
2961
3086
  return this.withFallback(() => this.rest.call("listSaves", this.gameFields()), () => ({ saves: this.store.listSaves() }));
2962
3087
  }
2963
- /** One slot's contents, or `save: null` when the slot is empty. `TData` is the game's own save type. */
3088
+ /**
3089
+ * One slot's contents, or `save: null` when the slot is empty. `TData` is
3090
+ * the game's own save type. Offline it answers from the device's copy, but
3091
+ * only when the device knows the hub's copy of the slot; otherwise (a fresh
3092
+ * device or cleared browser) it rejects with a {@link HubError} whose
3093
+ * `code` is `save_unknown` (`isOffline` is `true`), never a false "empty".
3094
+ */
2964
3095
  getSave(slot) {
2965
- return this.withFallback(async () => ({ save: typedSave((await this.rest.call("getSave", {
2966
- ...this.gameFields(),
2967
- slot
2968
- })).save) }), () => ({ save: typedSave(this.store.getSave(slot)) }));
3096
+ return this.withFallback(async () => {
3097
+ const { save } = await this.rest.call("getSave", {
3098
+ ...this.gameFields(),
3099
+ slot
3100
+ });
3101
+ if (this.outbox.count() === 0) this.store.cacheCloudSave(slot, save);
3102
+ return { save: typedSave(save) };
3103
+ }, () => ({ save: typedSave(this.offlineSave(slot)) }));
3104
+ }
3105
+ /** The device's copy of `slot`, when it can be trusted offline (see {@link getSave}). */
3106
+ offlineSave(slot) {
3107
+ const isLocalOnly = this.store.getUser()?.id === 0;
3108
+ const hasQueuedWrite = this.outbox.pending().some((op) => op.kind === PROGRESS_KINDS.save && isRecord(op.payload) && op.payload.slot === slot);
3109
+ if (!isLocalOnly && !hasQueuedWrite && this.store.cloudSave(slot) === void 0) throw new HubError(`Hub: offline, and this device hasn't seen save "${slot}" yet.`, void 0, { code: SAVE_UNKNOWN_CODE });
3110
+ return this.store.getSave(slot);
2969
3111
  }
2970
3112
  /** Create or overwrite a save slot. `saveData` is any JSON up to 64 KB. */
2971
3113
  putSave(slot, saveData, label) {
@@ -3882,6 +4024,6 @@ function createHub(options = {}) {
3882
4024
  return new Hub(options);
3883
4025
  }
3884
4026
  //#endregion
3885
- export { RESUME_DEBOUNCE_MS as a, OUTBOX_FULL_CODE as c, OfflineApi as d, ContentRejectedError as f, UNSUPPORTED_BY_HUB as h, HubResume as i, OUTBOX_MAX_CHARS as l, OfflineError as m, createHub as n, RESUME_MAX_STATE_BYTES as o, HubError as p, hub as r, CLIENT_VERSION_HEADER as s, Hub as t, OUTBOX_MAX_ENTRIES as u };
4027
+ export { RESUME_DEBOUNCE_MS as a, OUTBOX_FULL_CODE as c, OfflineApi as d, ContentRejectedError as f, UNSUPPORTED_BY_HUB as g, SAVE_UNKNOWN_CODE as h, HubResume as i, OUTBOX_MAX_CHARS as l, OfflineError as m, createHub as n, RESUME_MAX_STATE_BYTES as o, HubError as p, hub as r, CLIENT_VERSION_HEADER as s, Hub as t, OUTBOX_MAX_ENTRIES as u };
3886
4028
 
3887
4029
  //# sourceMappingURL=src.js.map