This commit is contained in:
368
packages/shared/src/contracts/runtime.ts
Normal file
368
packages/shared/src/contracts/runtime.ts
Normal file
@@ -0,0 +1,368 @@
|
||||
import type { JsonObject } from './common';
|
||||
|
||||
export const SAVE_SNAPSHOT_VERSION = 2;
|
||||
export const DEFAULT_MUSIC_VOLUME = 0.42;
|
||||
export const DEFAULT_PLATFORM_THEME = 'light';
|
||||
export const PLATFORM_THEMES = ['light', 'dark'] as const;
|
||||
export type PlatformTheme = (typeof PLATFORM_THEMES)[number];
|
||||
|
||||
export type SavedGameSnapshot<
|
||||
TGameState = unknown,
|
||||
TBottomTab extends string = string,
|
||||
TCurrentStory = unknown,
|
||||
> = {
|
||||
version: number;
|
||||
savedAt: string;
|
||||
gameState: TGameState;
|
||||
bottomTab: TBottomTab;
|
||||
currentStory: TCurrentStory | null;
|
||||
};
|
||||
|
||||
export type SavedGameSnapshotInput<
|
||||
TGameState = unknown,
|
||||
TBottomTab extends string = string,
|
||||
TCurrentStory = unknown,
|
||||
> = Omit<
|
||||
SavedGameSnapshot<TGameState, TBottomTab, TCurrentStory>,
|
||||
'savedAt' | 'version'
|
||||
> & {
|
||||
savedAt?: string;
|
||||
};
|
||||
|
||||
export type RuntimeSettings = {
|
||||
musicVolume: number;
|
||||
platformTheme: PlatformTheme;
|
||||
};
|
||||
|
||||
export type BasicOkResult = {
|
||||
ok: true;
|
||||
};
|
||||
|
||||
export type ProfileDashboardCardKey = 'wallet' | 'playTime' | 'playedWorks';
|
||||
|
||||
export type ProfileDashboardSummary = {
|
||||
walletBalance: number;
|
||||
totalPlayTimeMs: number;
|
||||
playedWorldCount: number;
|
||||
updatedAt: string | null;
|
||||
};
|
||||
|
||||
export type ProfileWalletLedgerEntry = {
|
||||
id: string;
|
||||
amountDelta: number;
|
||||
balanceAfter: number;
|
||||
sourceType:
|
||||
| 'snapshot_sync'
|
||||
| 'invite_inviter_reward'
|
||||
| 'invite_invitee_reward'
|
||||
| 'points_recharge';
|
||||
createdAt: string;
|
||||
};
|
||||
|
||||
export type ProfileWalletLedgerResponse = {
|
||||
entries: ProfileWalletLedgerEntry[];
|
||||
};
|
||||
|
||||
export type ProfileRechargeProductKind = 'points' | 'membership';
|
||||
export type ProfileMembershipStatus = 'normal' | 'active';
|
||||
export type ProfileMembershipTier = 'normal' | 'month' | 'season' | 'year';
|
||||
export type ProfileRechargeOrderStatus = 'paid';
|
||||
|
||||
export type ProfileRechargeProduct = {
|
||||
productId: string;
|
||||
title: string;
|
||||
priceCents: number;
|
||||
kind: ProfileRechargeProductKind;
|
||||
pointsAmount: number;
|
||||
bonusPoints: number;
|
||||
durationDays: number;
|
||||
badgeLabel: string;
|
||||
description: string;
|
||||
tier: ProfileMembershipTier;
|
||||
};
|
||||
|
||||
export type ProfileMembershipBenefit = {
|
||||
benefitName: string;
|
||||
normalValue: string;
|
||||
monthValue: string;
|
||||
seasonValue: string;
|
||||
yearValue: string;
|
||||
};
|
||||
|
||||
export type ProfileMembership = {
|
||||
status: ProfileMembershipStatus;
|
||||
tier: ProfileMembershipTier;
|
||||
startedAt: string | null;
|
||||
expiresAt: string | null;
|
||||
updatedAt: string | null;
|
||||
};
|
||||
|
||||
export type ProfileRechargeOrder = {
|
||||
orderId: string;
|
||||
productId: string;
|
||||
productTitle: string;
|
||||
kind: ProfileRechargeProductKind;
|
||||
amountCents: number;
|
||||
status: ProfileRechargeOrderStatus;
|
||||
paymentChannel: string;
|
||||
paidAt: string;
|
||||
createdAt: string;
|
||||
pointsDelta: number;
|
||||
membershipExpiresAt: string | null;
|
||||
};
|
||||
|
||||
export type ProfileRechargeCenterResponse = {
|
||||
walletBalance: number;
|
||||
membership: ProfileMembership;
|
||||
pointProducts: ProfileRechargeProduct[];
|
||||
membershipProducts: ProfileRechargeProduct[];
|
||||
benefits: ProfileMembershipBenefit[];
|
||||
latestOrder: ProfileRechargeOrder | null;
|
||||
hasPointsRecharged: boolean;
|
||||
};
|
||||
|
||||
export type CreateProfileRechargeOrderRequest = {
|
||||
productId: string;
|
||||
paymentChannel?: string;
|
||||
};
|
||||
|
||||
export type CreateProfileRechargeOrderResponse = {
|
||||
order: ProfileRechargeOrder;
|
||||
center: ProfileRechargeCenterResponse;
|
||||
};
|
||||
|
||||
export type ProfileReferralInviteCenterResponse = {
|
||||
inviteCode: string;
|
||||
inviteLinkPath: string;
|
||||
invitedCount: number;
|
||||
rewardedInviteCount: number;
|
||||
todayInviterRewardCount: number;
|
||||
todayInviterRewardRemaining: number;
|
||||
rewardPoints: number;
|
||||
hasRedeemedCode: boolean;
|
||||
boundInviterUserId: string | null;
|
||||
boundAt: string | null;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type RedeemProfileReferralInviteCodeRequest = {
|
||||
inviteCode: string;
|
||||
};
|
||||
|
||||
export type RedeemProfileReferralInviteCodeResponse = {
|
||||
center: ProfileReferralInviteCenterResponse;
|
||||
inviteeRewardGranted: boolean;
|
||||
inviterRewardGranted: boolean;
|
||||
inviteeBalanceAfter: number;
|
||||
inviterBalanceAfter: number;
|
||||
};
|
||||
|
||||
export type ProfilePlayedWorkSummary = {
|
||||
worldKey: string;
|
||||
ownerUserId: string | null;
|
||||
profileId: string | null;
|
||||
worldType: string | null;
|
||||
worldTitle: string;
|
||||
worldSubtitle: string;
|
||||
firstPlayedAt: string;
|
||||
lastPlayedAt: string;
|
||||
lastObservedPlayTimeMs: number;
|
||||
};
|
||||
|
||||
export type ProfilePlayStatsResponse = {
|
||||
totalPlayTimeMs: number;
|
||||
playedWorks: ProfilePlayedWorkSummary[];
|
||||
updatedAt: string | null;
|
||||
};
|
||||
|
||||
export type ProfileSaveArchiveSummary = {
|
||||
worldKey: string;
|
||||
ownerUserId: string | null;
|
||||
profileId: string | null;
|
||||
worldType: string | null;
|
||||
worldName: string;
|
||||
subtitle: string;
|
||||
summaryText: string;
|
||||
coverImageSrc: string | null;
|
||||
lastPlayedAt: string;
|
||||
};
|
||||
|
||||
export type ProfileSaveArchiveListResponse = {
|
||||
entries: ProfileSaveArchiveSummary[];
|
||||
};
|
||||
|
||||
export type ProfileSaveArchiveResumeResponse<
|
||||
TGameState = unknown,
|
||||
TBottomTab extends string = string,
|
||||
TCurrentStory = unknown,
|
||||
> = {
|
||||
entry: ProfileSaveArchiveSummary;
|
||||
snapshot: SavedGameSnapshot<TGameState, TBottomTab, TCurrentStory>;
|
||||
};
|
||||
|
||||
export type CustomWorldPublicationStatus = 'draft' | 'published';
|
||||
export type CustomWorldThemeMode =
|
||||
| 'martial'
|
||||
| 'arcane'
|
||||
| 'machina'
|
||||
| 'tide'
|
||||
| 'rift'
|
||||
| 'mythic';
|
||||
|
||||
export type CustomWorldProfileRecord = JsonObject & {
|
||||
id?: string;
|
||||
};
|
||||
|
||||
export type CustomWorldLibraryEntry<TProfile = CustomWorldProfileRecord> = {
|
||||
ownerUserId: string;
|
||||
profileId: string;
|
||||
publicWorkCode: string | null;
|
||||
authorPublicUserCode: string | null;
|
||||
profile: TProfile;
|
||||
visibility: CustomWorldPublicationStatus;
|
||||
publishedAt: string | null;
|
||||
updatedAt: string;
|
||||
authorDisplayName: string;
|
||||
worldName: string;
|
||||
subtitle: string;
|
||||
summaryText: string;
|
||||
coverImageSrc: string | null;
|
||||
themeMode: CustomWorldThemeMode;
|
||||
playableNpcCount: number;
|
||||
landmarkCount: number;
|
||||
};
|
||||
|
||||
export type CustomWorldGalleryCard = Omit<
|
||||
CustomWorldLibraryEntry<never>,
|
||||
'profile'
|
||||
>;
|
||||
|
||||
export type CustomWorldLibraryResponse<TProfile = CustomWorldProfileRecord> = {
|
||||
entries: CustomWorldLibraryEntry<TProfile>[];
|
||||
};
|
||||
|
||||
export type CustomWorldLibraryMutationResponse<
|
||||
TProfile = CustomWorldProfileRecord,
|
||||
> = {
|
||||
entry: CustomWorldLibraryEntry<TProfile>;
|
||||
entries: CustomWorldLibraryEntry<TProfile>[];
|
||||
};
|
||||
|
||||
export type CustomWorldGalleryResponse = {
|
||||
entries: CustomWorldGalleryCard[];
|
||||
};
|
||||
|
||||
export type CustomWorldGalleryDetailResponse<
|
||||
TProfile = CustomWorldProfileRecord,
|
||||
> = {
|
||||
entry: CustomWorldLibraryEntry<TProfile>;
|
||||
};
|
||||
|
||||
export type PlatformBrowseHistoryEntry = {
|
||||
ownerUserId: string;
|
||||
profileId: string;
|
||||
worldName: string;
|
||||
subtitle: string;
|
||||
summaryText: string;
|
||||
coverImageSrc: string | null;
|
||||
themeMode: CustomWorldThemeMode;
|
||||
authorDisplayName: string;
|
||||
visitedAt: string;
|
||||
};
|
||||
|
||||
export type PlatformBrowseHistoryWriteEntry = Omit<
|
||||
PlatformBrowseHistoryEntry,
|
||||
'visitedAt'
|
||||
> & {
|
||||
visitedAt?: string;
|
||||
};
|
||||
|
||||
export type PlatformBrowseHistoryResponse = {
|
||||
entries: PlatformBrowseHistoryEntry[];
|
||||
};
|
||||
|
||||
export type PlatformBrowseHistoryBatchSyncRequest = {
|
||||
entries: PlatformBrowseHistoryWriteEntry[];
|
||||
};
|
||||
|
||||
export const CUSTOM_WORLD_GENERATION_MODES = ['fast', 'full'] as const;
|
||||
export type CustomWorldGenerationMode =
|
||||
(typeof CUSTOM_WORLD_GENERATION_MODES)[number];
|
||||
|
||||
export const CUSTOM_WORLD_SESSION_STATUSES = [
|
||||
'clarifying',
|
||||
'ready_to_generate',
|
||||
'generating',
|
||||
'completed',
|
||||
'generation_error',
|
||||
] as const;
|
||||
export type CustomWorldSessionStatus =
|
||||
(typeof CUSTOM_WORLD_SESSION_STATUSES)[number];
|
||||
|
||||
export type CustomWorldQuestion = {
|
||||
id: string;
|
||||
label: string;
|
||||
question: string;
|
||||
answer?: string;
|
||||
};
|
||||
|
||||
export type CustomWorldGenerationStep = {
|
||||
id: string;
|
||||
label: string;
|
||||
detail: string;
|
||||
completed: number;
|
||||
total: number;
|
||||
status: 'pending' | 'active' | 'completed';
|
||||
};
|
||||
|
||||
export type CustomWorldGenerationProgress = {
|
||||
phaseId: string;
|
||||
phaseLabel: string;
|
||||
phaseDetail: string;
|
||||
batchLabel?: string;
|
||||
overallProgress: number;
|
||||
completedWeight: number;
|
||||
totalWeight: number;
|
||||
elapsedMs: number;
|
||||
estimatedRemainingMs: number | null;
|
||||
activeStepIndex: number;
|
||||
steps: CustomWorldGenerationStep[];
|
||||
};
|
||||
|
||||
export type GenerateCustomWorldProfileOptions = {
|
||||
onProgress?: (progress: CustomWorldGenerationProgress) => void;
|
||||
signal?: AbortSignal;
|
||||
};
|
||||
|
||||
export type GenerateCustomWorldProfileInput = {
|
||||
settingText: string;
|
||||
creatorIntent?: JsonObject | null;
|
||||
generationMode?: CustomWorldGenerationMode;
|
||||
};
|
||||
|
||||
export type CreateCustomWorldSessionRequest = {
|
||||
settingText: string;
|
||||
creatorIntent?: JsonObject | null;
|
||||
generationMode: CustomWorldGenerationMode;
|
||||
};
|
||||
|
||||
export type AnswerCustomWorldSessionQuestionRequest = {
|
||||
questionId: string;
|
||||
answer: string;
|
||||
};
|
||||
|
||||
export type CustomWorldSessionSummary = {
|
||||
sessionId: string;
|
||||
status: CustomWorldSessionStatus;
|
||||
questions: CustomWorldQuestion[];
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
export type CustomWorldSessionRecord = CustomWorldSessionSummary & {
|
||||
settingText: string;
|
||||
creatorIntent?: JsonObject | null;
|
||||
generationMode: CustomWorldGenerationMode;
|
||||
result?: JsonObject;
|
||||
lastError?: string;
|
||||
};
|
||||
Reference in New Issue
Block a user