@fzgame/sdk 0.1.0 → 0.3.1
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/README.md +110 -0
- package/dist/index.d.mts +412 -12
- package/dist/index.d.ts +412 -12
- package/dist/index.js +29 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +27 -1
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +2 -0
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +2 -0
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,6 +80,109 @@ yourself.
|
|
|
80
80
|
A timeout is combined with any `AbortSignal` you pass to a request, so your own cancellation keeps
|
|
81
81
|
working.
|
|
82
82
|
|
|
83
|
+
## Integrate a game into your app
|
|
84
|
+
|
|
85
|
+
The path from a configured client to a resumed play session. This code runs on your server; the
|
|
86
|
+
browser receives a play URL and never sees the app secret.
|
|
87
|
+
|
|
88
|
+
| Step | Call | Returns |
|
|
89
|
+
| --- | --- | --- |
|
|
90
|
+
| List the games published in the app | `GET /sdk/game-instances` | instances, each with its `game` |
|
|
91
|
+
| Identify or create the player | `POST /sdk/login` | `{ player, playerAccessToken }` |
|
|
92
|
+
| Read the player's live session, for a continue screen | `GET /sdk/sessions/active` | `{ session }`, the newest live session or `null` |
|
|
93
|
+
| Build the play URL | `resolveGameURL(...)` | `{resolvedUrl}?apiUrl=…&gameInstanceId=…&playerAccessToken=…` |
|
|
94
|
+
| Game connects, play starts or resumes | `POST /games/runtime/sessions` | the session, used by the kit |
|
|
95
|
+
| Game saves progress | `PUT /games/runtime/sessions/{token}/snapshot` | the session |
|
|
96
|
+
|
|
97
|
+
### 1. List the games
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const { data, error } = await fetchClient.GET('/sdk/game-instances', {
|
|
101
|
+
params: { query: { page: 1, limit: 20 } },
|
|
102
|
+
})
|
|
103
|
+
const instances = data?.data ?? []
|
|
104
|
+
const instance = instances.find((i) => i.game?.slug === 'flabby-bird')
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The response is paginated: `data.data` holds the instances and `data.pageInfo` holds `page`,
|
|
108
|
+
`total`, `hasNextPage`, and related counts. Only instances in `PUBLISHED` status for the client's app
|
|
109
|
+
come back. Each item carries `id`, `resolvedUrl`, `overrides`, `status`, and a `game` relation.
|
|
110
|
+
`game.configSchema` is JSON; read `viewMode` out of it (`'landscape'` or `'portrait'`) to tell the two
|
|
111
|
+
apart.
|
|
112
|
+
|
|
113
|
+
### 2. Identify the player
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
const { data: login } = await fetchClient.POST('/sdk/login', {
|
|
117
|
+
body: { identity: 'user-42', displayName: 'Ada' },
|
|
118
|
+
})
|
|
119
|
+
const player = login?.player
|
|
120
|
+
const playerAccessToken = login?.playerAccessToken
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
`identity` is your own user id. `AppPlayer` is unique per app on that value, so sending it on every
|
|
124
|
+
call returns the same player. `playerAccessToken` is a signed JWT for that player, bound to your app
|
|
125
|
+
and expiring with the play-session window; pass it to the game, never the app secret.
|
|
126
|
+
|
|
127
|
+
### 3. Build the play URL
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
const url = resolveGameURL(instance, login.playerAccessToken)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
`resolveGameURL` starts from `instance.resolvedUrl` and appends `apiUrl` (the one passed to
|
|
134
|
+
`initializeSDK`), `gameInstanceId`, `playerAccessToken`, and the instance `overrides` as `gameSetting`.
|
|
135
|
+
`apiUrl` must be reachable from the browser, because the frame calls the public instance and runtime
|
|
136
|
+
endpoints itself. A `createSdkClient` caller, or one that needs a different frame origin, passes it in
|
|
137
|
+
the optional third argument: `resolveGameURL(instance, token, { apiUrl, config })`, where `config`
|
|
138
|
+
replaces the instance overrides. The kit reads the token when the game calls `createKit()`; nothing
|
|
139
|
+
has to be sent into the frame first.
|
|
140
|
+
|
|
141
|
+
The token is a bearer credential: anyone holding it can play as that player, so keep it out of logs
|
|
142
|
+
and analytics. A host that holds a `playSessionToken` already can also post the kit's `session`
|
|
143
|
+
message after the frame loads (see the kit README).
|
|
144
|
+
|
|
145
|
+
### 4. Landscape games
|
|
146
|
+
|
|
147
|
+
On a phone, present the frame rotated when `viewMode` is `'landscape'`. The API reports the value
|
|
148
|
+
only; rotation belongs to the host. The portal's `GameViewport` rotates the iframe with a CSS
|
|
149
|
+
transform on coarse-pointer devices, and the SDK plays no part in it.
|
|
150
|
+
|
|
151
|
+
### 5. Play and resume
|
|
152
|
+
|
|
153
|
+
The game calls `POST /games/runtime/sessions` with `{ gameInstanceId, playerAccessToken }`. The
|
|
154
|
+
server verifies the JWT with the same key as `BETTER_AUTH_SECRET`, resolves the player it names, and
|
|
155
|
+
creates a session or returns the player's newest `CREATED`, `READY`, `PLAYING`, or `PAUSED` session
|
|
156
|
+
for the instance, so a returning player lands back in the same round with `kit.snapshot.state`
|
|
157
|
+
restored from the session's snapshot. The host does not create the session and does not look it up.
|
|
158
|
+
|
|
159
|
+
A host that shows its own "continue" or progress screen reads the same session first:
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
const { data } = await fetchClient.GET('/sdk/sessions/active', {
|
|
163
|
+
params: { query: { playerId: player.id, gameInstanceId: instance.id } },
|
|
164
|
+
})
|
|
165
|
+
// data.session is the newest live session, or null
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
`playerId` is the `AppPlayer.id` (the row id) and `playerAccessToken` is the JWT login returned. Pass
|
|
169
|
+
`gameInstanceId` to scope the answer to one build. A live session carries `token`, `status`,
|
|
170
|
+
`metadata.snapshot`, `score`, and `result`, so a host can show "your last round" before the frame
|
|
171
|
+
loads. The numbers and state the game reports stay authoritative: the host reads, the game writes.
|
|
172
|
+
|
|
173
|
+
A session ends on `finish` or `abort`, and a terminal session is not resumed. Closing the frame
|
|
174
|
+
leaves it resumable. Sessions expire after 24 hours, and `POST /sdk/players/{id}/logout` expires
|
|
175
|
+
every live session of a player.
|
|
176
|
+
|
|
177
|
+
### What this package does not cover
|
|
178
|
+
|
|
179
|
+
- The frame side of the flow. `postMessage`, the snapshot, and the lifecycle events belong to
|
|
180
|
+
`@fzgame/kit`; this package calls the HTTP API.
|
|
181
|
+
- Landscape presentation. The API reports `viewMode` inside `game.configSchema`; rotating the frame
|
|
182
|
+
is the host's job, and the portal's `GameViewport` is the reference.
|
|
183
|
+
- `POST /sdk/sessions` creates a session from the host when you need the token up front. The session
|
|
184
|
+
events are `/sdk/sessions/{token}/start|pause|resume|score|finish|abort`.
|
|
185
|
+
|
|
83
186
|
## Errors
|
|
84
187
|
|
|
85
188
|
| Error | Thrown when |
|
|
@@ -99,10 +202,14 @@ status and the parsed body.
|
|
|
99
202
|
document separately:
|
|
100
203
|
|
|
101
204
|
```ts
|
|
205
|
+
import { resolveGameURL, type GameInstance } from '@fzgame/sdk'
|
|
102
206
|
import type { components, paths } from '@fzgame/sdk'
|
|
103
207
|
|
|
104
208
|
type App = components['schemas']['App']
|
|
105
209
|
type GameInstanceId = paths['/sdk/game-instances/{id}']['get']['parameters']['path']['id']
|
|
210
|
+
|
|
211
|
+
// GameInstance is components['schemas']['AppGameInstance']: an item from GET /sdk/game-instances.
|
|
212
|
+
const playUrl = resolveGameURL(gameInstance, playerAccessToken)
|
|
106
213
|
```
|
|
107
214
|
|
|
108
215
|
`src/types/schema.d.ts` is generated from the server document that the workspace build writes to
|
|
@@ -115,6 +222,9 @@ pnpm --filter @fzgame/sdk compile
|
|
|
115
222
|
That path is inside this monorepo, so the script runs for contributors here. Consumers of the
|
|
116
223
|
published package get the types that were generated at release time.
|
|
117
224
|
|
|
225
|
+
A route without a declared success response types its body as empty. When you add a controller
|
|
226
|
+
response, annotate it with `@ApiResponse` so the regenerated types carry it.
|
|
227
|
+
|
|
118
228
|
## Entry points
|
|
119
229
|
|
|
120
230
|
| Specifier | Contents |
|