@genex-ai/embed-sdk 0.3.1 → 0.4.0
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/dist/{chunk-S7JLPCNE.js → chunk-6E5DPEVG.js} +162 -5
- package/dist/index.d.ts +110 -1
- package/dist/index.js +13 -1
- package/dist/sentry.js +1 -1
- package/package.json +11 -10
|
@@ -22,6 +22,9 @@ var authWaiters = [];
|
|
|
22
22
|
var playerWaiters = [];
|
|
23
23
|
var overlayEl = null;
|
|
24
24
|
var popoverEl = null;
|
|
25
|
+
var pendingPlayerSave;
|
|
26
|
+
var hasPendingPlayerSave = false;
|
|
27
|
+
var pendingScores = /* @__PURE__ */ new Map();
|
|
25
28
|
function win() {
|
|
26
29
|
return globalThis.window;
|
|
27
30
|
}
|
|
@@ -129,6 +132,140 @@ function on(event, cb) {
|
|
|
129
132
|
set.delete(cb);
|
|
130
133
|
};
|
|
131
134
|
}
|
|
135
|
+
var KEEPALIVE_MAX_BYTES = 32 * 1024;
|
|
136
|
+
async function ensurePlayer() {
|
|
137
|
+
if (!config) throw new Error("genex embed: call initEmbed() first");
|
|
138
|
+
if (state === "pending") await waitForPlayer();
|
|
139
|
+
if (state === "blocked") throw new Error("genex embed auth: blocked");
|
|
140
|
+
}
|
|
141
|
+
function stateFetch(path, init = {}) {
|
|
142
|
+
const headers = { Authorization: `Bearer ${embedToken}` };
|
|
143
|
+
if (init.body !== void 0) headers["Content-Type"] = "application/json";
|
|
144
|
+
if (init.ifVersion !== void 0) headers["If-Match"] = String(init.ifVersion);
|
|
145
|
+
return doFetch(`${config.apiUrl}/api/projects/${encodeURIComponent(config.slug)}${path}`, {
|
|
146
|
+
method: init.method ?? "GET",
|
|
147
|
+
headers,
|
|
148
|
+
body: init.body,
|
|
149
|
+
keepalive: init.body !== void 0 && init.body.length <= KEEPALIVE_MAX_BYTES ? true : void 0
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function decodeSave(res) {
|
|
153
|
+
if (res.status === 409) {
|
|
154
|
+
const body2 = await res.json().catch(() => ({}));
|
|
155
|
+
return { saved: false, conflict: true, version: body2.version };
|
|
156
|
+
}
|
|
157
|
+
if (!res.ok) {
|
|
158
|
+
emit("error", { error: new Error(`genex state save failed (${res.status})`) });
|
|
159
|
+
return { saved: false };
|
|
160
|
+
}
|
|
161
|
+
const body = await res.json();
|
|
162
|
+
return { saved: true, version: body.version };
|
|
163
|
+
}
|
|
164
|
+
async function loadPlayerState() {
|
|
165
|
+
await ensurePlayer();
|
|
166
|
+
if (state === "guest") {
|
|
167
|
+
return { data: hasPendingPlayerSave ? pendingPlayerSave : null, version: 0, guest: true };
|
|
168
|
+
}
|
|
169
|
+
const res = await stateFetch("/state/me");
|
|
170
|
+
if (!res.ok) throw new Error(`genex load player state failed (${res.status})`);
|
|
171
|
+
const body = await res.json();
|
|
172
|
+
return { data: body.data, version: body.version };
|
|
173
|
+
}
|
|
174
|
+
async function savePlayerState(data, opts) {
|
|
175
|
+
await ensurePlayer();
|
|
176
|
+
if (state === "guest") {
|
|
177
|
+
pendingPlayerSave = data;
|
|
178
|
+
hasPendingPlayerSave = true;
|
|
179
|
+
return { saved: false, guest: true, queued: true };
|
|
180
|
+
}
|
|
181
|
+
return decodeSave(
|
|
182
|
+
await stateFetch("/state/me", {
|
|
183
|
+
method: "PUT",
|
|
184
|
+
body: JSON.stringify(data),
|
|
185
|
+
ifVersion: opts?.ifVersion
|
|
186
|
+
})
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
async function loadWorldState() {
|
|
190
|
+
await ensurePlayer();
|
|
191
|
+
if (state === "guest") return { data: null, version: 0, guest: true };
|
|
192
|
+
const res = await stateFetch("/state");
|
|
193
|
+
if (!res.ok) throw new Error(`genex load world state failed (${res.status})`);
|
|
194
|
+
const body = await res.json();
|
|
195
|
+
return { data: body.data, version: body.version };
|
|
196
|
+
}
|
|
197
|
+
async function saveWorldState(data, opts) {
|
|
198
|
+
await ensurePlayer();
|
|
199
|
+
if (state === "guest") return { saved: false, guest: true };
|
|
200
|
+
return decodeSave(
|
|
201
|
+
await stateFetch("/state", {
|
|
202
|
+
method: "PUT",
|
|
203
|
+
body: JSON.stringify(data),
|
|
204
|
+
ifVersion: opts?.ifVersion
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
async function submitScore(score, opts) {
|
|
209
|
+
const board = opts?.board ?? "main";
|
|
210
|
+
const mode = opts?.mode ?? "max";
|
|
211
|
+
await ensurePlayer();
|
|
212
|
+
if (state === "guest") {
|
|
213
|
+
const prev = pendingScores.get(board);
|
|
214
|
+
if (!prev || (mode === "min" ? score < prev.score : score > prev.score)) {
|
|
215
|
+
pendingScores.set(board, { score, mode });
|
|
216
|
+
}
|
|
217
|
+
return { submitted: false, guest: true, queued: true };
|
|
218
|
+
}
|
|
219
|
+
const res = await stateFetch("/scores", {
|
|
220
|
+
method: "POST",
|
|
221
|
+
body: JSON.stringify({ score, board, mode })
|
|
222
|
+
});
|
|
223
|
+
if (!res.ok) {
|
|
224
|
+
emit("error", { error: new Error(`genex score submit failed (${res.status})`) });
|
|
225
|
+
return { submitted: false };
|
|
226
|
+
}
|
|
227
|
+
const body = await res.json();
|
|
228
|
+
return { submitted: true, best: body.best, improved: body.improved };
|
|
229
|
+
}
|
|
230
|
+
async function getLeaderboard(opts) {
|
|
231
|
+
if (!config) throw new Error("genex embed: call initEmbed() first");
|
|
232
|
+
if (state === "pending") {
|
|
233
|
+
try {
|
|
234
|
+
await waitForPlayer();
|
|
235
|
+
} catch {
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const params = new URLSearchParams();
|
|
239
|
+
if (opts?.board !== void 0) params.set("board", opts.board);
|
|
240
|
+
if (opts?.limit !== void 0) params.set("limit", String(opts.limit));
|
|
241
|
+
if (opts?.order !== void 0) params.set("order", opts.order);
|
|
242
|
+
const qs = params.toString();
|
|
243
|
+
const headers = {};
|
|
244
|
+
if (embedToken) headers.Authorization = `Bearer ${embedToken}`;
|
|
245
|
+
const res = await doFetch(
|
|
246
|
+
`${config.apiUrl}/api/projects/${encodeURIComponent(config.slug)}/leaderboard${qs ? `?${qs}` : ""}`,
|
|
247
|
+
{ headers }
|
|
248
|
+
);
|
|
249
|
+
if (!res.ok) throw new Error(`genex leaderboard failed (${res.status})`);
|
|
250
|
+
return await res.json();
|
|
251
|
+
}
|
|
252
|
+
async function flushGuestQueue() {
|
|
253
|
+
try {
|
|
254
|
+
if (hasPendingPlayerSave) {
|
|
255
|
+
const save = pendingPlayerSave;
|
|
256
|
+
pendingPlayerSave = void 0;
|
|
257
|
+
hasPendingPlayerSave = false;
|
|
258
|
+
await savePlayerState(save);
|
|
259
|
+
}
|
|
260
|
+
const queued = [...pendingScores.entries()];
|
|
261
|
+
pendingScores.clear();
|
|
262
|
+
for (const [board, { score, mode }] of queued) {
|
|
263
|
+
await submitScore(score, { board, mode });
|
|
264
|
+
}
|
|
265
|
+
} catch (error) {
|
|
266
|
+
emit("error", { error });
|
|
267
|
+
}
|
|
268
|
+
}
|
|
132
269
|
function emit(event, ctx) {
|
|
133
270
|
const set = listeners.get(event);
|
|
134
271
|
if (!set) return;
|
|
@@ -204,9 +341,20 @@ function _stashTicketFromUrl() {
|
|
|
204
341
|
if (!hash || !hash.includes("genex_")) return;
|
|
205
342
|
stashedTicketHash = hash;
|
|
206
343
|
try {
|
|
207
|
-
w.history.replaceState(null, "", w
|
|
344
|
+
w.history.replaceState(null, "", cleanUrl(w));
|
|
345
|
+
} catch {
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function cleanUrl(w) {
|
|
349
|
+
let search = w.location.search;
|
|
350
|
+
try {
|
|
351
|
+
const params = new URLSearchParams(search);
|
|
352
|
+
params.delete("genex_r");
|
|
353
|
+
const rest = params.toString();
|
|
354
|
+
search = rest ? `?${rest}` : "";
|
|
208
355
|
} catch {
|
|
209
356
|
}
|
|
357
|
+
return w.location.pathname + search;
|
|
210
358
|
}
|
|
211
359
|
function readFragment(w) {
|
|
212
360
|
const hash = stashedTicketHash ?? w.location.hash;
|
|
@@ -223,7 +371,7 @@ function readFragment(w) {
|
|
|
223
371
|
}
|
|
224
372
|
if (ticket || guest) {
|
|
225
373
|
try {
|
|
226
|
-
w.history.replaceState(null, "", w
|
|
374
|
+
w.history.replaceState(null, "", cleanUrl(w));
|
|
227
375
|
} catch {
|
|
228
376
|
}
|
|
229
377
|
}
|
|
@@ -231,8 +379,7 @@ function readFragment(w) {
|
|
|
231
379
|
}
|
|
232
380
|
async function redirectToAuthorize(w, opts) {
|
|
233
381
|
if (!config) return;
|
|
234
|
-
const
|
|
235
|
-
const origin = origins[0] ?? config.dashboardOrigins[0];
|
|
382
|
+
const origin = opts?.guestOk ? config.dashboardOrigins[0] ?? (await fetchDashboardOrigins())[0] : (await fetchDashboardOrigins())[0] ?? config.dashboardOrigins[0];
|
|
236
383
|
if (!origin) {
|
|
237
384
|
enterBlocked();
|
|
238
385
|
return;
|
|
@@ -283,6 +430,7 @@ async function redeemTicket(ticket) {
|
|
|
283
430
|
if (isEmbedded() && parentOrigin) {
|
|
284
431
|
postToParent({ type: "genex:embed:authenticated", v: PROTOCOL_VERSION }, parentOrigin);
|
|
285
432
|
}
|
|
433
|
+
if (upgradingFromGuest) await flushGuestQueue();
|
|
286
434
|
const ctx = { user };
|
|
287
435
|
for (const waiter of authWaiters.splice(0)) waiter.resolve(ctx);
|
|
288
436
|
for (const waiter of playerWaiters.splice(0)) waiter.resolve({ user, guest: false });
|
|
@@ -437,7 +585,7 @@ function removeOverlay() {
|
|
|
437
585
|
function buildOverlay(d) {
|
|
438
586
|
const root = d.createElement("div");
|
|
439
587
|
root.setAttribute("data-genex-embed-overlay", "");
|
|
440
|
-
root.style.cssText = "position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:16px;background
|
|
588
|
+
root.style.cssText = "position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;flex-direction:column;gap:16px;background:#080a14;color:#fff;font-family:system-ui,-apple-system,sans-serif;text-align:center;pointer-events:auto;user-select:none";
|
|
441
589
|
const title = d.createElement("div");
|
|
442
590
|
title.style.cssText = "font-size:18px;font-weight:600;padding:0 24px";
|
|
443
591
|
root.appendChild(title);
|
|
@@ -540,6 +688,9 @@ function __resetForTests(overrides) {
|
|
|
540
688
|
authWaiters = [];
|
|
541
689
|
playerWaiters = [];
|
|
542
690
|
stashedTicketHash = null;
|
|
691
|
+
pendingPlayerSave = void 0;
|
|
692
|
+
hasPendingPlayerSave = false;
|
|
693
|
+
pendingScores.clear();
|
|
543
694
|
handshakeTimeoutMs = overrides?.handshakeTimeoutMs ?? 1e4;
|
|
544
695
|
refreshDelayMs = overrides?.refreshDelayMs ?? 10 * 6e4;
|
|
545
696
|
refreshRetryMs = overrides?.refreshRetryMs ?? 6e4;
|
|
@@ -555,6 +706,12 @@ export {
|
|
|
555
706
|
waitForAuth,
|
|
556
707
|
waitForPlayer,
|
|
557
708
|
on,
|
|
709
|
+
loadPlayerState,
|
|
710
|
+
savePlayerState,
|
|
711
|
+
loadWorldState,
|
|
712
|
+
saveWorldState,
|
|
713
|
+
submitScore,
|
|
714
|
+
getLeaderboard,
|
|
558
715
|
_stashTicketFromUrl,
|
|
559
716
|
__resetForTests
|
|
560
717
|
};
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,56 @@ interface EmbedUser {
|
|
|
17
17
|
}
|
|
18
18
|
type AuthState = 'pending' | 'authenticated' | 'guest' | 'blocked';
|
|
19
19
|
type EmbedEvent = 'authenticated' | 'guest' | 'blocked' | 'error';
|
|
20
|
+
interface PlayerStateResult {
|
|
21
|
+
/** The saved blob, or null when this player never saved. For guests: the queued pending save, if any. */
|
|
22
|
+
data: unknown;
|
|
23
|
+
/** Optimistic-concurrency version — pass to savePlayerState({ ifVersion }) to detect races. 0 = never saved. */
|
|
24
|
+
version: number;
|
|
25
|
+
/** True when the current identity is a guest (server storage untouched). */
|
|
26
|
+
guest?: boolean;
|
|
27
|
+
}
|
|
28
|
+
interface WorldStateResult {
|
|
29
|
+
data: unknown;
|
|
30
|
+
version: number;
|
|
31
|
+
guest?: boolean;
|
|
32
|
+
}
|
|
33
|
+
interface SaveStateResult {
|
|
34
|
+
/** True when the write landed on the server. */
|
|
35
|
+
saved: boolean;
|
|
36
|
+
/** The row's version after a successful write — or the CURRENT server version on a conflict. */
|
|
37
|
+
version?: number;
|
|
38
|
+
/** True when If-Match lost the race (reload, merge, retry with the fresh version). */
|
|
39
|
+
conflict?: boolean;
|
|
40
|
+
/** True when the caller is a guest — accounts save, guests don't. */
|
|
41
|
+
guest?: boolean;
|
|
42
|
+
/** True when the value was queued to auto-flush if this guest signs in mid-game. */
|
|
43
|
+
queued?: boolean;
|
|
44
|
+
}
|
|
45
|
+
interface SubmitScoreResult {
|
|
46
|
+
submitted: boolean;
|
|
47
|
+
/** The standing best after this submit (may be an older, better score). */
|
|
48
|
+
best?: number;
|
|
49
|
+
/** True when this submit improved the player's best. */
|
|
50
|
+
improved?: boolean;
|
|
51
|
+
guest?: boolean;
|
|
52
|
+
/** True when the score was queued to auto-submit if this guest signs in mid-game. */
|
|
53
|
+
queued?: boolean;
|
|
54
|
+
}
|
|
55
|
+
interface LeaderboardEntry {
|
|
56
|
+
userId: string;
|
|
57
|
+
name: string;
|
|
58
|
+
image?: string | null;
|
|
59
|
+
score: number;
|
|
60
|
+
updatedAt: string;
|
|
61
|
+
}
|
|
62
|
+
interface Leaderboard {
|
|
63
|
+
items: LeaderboardEntry[];
|
|
64
|
+
/** This player's rank + best — null for guests, tokenless reads, and players with no score. */
|
|
65
|
+
me: {
|
|
66
|
+
rank: number;
|
|
67
|
+
score: number;
|
|
68
|
+
} | null;
|
|
69
|
+
}
|
|
20
70
|
interface EventContext {
|
|
21
71
|
user?: EmbedUser;
|
|
22
72
|
error?: unknown;
|
|
@@ -92,6 +142,65 @@ declare function waitForPlayer(): Promise<{
|
|
|
92
142
|
}>;
|
|
93
143
|
/** Subscribe to auth lifecycle events. Returns an unsubscribe function. */
|
|
94
144
|
declare function on(event: EmbedEvent, cb: (ctx?: EventContext) => void): () => void;
|
|
145
|
+
/**
|
|
146
|
+
* Load THIS player's own save slot (per-player, per-game — another player can
|
|
147
|
+
* never read or clobber it). Resolves { data: null, version: 0 } when they
|
|
148
|
+
* never saved. Guests get their queued pending save (if any) so a round-trip
|
|
149
|
+
* works mid-session; the server is untouched. Await-safe from boot: resolves
|
|
150
|
+
* after identity exists, rejects only when the session is blocked.
|
|
151
|
+
*/
|
|
152
|
+
declare function loadPlayerState(): Promise<PlayerStateResult>;
|
|
153
|
+
/**
|
|
154
|
+
* Save THIS player's own slot (any JSON ≤ 256KB; last-write-wins on their own
|
|
155
|
+
* row). Guests: the value is QUEUED in memory and auto-flushed if they sign in
|
|
156
|
+
* mid-game ({ saved: false, guest: true, queued: true }) — no extra game code.
|
|
157
|
+
* Pass { ifVersion } (from the last load/save) to detect same-account
|
|
158
|
+
* two-device races: a losing write resolves { conflict: true, version } —
|
|
159
|
+
* reload, merge, retry. Small saves survive tab close (fetch keepalive).
|
|
160
|
+
*/
|
|
161
|
+
declare function savePlayerState(data: unknown, opts?: {
|
|
162
|
+
ifVersion?: number;
|
|
163
|
+
}): Promise<SaveStateResult>;
|
|
164
|
+
/**
|
|
165
|
+
* Load the game's SHARED world slot (one blob per game — every player reads
|
|
166
|
+
* the same world). Guests resolve { data: null, guest: true } — the server
|
|
167
|
+
* refuses guest reads; in multiplayer, guests receive the live world through
|
|
168
|
+
* the room instead.
|
|
169
|
+
*/
|
|
170
|
+
declare function loadWorldState(): Promise<WorldStateResult>;
|
|
171
|
+
/**
|
|
172
|
+
* Save the game's SHARED world slot (any JSON ≤ 1MB, any signed-in player may
|
|
173
|
+
* write — let ONE authority do it, e.g. the multiplayer host, and debounce).
|
|
174
|
+
* Strongly prefer { ifVersion } here: the slot is shared, so unconditional
|
|
175
|
+
* writes silently lose races. Guests: { saved: false, guest: true } — world
|
|
176
|
+
* saves are NEVER queued (flushing a stale world later would clobber the live
|
|
177
|
+
* one; the eventual account host keeps saving instead).
|
|
178
|
+
*/
|
|
179
|
+
declare function saveWorldState(data: unknown, opts?: {
|
|
180
|
+
ifVersion?: number;
|
|
181
|
+
}): Promise<SaveStateResult>;
|
|
182
|
+
/**
|
|
183
|
+
* Submit a score (soft-trust leaderboards: identity is verified server-side,
|
|
184
|
+
* one row per real player per board). Keep-best: only a strictly better score
|
|
185
|
+
* moves the row — `mode: "min"` for lap-time-style boards (send a consistent
|
|
186
|
+
* mode per board). Guests: the BEST value per board is queued and
|
|
187
|
+
* auto-submitted if they sign in mid-game.
|
|
188
|
+
*/
|
|
189
|
+
declare function submitScore(score: number, opts?: {
|
|
190
|
+
board?: string;
|
|
191
|
+
mode?: 'max' | 'min';
|
|
192
|
+
}): Promise<SubmitScoreResult>;
|
|
193
|
+
/**
|
|
194
|
+
* Read a leaderboard: top entries (verified display names) + this player's
|
|
195
|
+
* own rank when signed in. Works for EVERYONE on published games — guests and
|
|
196
|
+
* even tokenless contexts read the public board (me: null). limit ≤ 100;
|
|
197
|
+
* order "asc" for lower-is-better boards.
|
|
198
|
+
*/
|
|
199
|
+
declare function getLeaderboard(opts?: {
|
|
200
|
+
board?: string;
|
|
201
|
+
limit?: number;
|
|
202
|
+
order?: 'desc' | 'asc';
|
|
203
|
+
}): Promise<Leaderboard>;
|
|
95
204
|
/**
|
|
96
205
|
* @internal — for ./sentry.ts ONLY, not public API (games never call this).
|
|
97
206
|
* If the URL fragment carries any genex marker (`genex_ticket` — a secret —
|
|
@@ -105,4 +214,4 @@ declare function __resetForTests(overrides?: {
|
|
|
105
214
|
refreshRetryMs?: number;
|
|
106
215
|
}): void;
|
|
107
216
|
|
|
108
|
-
export { type AuthState, type EmbedConfig, type EmbedEvent, type EmbedUser, __resetForTests, _stashTicketFromUrl, getAuthState, getColyseusAuth, getEmbedToken, getUser, initEmbed, isEmbedded, on, waitForAuth, waitForPlayer };
|
|
217
|
+
export { type AuthState, type EmbedConfig, type EmbedEvent, type EmbedUser, type Leaderboard, type LeaderboardEntry, type PlayerStateResult, type SaveStateResult, type SubmitScoreResult, type WorldStateResult, __resetForTests, _stashTicketFromUrl, getAuthState, getColyseusAuth, getEmbedToken, getLeaderboard, getUser, initEmbed, isEmbedded, loadPlayerState, loadWorldState, on, savePlayerState, saveWorldState, submitScore, waitForAuth, waitForPlayer };
|
package/dist/index.js
CHANGED
|
@@ -4,23 +4,35 @@ import {
|
|
|
4
4
|
getAuthState,
|
|
5
5
|
getColyseusAuth,
|
|
6
6
|
getEmbedToken,
|
|
7
|
+
getLeaderboard,
|
|
7
8
|
getUser,
|
|
8
9
|
initEmbed,
|
|
9
10
|
isEmbedded,
|
|
11
|
+
loadPlayerState,
|
|
12
|
+
loadWorldState,
|
|
10
13
|
on,
|
|
14
|
+
savePlayerState,
|
|
15
|
+
saveWorldState,
|
|
16
|
+
submitScore,
|
|
11
17
|
waitForAuth,
|
|
12
18
|
waitForPlayer
|
|
13
|
-
} from "./chunk-
|
|
19
|
+
} from "./chunk-6E5DPEVG.js";
|
|
14
20
|
export {
|
|
15
21
|
__resetForTests,
|
|
16
22
|
_stashTicketFromUrl,
|
|
17
23
|
getAuthState,
|
|
18
24
|
getColyseusAuth,
|
|
19
25
|
getEmbedToken,
|
|
26
|
+
getLeaderboard,
|
|
20
27
|
getUser,
|
|
21
28
|
initEmbed,
|
|
22
29
|
isEmbedded,
|
|
30
|
+
loadPlayerState,
|
|
31
|
+
loadWorldState,
|
|
23
32
|
on,
|
|
33
|
+
savePlayerState,
|
|
34
|
+
saveWorldState,
|
|
35
|
+
submitScore,
|
|
24
36
|
waitForAuth,
|
|
25
37
|
waitForPlayer
|
|
26
38
|
};
|
package/dist/sentry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/embed-sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Player identity for genex games — signed-in
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Player identity + durable game state for genex games — signed-in or guest play, per-player save slots, shared world state, and soft-trust leaderboards.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -20,6 +20,13 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"dist"
|
|
22
22
|
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsup",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "node --test test/*.test.ts",
|
|
27
|
+
"check": "pnpm build && publint && attw --pack . --profile esm-only",
|
|
28
|
+
"prepack": "pnpm build && publint"
|
|
29
|
+
},
|
|
23
30
|
"publishConfig": {
|
|
24
31
|
"access": "public"
|
|
25
32
|
},
|
|
@@ -42,11 +49,5 @@
|
|
|
42
49
|
"url": "git+https://github.com/me-ai-org/genex-demo.git",
|
|
43
50
|
"directory": "packages/embed-sdk"
|
|
44
51
|
},
|
|
45
|
-
"license": "MIT"
|
|
46
|
-
|
|
47
|
-
"build": "tsup",
|
|
48
|
-
"typecheck": "tsc --noEmit",
|
|
49
|
-
"test": "node --test test/*.test.ts",
|
|
50
|
-
"check": "pnpm build && publint && attw --pack . --profile esm-only"
|
|
51
|
-
}
|
|
52
|
-
}
|
|
52
|
+
"license": "MIT"
|
|
53
|
+
}
|