Nuxt Recipe
The SDK is framework-agnostic. Keep Vue-specific glue in the consuming app so the SDK stays testable and portable.
Singleton client
// composables/useSimulatorClient.ts
import { createSimulatorClient } from '@my-swu/simulator-client'
import type { SimulatorClient } from '@my-swu/simulator-client'
let simulatorClient: SimulatorClient | undefined
export function useSimulatorClient(): SimulatorClient {
if (simulatorClient == null) {
const config = useRuntimeConfig()
// Keep one client instance per app runtime.
simulatorClient = createSimulatorClient({ baseUrl: config.public.simulatorBaseUrl })
}
return simulatorClient
}Expose simulatorBaseUrl via nuxt.config.ts:
export default defineNuxtConfig({
runtimeConfig: {
public: {
// Public because browser clients need this origin for HTTP and sockets.
simulatorBaseUrl: 'http://127.0.0.1:4000',
},
},
})Reactive match state
// composables/useMatch.ts
import type { GameEvent, GameState, MatchSession } from '@my-swu/simulator-client'
export function useMatch() {
const simulatorClient = useSimulatorClient()
// Store authoritative state and append-only event log separately.
const matchSnapshot = ref<GameState | null>(null)
const gameEvents = ref<GameEvent[]>([])
let matchSession: MatchSession | undefined
async function connect(matchId: string, seatToken: string) {
// Seat token is private; pass it only to the socket-owning client.
matchSession = simulatorClient.session.create({ matchId, seatToken })
await matchSession.connect()
matchSnapshot.value = matchSession.snapshot ?? null
matchSession.on('snapshot', nextMatchSnapshot => {
// Replace state on each snapshot.
matchSnapshot.value = nextMatchSnapshot
})
matchSession.on('event', gameEvent => {
// Events drive timeline UI and animations.
gameEvents.value.push(gameEvent)
})
}
// Close sockets when the composable owner unmounts.
onBeforeUnmount(() => matchSession?.close())
return { connect, session: () => matchSession, events: gameEvents, snapshot: matchSnapshot }
}SSR safety
The SDK uses fetch and WebSocket globals. In Nuxt server contexts:
fetchis available natively.WebSocketis not. GuardmatchSession.connect()behind a client-only boundary, for example insideonMountedor a<ClientOnly>wrapper.
Developing against a linked SDK
The first-party consumer, the swu app at ../swu, depends on this package as "@my-swu/simulator-client": "link:../swu-simulator/packages/ts-sdk" rather than a published version. The link resolves through the package exports map, which points at dist/, so Nuxt loads the built SDK, not its TypeScript source.
The practical consequence: editing SDK source changes nothing in the Nuxt app until you rebuild. Run pnpm sdk:build in the simulator repo (or pnpm --filter @my-swu/simulator-client run build when only TypeScript changed), then let Nuxt pick the new dist/ up.
See Getting Started for the same note alongside the rest of the setup.
