Files
Genarrative/packages/shared/src/contracts/runtime.ts

382 lines
9.1 KiB
TypeScript

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'
| 'asset_generation_consume'
| 'asset_generation_refund'
| 'redeem_code_reward';
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 RedeemProfileRewardCodeRequest = {
code: string;
};
export type RedeemProfileRewardCodeResponse = {
walletBalance: number;
amountGranted: number;
ledgerEntry: ProfileWalletLedgerEntry;
};
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;
};