@genex-ai/embed-sdk 0.4.0 → 0.5.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-6E5DPEVG.js → chunk-GDJLPL6W.js} +50 -5
- package/dist/index.d.ts +11 -5
- package/dist/index.js +1 -1
- package/dist/sentry.js +1 -1
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ var POPOVER_DISMISSED_FLAG = "genex:guest:popover-dismissed";
|
|
|
5
5
|
var config = null;
|
|
6
6
|
var state = "pending";
|
|
7
7
|
var user = null;
|
|
8
|
+
var localTestMode = false;
|
|
8
9
|
var embedToken;
|
|
9
10
|
var colyseusUrl;
|
|
10
11
|
var parentOrigin = null;
|
|
@@ -77,6 +78,10 @@ function initEmbed(cfg) {
|
|
|
77
78
|
dashboardOrigins: [...cfg.dashboardOrigins]
|
|
78
79
|
};
|
|
79
80
|
state = "pending";
|
|
81
|
+
if (wantsLocalTest(w)) {
|
|
82
|
+
enterLocalTestMode(w);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
80
85
|
showOverlay("connecting");
|
|
81
86
|
if (isEmbedded()) {
|
|
82
87
|
startEmbeddedHandshake(w);
|
|
@@ -138,6 +143,7 @@ async function ensurePlayer() {
|
|
|
138
143
|
if (state === "pending") await waitForPlayer();
|
|
139
144
|
if (state === "blocked") throw new Error("genex embed auth: blocked");
|
|
140
145
|
}
|
|
146
|
+
var lastWorldVersion;
|
|
141
147
|
function stateFetch(path, init = {}) {
|
|
142
148
|
const headers = { Authorization: `Bearer ${embedToken}` };
|
|
143
149
|
if (init.body !== void 0) headers["Content-Type"] = "application/json";
|
|
@@ -192,18 +198,22 @@ async function loadWorldState() {
|
|
|
192
198
|
const res = await stateFetch("/state");
|
|
193
199
|
if (!res.ok) throw new Error(`genex load world state failed (${res.status})`);
|
|
194
200
|
const body = await res.json();
|
|
201
|
+
lastWorldVersion = body.version;
|
|
195
202
|
return { data: body.data, version: body.version };
|
|
196
203
|
}
|
|
197
204
|
async function saveWorldState(data, opts) {
|
|
198
205
|
await ensurePlayer();
|
|
199
206
|
if (state === "guest") return { saved: false, guest: true };
|
|
200
|
-
|
|
207
|
+
const guard = opts && "ifVersion" in opts ? opts.ifVersion : lastWorldVersion;
|
|
208
|
+
const result = await decodeSave(
|
|
201
209
|
await stateFetch("/state", {
|
|
202
210
|
method: "PUT",
|
|
203
211
|
body: JSON.stringify(data),
|
|
204
|
-
ifVersion:
|
|
212
|
+
ifVersion: guard == null ? void 0 : guard
|
|
205
213
|
})
|
|
206
214
|
);
|
|
215
|
+
if (result.version !== void 0) lastWorldVersion = result.version;
|
|
216
|
+
return result;
|
|
207
217
|
}
|
|
208
218
|
async function submitScore(score, opts) {
|
|
209
219
|
const board = opts?.board ?? "main";
|
|
@@ -229,6 +239,7 @@ async function submitScore(score, opts) {
|
|
|
229
239
|
}
|
|
230
240
|
async function getLeaderboard(opts) {
|
|
231
241
|
if (!config) throw new Error("genex embed: call initEmbed() first");
|
|
242
|
+
if (localTestMode) return { items: [], me: null };
|
|
232
243
|
if (state === "pending") {
|
|
233
244
|
try {
|
|
234
245
|
await waitForPlayer();
|
|
@@ -378,7 +389,7 @@ function readFragment(w) {
|
|
|
378
389
|
return { ticket, guest };
|
|
379
390
|
}
|
|
380
391
|
async function redirectToAuthorize(w, opts) {
|
|
381
|
-
if (!config) return;
|
|
392
|
+
if (!config || localTestMode) return;
|
|
382
393
|
const origin = opts?.guestOk ? config.dashboardOrigins[0] ?? (await fetchDashboardOrigins())[0] : (await fetchDashboardOrigins())[0] ?? config.dashboardOrigins[0];
|
|
383
394
|
if (!origin) {
|
|
384
395
|
enterBlocked();
|
|
@@ -403,7 +414,7 @@ async function fetchDashboardOrigins() {
|
|
|
403
414
|
return config.dashboardOrigins;
|
|
404
415
|
}
|
|
405
416
|
async function redeemTicket(ticket) {
|
|
406
|
-
if (!config || redeeming || state !== "pending" && state !== "guest") return;
|
|
417
|
+
if (!config || localTestMode || redeeming || state !== "pending" && state !== "guest") return;
|
|
407
418
|
const upgradingFromGuest = state === "guest";
|
|
408
419
|
redeeming = true;
|
|
409
420
|
try {
|
|
@@ -443,7 +454,7 @@ async function redeemTicket(ticket) {
|
|
|
443
454
|
}
|
|
444
455
|
}
|
|
445
456
|
async function requestGuestSession() {
|
|
446
|
-
if (!config || requestingGuest || redeeming) return;
|
|
457
|
+
if (!config || localTestMode || requestingGuest || redeeming) return;
|
|
447
458
|
if (state !== "pending" && state !== "guest") return;
|
|
448
459
|
requestingGuest = true;
|
|
449
460
|
try {
|
|
@@ -489,6 +500,39 @@ function enterGuest() {
|
|
|
489
500
|
emit("guest", ctx);
|
|
490
501
|
}
|
|
491
502
|
}
|
|
503
|
+
function isLoopbackLocation(w) {
|
|
504
|
+
try {
|
|
505
|
+
const u = new URL(w.location.origin);
|
|
506
|
+
return u.protocol === "http:" && (u.hostname === "localhost" || u.hostname === "127.0.0.1" || u.hostname === "[::1]");
|
|
507
|
+
} catch {
|
|
508
|
+
return false;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
function wantsLocalTest(w) {
|
|
512
|
+
if (isEmbedded()) return false;
|
|
513
|
+
if (!isLoopbackLocation(w)) return false;
|
|
514
|
+
try {
|
|
515
|
+
return new URLSearchParams(w.location.search).get("genex_local_test") === "1";
|
|
516
|
+
} catch {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
function enterLocalTestMode(w) {
|
|
521
|
+
localTestMode = true;
|
|
522
|
+
readFragment(w);
|
|
523
|
+
const localUser = { id: "local:test", name: "Local Tester" };
|
|
524
|
+
user = localUser;
|
|
525
|
+
state = "guest";
|
|
526
|
+
removeOverlay();
|
|
527
|
+
try {
|
|
528
|
+
console.info(
|
|
529
|
+
"[genex] local test mode \u2014 local visuals and controls only. Auth, saves, leaderboards, and multiplayer are NOT validated here; no Genex credential exists."
|
|
530
|
+
);
|
|
531
|
+
} catch {
|
|
532
|
+
}
|
|
533
|
+
for (const waiter of playerWaiters.splice(0)) waiter.resolve({ user: localUser, guest: true });
|
|
534
|
+
emit("guest", { user: localUser });
|
|
535
|
+
}
|
|
492
536
|
async function handleRedeemFailure() {
|
|
493
537
|
const w = win();
|
|
494
538
|
if (w && !isEmbedded() && !readRetryFlag()) {
|
|
@@ -678,6 +722,7 @@ function __resetForTests(overrides) {
|
|
|
678
722
|
config = null;
|
|
679
723
|
state = "pending";
|
|
680
724
|
user = null;
|
|
725
|
+
localTestMode = false;
|
|
681
726
|
embedToken = void 0;
|
|
682
727
|
colyseusUrl = void 0;
|
|
683
728
|
parentOrigin = null;
|
package/dist/index.d.ts
CHANGED
|
@@ -98,6 +98,9 @@ declare function getAuthState(): AuthState;
|
|
|
98
98
|
/**
|
|
99
99
|
* The current player — non-null once 'authenticated' OR 'guest'. Guests have
|
|
100
100
|
* an `id` prefixed `guest:` and a server-assigned name like "Guest-1234".
|
|
101
|
+
* Local test mode is the one guest-state exception: `id` is `local:test`
|
|
102
|
+
* (a prefix that can never collide with `guest:` or account ids) and the
|
|
103
|
+
* name is "Local Tester".
|
|
101
104
|
*/
|
|
102
105
|
declare function getUser(): EmbedUser | null;
|
|
103
106
|
/**
|
|
@@ -171,13 +174,16 @@ declare function loadWorldState(): Promise<WorldStateResult>;
|
|
|
171
174
|
/**
|
|
172
175
|
* Save the game's SHARED world slot (any JSON ≤ 1MB, any signed-in player may
|
|
173
176
|
* write — let ONE authority do it, e.g. the multiplayer host, and debounce).
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
177
|
+
* If-Match is sent BY DEFAULT (from the last version this SDK loaded/saved), so a
|
|
178
|
+
* shared write can't silently clobber a newer one — a lost race resolves
|
|
179
|
+
* { conflict: true, version }: reload, merge, retry. Pass { ifVersion: n } to
|
|
180
|
+
* guard against a specific version, or { ifVersion: null } to force an
|
|
181
|
+
* unconditional overwrite (the old last-write-wins behaviour). Guests:
|
|
182
|
+
* { saved: false, guest: true } — world saves are NEVER queued (flushing a stale
|
|
183
|
+
* world later would clobber the live one; the eventual account host keeps saving).
|
|
178
184
|
*/
|
|
179
185
|
declare function saveWorldState(data: unknown, opts?: {
|
|
180
|
-
ifVersion?: number;
|
|
186
|
+
ifVersion?: number | null;
|
|
181
187
|
}): Promise<SaveStateResult>;
|
|
182
188
|
/**
|
|
183
189
|
* Submit a score (soft-trust leaderboards: identity is verified server-side,
|
package/dist/index.js
CHANGED
package/dist/sentry.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genex-ai/embed-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
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",
|