Skip to content

@ludus/developer-portal

The developer portal SDK. Provides React hooks, dashboard components, and Firestore converters for the Ludus game developer experience — submission management, analytics, revenue tracking, payout requests, and version history.

Terminal window
npm install @ludus/developer-portal

The portal uses a provider pattern: all hooks accept a provider interface as their first argument, keeping the portal transport-agnostic. Swap providers to switch between Firestore, REST APIs, or test mocks without changing component code.

Five provider interfaces define the data layer:

import type {
SubmissionProvider,
RevenueProvider,
AnalyticsProvider,
VersionProvider,
ProfileProvider,
} from '@ludus/developer-portal';
interface SubmissionProvider {
listByDeveloper(developerId: string): Promise<PortalSubmission[]>;
getSubmission(submissionId: string): Promise<PortalSubmission>;
submitForReview(submissionId: string): Promise<void>;
}
interface RevenueProvider {
getSummary(developerId: string): Promise<RevenueSummary>;
getPayouts(developerId: string): Promise<PortalPayout[]>;
requestPayout(developerId: string): Promise<PortalPayout>;
getRevenueTimeSeries(developerId: string, period: TimePeriod): Promise<TimeSeriesPoint[]>;
}
interface AnalyticsProvider {
getGameAnalytics(gameId: string, period: TimePeriod): Promise<GameAnalytics>;
getDeveloperOverview(developerId: string): Promise<DeveloperOverview>;
}
interface VersionProvider {
getVersions(gameName: string): Promise<PortalVersionEntry[]>;
getLatest(gameName: string): Promise<PortalVersionEntry | null>;
}
interface ProfileProvider {
getProfile(developerId: string): Promise<DeveloperProfile>;
updateProfile(
developerId: string,
update: Partial<Omit<DeveloperProfile, 'id' | 'joinedAt' | 'verified'>>,
): Promise<DeveloperProfile>;
}

Six React hooks for data fetching and mutations:

import { useSubmissions } from '@ludus/developer-portal';
import type { UseSubmissionsResult } from '@ludus/developer-portal';
function MySubmissions({ provider, developerId }) {
const { submissions, loading, error, refresh } = useSubmissions(provider, developerId);
return submissions.map(s => (
<div key={s.id}>{s.gameName} v{s.version} — {s.status}</div>
));
}
import { useGameAnalytics } from '@ludus/developer-portal';
import type { UseGameAnalyticsResult } from '@ludus/developer-portal';
const { analytics, loading, error } = useGameAnalytics(provider, gameId, 'week');
// analytics: { gamesPlayed, uniquePlayers, avgGameDurationMs, completionRate,
// abandonmentRate, avgRating, ratingCount, retention, revenue }
import { useRevenue } from '@ludus/developer-portal';
import type { UseRevenueResult } from '@ludus/developer-portal';
const { summary, timeSeries, loading, error } = useRevenue(provider, developerId, 'month');
// summary: { totalFees, developerShare, protocolShare, pendingPayout, currency }
// timeSeries: [{ date, value }, ...]
import { usePayouts } from '@ludus/developer-portal';
import type { UsePayoutsResult } from '@ludus/developer-portal';
const { payouts, pendingAmount, requestPayout, requesting, loading, error } = usePayouts(provider, developerId);
// Request a new payout
await requestPayout();
import { useVersions } from '@ludus/developer-portal';
import type { UseVersionsResult } from '@ludus/developer-portal';
const { versions, latest, loading, error } = useVersions(provider, gameName);
// versions: [{ version, manifestHash, registeredAt, deprecated, isLatest }, ...]
import { useDeveloperProfile } from '@ludus/developer-portal';
import type { UseDeveloperProfileResult } from '@ludus/developer-portal';
const { profile, updateProfile, updating, loading, error } = useDeveloperProfile(provider, developerId);
// Update profile fields
await updateProfile({ displayName: 'New Name', bio: 'Updated bio' });

Thirteen React components for building the developer dashboard:

Top-level dashboard layout combining stats, activity feed, and quick actions:

import { DeveloperDashboard } from '@ludus/developer-portal';
import type { DeveloperDashboardProps } from '@ludus/developer-portal';
<DeveloperDashboard
submissionProvider={submissionProvider}
revenueProvider={revenueProvider}
analyticsProvider={analyticsProvider}
developerId="dev-123"
/>

Displays a single metric with label, value, and optional trend indicator:

import { StatCard } from '@ludus/developer-portal';
<StatCard label="Total Revenue" value="$12,450" trend="+8%" />

Time series chart of revenue data:

import { RevenueChart } from '@ludus/developer-portal';
<RevenueChart timeSeries={timeSeries} period="month" />

Visualizes D1/D7/D30 player retention metrics:

import { RetentionChart } from '@ludus/developer-portal';
<RetentionChart retention={{ d1: 0.45, d7: 0.28, d30: 0.12 }} />
import { SubmissionList, SubmissionDetail, QualityGateReport } from '@ludus/developer-portal';
// List all submissions
<SubmissionList submissions={submissions} onSelect={handleSelect} />
// Single submission detail with quality gate results
<SubmissionDetail submission={submission} />
// Quality gate breakdown
<QualityGateReport report={submission.qualityGateReport} />
import { GameCard, GameAnalyticsPanel } from '@ludus/developer-portal';
<GameCard game={game} onClick={handleClick} />
<GameAnalyticsPanel analytics={analytics} period="week" />
import { PayoutHistory, PayoutRequestForm } from '@ludus/developer-portal';
<PayoutHistory payouts={payouts} />
<PayoutRequestForm pendingAmount={pendingAmount} onRequest={requestPayout} requesting={requesting} />
import { VersionTimeline, DeveloperProfile } from '@ludus/developer-portal';
<VersionTimeline versions={versions} />
<DeveloperProfile profile={profile} onUpdate={updateProfile} />
import { initFirebase, getDb, setDb, resetFirebase } from '@ludus/developer-portal';
import type { FirebaseConfig, EmulatorConfig } from '@ludus/developer-portal';
initFirebase({
apiKey: '...',
projectId: 'ludus-dev',
// ...
});
const db = getDb();

Firestore document converters for type-safe reads and writes:

import {
developerProfileConverter,
portalSubmissionConverter,
portalPayoutConverter,
gameAnalyticsConverter,
portalVersionEntryConverter,
} from '@ludus/developer-portal';
// Usage with Firestore
const ref = doc(db, 'developers', devId).withConverter(developerProfileConverter);
import type {
// Time
TimePeriod, // 'day' | 'week' | 'month'
TimeSeriesPoint, // { date, value }
// Profile
DeveloperProfileData, // { id, displayName, bio?, walletAddress, totalGamesPublished, ... }
// Submissions
PortalSubmission, // { id, gameTypeId, gameName, version, status, qualityGateReport, ... }
GateResult, // { name, passed, score, details, executionTimeMs }
QualityGateReportData, // { gates, overallPassed, executionTimeMs }
// Revenue
RevenueSummary, // { totalFees, developerShare, protocolShare, pendingPayout, currency }
PortalPayout, // { id, amount, protocolAmount, currency, status, txHash?, ... }
// Analytics
GameAnalytics, // { gameId, gamesPlayed, uniquePlayers, retention, revenue, ... }
RetentionMetrics, // { d1, d7, d30 }
ActivityItem, // { id, type, message, timestamp, gameId? }
DeveloperOverview, // { totalGamesPublished, totalRevenue, totalPlayers, pendingPayout, recentActivity }
// Versions
PortalVersionEntry, // { version, manifestHash, registeredAt, deprecated, isLatest }
} from '@ludus/developer-portal';
// Re-exported from @ludus/game-protocol
import type { SubmissionStatus, PayoutStatus, GameCategory } from '@ludus/developer-portal';