@playcademy/sdk 0.22.0 → 0.23.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/README.md +1 -1
- package/dist/assets.js +0 -12
- package/dist/contracts.js +3 -15
- package/dist/index.js +64 -64
- package/dist/internal.d.ts +1 -1
- package/dist/internal.js +86 -86
- package/dist/server/edge.js +5 -17
- package/dist/server.js +17 -22
- package/dist/types.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -375,7 +375,7 @@ const client = await PlaycademyClient.init()
|
|
|
375
375
|
|
|
376
376
|
## Dependencies
|
|
377
377
|
|
|
378
|
-
- Peer dependencies: TypeScript (`^5 || ^6`) and Drizzle ORM (`^0.
|
|
378
|
+
- Peer dependencies: TypeScript (`^5 || ^6`) and Drizzle ORM (`^0.45.3`), as declared in
|
|
379
379
|
[package.json](./package.json).
|
|
380
380
|
- Workspace development dependencies include `@playcademy/types`, `@playcademy/constants`,
|
|
381
381
|
`@playcademy/utils`, and `@playcademy/test-utils`. Contributors install these through the root
|
package/dist/assets.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
-
|
|
13
1
|
// src/assets.ts
|
|
14
2
|
function getManifest() {
|
|
15
3
|
return globalThis.__PLAYCADEMY_ASSET_MANIFEST ?? null;
|
package/dist/contracts.js
CHANGED
|
@@ -1,23 +1,11 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
-
|
|
13
1
|
// src/contracts/child-catalog.ts
|
|
14
2
|
var CHILD_CATALOG_CONTRACT = "playcademy-child-catalog-v1";
|
|
15
3
|
var CHILD_ATTEMPT_CONTRACT = "playcademy-child-attempt-v1";
|
|
16
4
|
var CATALOG_LEVELS = ["e1", "e2", "e3", "e4"];
|
|
17
5
|
var CATALOG_READINESS = ["ready", "simulated", "unavailable"];
|
|
18
6
|
export {
|
|
19
|
-
|
|
20
|
-
CHILD_ATTEMPT_CONTRACT,
|
|
7
|
+
CATALOG_LEVELS,
|
|
21
8
|
CATALOG_READINESS,
|
|
22
|
-
|
|
9
|
+
CHILD_ATTEMPT_CONTRACT,
|
|
10
|
+
CHILD_CATALOG_CONTRACT
|
|
23
11
|
};
|
package/dist/index.js
CHANGED
|
@@ -1,36 +1,24 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
-
|
|
13
1
|
// src/messaging.ts
|
|
14
2
|
var MessageEvents;
|
|
15
|
-
((
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
3
|
+
((MessageEvents) => {
|
|
4
|
+
MessageEvents["INIT"] = "PLAYCADEMY_INIT";
|
|
5
|
+
MessageEvents["TOKEN_REFRESH"] = "PLAYCADEMY_TOKEN_REFRESH";
|
|
6
|
+
MessageEvents["PAUSE"] = "PLAYCADEMY_PAUSE";
|
|
7
|
+
MessageEvents["RESUME"] = "PLAYCADEMY_RESUME";
|
|
8
|
+
MessageEvents["FORCE_EXIT"] = "PLAYCADEMY_FORCE_EXIT";
|
|
9
|
+
MessageEvents["OVERLAY"] = "PLAYCADEMY_OVERLAY";
|
|
10
|
+
MessageEvents["READY"] = "PLAYCADEMY_READY";
|
|
11
|
+
MessageEvents["INIT_ERROR"] = "PLAYCADEMY_INIT_ERROR";
|
|
12
|
+
MessageEvents["EXIT"] = "PLAYCADEMY_EXIT";
|
|
13
|
+
MessageEvents["TELEMETRY"] = "PLAYCADEMY_TELEMETRY";
|
|
14
|
+
MessageEvents["KEY_EVENT"] = "PLAYCADEMY_KEY_EVENT";
|
|
15
|
+
MessageEvents["DEMO_END"] = "PLAYCADEMY_DEMO_END";
|
|
16
|
+
MessageEvents["TIMEBACK_HEARTBEAT_RELAY"] = "PLAYCADEMY_TIMEBACK_HEARTBEAT_RELAY";
|
|
17
|
+
MessageEvents["TIMEBACK_ACTIVITY_START"] = "PLAYCADEMY_TIMEBACK_ACTIVITY_START";
|
|
18
|
+
MessageEvents["TIMEBACK_ACTIVITY_END"] = "PLAYCADEMY_TIMEBACK_ACTIVITY_END";
|
|
19
|
+
MessageEvents["CHECKPOINT"] = "PLAYCADEMY_CHECKPOINT";
|
|
20
|
+
MessageEvents["AUTH_STATE_CHANGE"] = "PLAYCADEMY_AUTH_STATE_CHANGE";
|
|
21
|
+
MessageEvents["AUTH_CALLBACK"] = "PLAYCADEMY_AUTH_CALLBACK";
|
|
34
22
|
})(MessageEvents ||= {});
|
|
35
23
|
function isFromParentWindow(event) {
|
|
36
24
|
return globalThis.window.parent !== globalThis.window && event.source === globalThis.window.parent;
|
|
@@ -413,6 +401,15 @@ function formatJSONPretty(level, message, context, scope) {
|
|
|
413
401
|
const consoleMethod = getConsoleMethod(level);
|
|
414
402
|
consoleMethod(safeJSONStringify(logEntry, 2));
|
|
415
403
|
}
|
|
404
|
+
function serializeError(error) {
|
|
405
|
+
return {
|
|
406
|
+
...error,
|
|
407
|
+
name: error.name,
|
|
408
|
+
message: error.message,
|
|
409
|
+
stack: error.stack,
|
|
410
|
+
cause: error.cause
|
|
411
|
+
};
|
|
412
|
+
}
|
|
416
413
|
function safeJSONStringify(value, space) {
|
|
417
414
|
const seen = new WeakSet;
|
|
418
415
|
try {
|
|
@@ -425,6 +422,9 @@ function safeJSONStringify(value, space) {
|
|
|
425
422
|
return "[Circular]";
|
|
426
423
|
}
|
|
427
424
|
seen.add(currentValue);
|
|
425
|
+
if (currentValue instanceof Error) {
|
|
426
|
+
return serializeError(currentValue);
|
|
427
|
+
}
|
|
428
428
|
}
|
|
429
429
|
return currentValue;
|
|
430
430
|
}, space);
|
|
@@ -738,8 +738,8 @@ function createBackendNamespace(client) {
|
|
|
738
738
|
url(pathOrStrings, ...values) {
|
|
739
739
|
if (Array.isArray(pathOrStrings) && "raw" in pathOrStrings) {
|
|
740
740
|
const strings = pathOrStrings;
|
|
741
|
-
const
|
|
742
|
-
return `${client.gameUrl}/api${
|
|
741
|
+
const path = strings.reduce((acc, str, i) => acc + str + (values[i] != null ? String(values[i]) : ""), "");
|
|
742
|
+
return `${client.gameUrl}/api${path.startsWith("/") ? path : `/${path}`}`;
|
|
743
743
|
}
|
|
744
744
|
const path = pathOrStrings;
|
|
745
745
|
return `${client.gameUrl}/api${path.startsWith("/") ? path : `/${path}`}`;
|
|
@@ -940,13 +940,13 @@ function tokenUserId(token) {
|
|
|
940
940
|
return null;
|
|
941
941
|
}
|
|
942
942
|
}
|
|
943
|
-
function buildResumeStorageKey(
|
|
943
|
+
function buildResumeStorageKey(identity) {
|
|
944
944
|
const parts = [
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
945
|
+
identity.userId,
|
|
946
|
+
identity.parentGameId,
|
|
947
|
+
identity.slug,
|
|
948
|
+
identity.intent.lessonId,
|
|
949
|
+
identity.intent.eLevel
|
|
950
950
|
];
|
|
951
951
|
return `playcademy:embed:resume:v1:${parts.map(encodeURIComponent).join(":")}`;
|
|
952
952
|
}
|
|
@@ -2335,15 +2335,15 @@ function createEmbedNamespace(client) {
|
|
|
2335
2335
|
postEndActivity: (body) => client["requestGameBackend"](TIMEBACK_ROUTES.END_ACTIVITY, "POST", body, undefined, { retryPolicy: END_ACTIVITY_RETRY_POLICY })
|
|
2336
2336
|
},
|
|
2337
2337
|
resolveTarget: async (resumeContext) => {
|
|
2338
|
-
const
|
|
2338
|
+
const game = await client["request"](`/games/${encodeURIComponent(options.slug)}`, "GET").catch((error) => {
|
|
2339
2339
|
if (!options.gameUrl) {
|
|
2340
2340
|
throw error;
|
|
2341
2341
|
}
|
|
2342
2342
|
console.warn(`[Playcademy SDK] embed.launch(): slug '${options.slug}' did not resolve (${errorMessage(error)}). Continuing with gameUrl; the slug stands in as the child's game id.`);
|
|
2343
2343
|
return null;
|
|
2344
2344
|
});
|
|
2345
|
-
const childGameId =
|
|
2346
|
-
const deploymentUrl =
|
|
2345
|
+
const childGameId = game?.id ?? options.slug;
|
|
2346
|
+
const deploymentUrl = game ? game.deploymentUrl : undefined;
|
|
2347
2347
|
const rawUrl = options.gameUrl ?? deploymentUrl;
|
|
2348
2348
|
if (!rawUrl) {
|
|
2349
2349
|
throw new PlaycademyError(`Game '${options.slug}' has no deployment URL. Pass gameUrl to embed.launch() to point at one.`);
|
|
@@ -2433,11 +2433,11 @@ async function initiatePopupFlow(options) {
|
|
|
2433
2433
|
});
|
|
2434
2434
|
return await waitForServerMessage(popup, onStateChange);
|
|
2435
2435
|
} catch (error) {
|
|
2436
|
-
const
|
|
2436
|
+
const errorMessage = error instanceof Error ? error.message : "Authentication failed";
|
|
2437
2437
|
onStateChange?.({
|
|
2438
2438
|
status: "error",
|
|
2439
|
-
message:
|
|
2440
|
-
error: error instanceof Error ? error : new Error(
|
|
2439
|
+
message: errorMessage,
|
|
2440
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
2441
2441
|
});
|
|
2442
2442
|
throw error;
|
|
2443
2443
|
}
|
|
@@ -2540,11 +2540,11 @@ async function initiateRedirectFlow(options) {
|
|
|
2540
2540
|
globalThis.location.href = authUrl;
|
|
2541
2541
|
return new Promise(() => {});
|
|
2542
2542
|
} catch (error) {
|
|
2543
|
-
const
|
|
2543
|
+
const errorMessage = error instanceof Error ? error.message : "Authentication failed";
|
|
2544
2544
|
onStateChange?.({
|
|
2545
2545
|
status: "error",
|
|
2546
|
-
message:
|
|
2547
|
-
error: error instanceof Error ? error : new Error(
|
|
2546
|
+
message: errorMessage,
|
|
2547
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
2548
2548
|
});
|
|
2549
2549
|
throw error;
|
|
2550
2550
|
}
|
|
@@ -3629,15 +3629,15 @@ var retryRuntime = { wait };
|
|
|
3629
3629
|
function isRetryableStatus(status) {
|
|
3630
3630
|
return status === 429 || status >= 500;
|
|
3631
3631
|
}
|
|
3632
|
-
async function fetchWithRetry(url,
|
|
3632
|
+
async function fetchWithRetry(url, init) {
|
|
3633
3633
|
const startedAt = Date.now();
|
|
3634
|
-
const retryDelaysMs =
|
|
3634
|
+
const retryDelaysMs = init.retryPolicy?.retryDelaysMs ?? RETRY_DELAYS_MS;
|
|
3635
3635
|
for (let attempt = 0;attempt <= retryDelaysMs.length; attempt++) {
|
|
3636
3636
|
const retryDelayMs = retryDelaysMs[attempt];
|
|
3637
|
-
const retryableMethods =
|
|
3638
|
-
const canRetry = retryDelayMs !== undefined && retryableMethods.includes(
|
|
3637
|
+
const retryableMethods = init.retryPolicy?.retryableMethods ?? ["GET"];
|
|
3638
|
+
const canRetry = retryDelayMs !== undefined && retryableMethods.includes(init.method);
|
|
3639
3639
|
try {
|
|
3640
|
-
const response = await fetch(url,
|
|
3640
|
+
const response = await fetch(url, init);
|
|
3641
3641
|
if (canRetry && isRetryableStatus(response.status)) {
|
|
3642
3642
|
await retryRuntime.wait(retryDelayMs);
|
|
3643
3643
|
} else {
|
|
@@ -3762,7 +3762,7 @@ async function request({
|
|
|
3762
3762
|
return rawText && rawText.length > 0 ? rawText : undefined;
|
|
3763
3763
|
}
|
|
3764
3764
|
// src/version.ts
|
|
3765
|
-
var SDK_VERSION = "0.
|
|
3765
|
+
var SDK_VERSION = "0.23.0";
|
|
3766
3766
|
|
|
3767
3767
|
// src/clients/base.ts
|
|
3768
3768
|
class PlaycademyBaseClient {
|
|
@@ -3792,16 +3792,16 @@ class PlaycademyBaseClient {
|
|
|
3792
3792
|
}
|
|
3793
3793
|
getBaseUrl() {
|
|
3794
3794
|
const isRelative = this.baseUrl.startsWith("/");
|
|
3795
|
-
const
|
|
3796
|
-
return isRelative &&
|
|
3795
|
+
const isBrowser = typeof globalThis.window !== "undefined";
|
|
3796
|
+
return isRelative && isBrowser ? `${globalThis.location.origin}${this.baseUrl}` : this.baseUrl;
|
|
3797
3797
|
}
|
|
3798
3798
|
getGameBackendUrl() {
|
|
3799
3799
|
if (!this.gameUrl) {
|
|
3800
3800
|
throw new PlaycademyError("Game backend URL not configured. gameUrl must be set to use game backend features.");
|
|
3801
3801
|
}
|
|
3802
3802
|
const isRelative = this.gameUrl.startsWith("/");
|
|
3803
|
-
const
|
|
3804
|
-
const effectiveGameUrl = isRelative &&
|
|
3803
|
+
const isBrowser = typeof globalThis.window !== "undefined";
|
|
3804
|
+
const effectiveGameUrl = isRelative && isBrowser ? `${globalThis.location.origin}${this.gameUrl}` : this.gameUrl;
|
|
3805
3805
|
return `${effectiveGameUrl}/api`;
|
|
3806
3806
|
}
|
|
3807
3807
|
ping() {
|
|
@@ -3958,11 +3958,11 @@ function isChildLaunched(client) {
|
|
|
3958
3958
|
return client.mode === "child" && client.parent !== null;
|
|
3959
3959
|
}
|
|
3960
3960
|
export {
|
|
3961
|
-
|
|
3962
|
-
isChildLaunched,
|
|
3963
|
-
extractApiErrorInfo,
|
|
3964
|
-
PlaycademyError,
|
|
3965
|
-
PlaycademyClient,
|
|
3961
|
+
ApiError,
|
|
3966
3962
|
MessageEvents,
|
|
3967
|
-
|
|
3963
|
+
PlaycademyClient,
|
|
3964
|
+
PlaycademyError,
|
|
3965
|
+
extractApiErrorInfo,
|
|
3966
|
+
isChildLaunched,
|
|
3967
|
+
messaging
|
|
3968
3968
|
};
|
package/dist/internal.d.ts
CHANGED
|
@@ -6441,7 +6441,7 @@ interface PlatformTimebackUserContext {
|
|
|
6441
6441
|
*/
|
|
6442
6442
|
interface PlatformTimebackUser extends PlatformTimebackUserContext {
|
|
6443
6443
|
/**
|
|
6444
|
-
* Fetch TimeBack data from the server (cached for
|
|
6444
|
+
* Fetch TimeBack data from the server (cached for 30 sec).
|
|
6445
6445
|
* Updates the cached values so subsequent property access returns fresh data.
|
|
6446
6446
|
* @param options - Cache options (pass { force: true } to bypass cache)
|
|
6447
6447
|
* @returns Promise resolving to user context with full enrollment data
|
package/dist/internal.js
CHANGED
|
@@ -1,36 +1,24 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
-
|
|
13
1
|
// src/messaging.ts
|
|
14
2
|
var MessageEvents;
|
|
15
|
-
((
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
3
|
+
((MessageEvents) => {
|
|
4
|
+
MessageEvents["INIT"] = "PLAYCADEMY_INIT";
|
|
5
|
+
MessageEvents["TOKEN_REFRESH"] = "PLAYCADEMY_TOKEN_REFRESH";
|
|
6
|
+
MessageEvents["PAUSE"] = "PLAYCADEMY_PAUSE";
|
|
7
|
+
MessageEvents["RESUME"] = "PLAYCADEMY_RESUME";
|
|
8
|
+
MessageEvents["FORCE_EXIT"] = "PLAYCADEMY_FORCE_EXIT";
|
|
9
|
+
MessageEvents["OVERLAY"] = "PLAYCADEMY_OVERLAY";
|
|
10
|
+
MessageEvents["READY"] = "PLAYCADEMY_READY";
|
|
11
|
+
MessageEvents["INIT_ERROR"] = "PLAYCADEMY_INIT_ERROR";
|
|
12
|
+
MessageEvents["EXIT"] = "PLAYCADEMY_EXIT";
|
|
13
|
+
MessageEvents["TELEMETRY"] = "PLAYCADEMY_TELEMETRY";
|
|
14
|
+
MessageEvents["KEY_EVENT"] = "PLAYCADEMY_KEY_EVENT";
|
|
15
|
+
MessageEvents["DEMO_END"] = "PLAYCADEMY_DEMO_END";
|
|
16
|
+
MessageEvents["TIMEBACK_HEARTBEAT_RELAY"] = "PLAYCADEMY_TIMEBACK_HEARTBEAT_RELAY";
|
|
17
|
+
MessageEvents["TIMEBACK_ACTIVITY_START"] = "PLAYCADEMY_TIMEBACK_ACTIVITY_START";
|
|
18
|
+
MessageEvents["TIMEBACK_ACTIVITY_END"] = "PLAYCADEMY_TIMEBACK_ACTIVITY_END";
|
|
19
|
+
MessageEvents["CHECKPOINT"] = "PLAYCADEMY_CHECKPOINT";
|
|
20
|
+
MessageEvents["AUTH_STATE_CHANGE"] = "PLAYCADEMY_AUTH_STATE_CHANGE";
|
|
21
|
+
MessageEvents["AUTH_CALLBACK"] = "PLAYCADEMY_AUTH_CALLBACK";
|
|
34
22
|
})(MessageEvents ||= {});
|
|
35
23
|
function isFromParentWindow(event) {
|
|
36
24
|
return globalThis.window.parent !== globalThis.window && event.source === globalThis.window.parent;
|
|
@@ -413,6 +401,15 @@ function formatJSONPretty(level, message, context, scope) {
|
|
|
413
401
|
const consoleMethod = getConsoleMethod(level);
|
|
414
402
|
consoleMethod(safeJSONStringify(logEntry, 2));
|
|
415
403
|
}
|
|
404
|
+
function serializeError(error) {
|
|
405
|
+
return {
|
|
406
|
+
...error,
|
|
407
|
+
name: error.name,
|
|
408
|
+
message: error.message,
|
|
409
|
+
stack: error.stack,
|
|
410
|
+
cause: error.cause
|
|
411
|
+
};
|
|
412
|
+
}
|
|
416
413
|
function safeJSONStringify(value, space) {
|
|
417
414
|
const seen = new WeakSet;
|
|
418
415
|
try {
|
|
@@ -425,6 +422,9 @@ function safeJSONStringify(value, space) {
|
|
|
425
422
|
return "[Circular]";
|
|
426
423
|
}
|
|
427
424
|
seen.add(currentValue);
|
|
425
|
+
if (currentValue instanceof Error) {
|
|
426
|
+
return serializeError(currentValue);
|
|
427
|
+
}
|
|
428
428
|
}
|
|
429
429
|
return currentValue;
|
|
430
430
|
}, space);
|
|
@@ -738,8 +738,8 @@ function createBackendNamespace(client) {
|
|
|
738
738
|
url(pathOrStrings, ...values) {
|
|
739
739
|
if (Array.isArray(pathOrStrings) && "raw" in pathOrStrings) {
|
|
740
740
|
const strings = pathOrStrings;
|
|
741
|
-
const
|
|
742
|
-
return `${client.gameUrl}/api${
|
|
741
|
+
const path = strings.reduce((acc, str, i) => acc + str + (values[i] != null ? String(values[i]) : ""), "");
|
|
742
|
+
return `${client.gameUrl}/api${path.startsWith("/") ? path : `/${path}`}`;
|
|
743
743
|
}
|
|
744
744
|
const path = pathOrStrings;
|
|
745
745
|
return `${client.gameUrl}/api${path.startsWith("/") ? path : `/${path}`}`;
|
|
@@ -940,13 +940,13 @@ function tokenUserId(token) {
|
|
|
940
940
|
return null;
|
|
941
941
|
}
|
|
942
942
|
}
|
|
943
|
-
function buildResumeStorageKey(
|
|
943
|
+
function buildResumeStorageKey(identity) {
|
|
944
944
|
const parts = [
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
945
|
+
identity.userId,
|
|
946
|
+
identity.parentGameId,
|
|
947
|
+
identity.slug,
|
|
948
|
+
identity.intent.lessonId,
|
|
949
|
+
identity.intent.eLevel
|
|
950
950
|
];
|
|
951
951
|
return `playcademy:embed:resume:v1:${parts.map(encodeURIComponent).join(":")}`;
|
|
952
952
|
}
|
|
@@ -2335,15 +2335,15 @@ function createEmbedNamespace(client) {
|
|
|
2335
2335
|
postEndActivity: (body) => client["requestGameBackend"](TIMEBACK_ROUTES.END_ACTIVITY, "POST", body, undefined, { retryPolicy: END_ACTIVITY_RETRY_POLICY })
|
|
2336
2336
|
},
|
|
2337
2337
|
resolveTarget: async (resumeContext) => {
|
|
2338
|
-
const
|
|
2338
|
+
const game = await client["request"](`/games/${encodeURIComponent(options.slug)}`, "GET").catch((error) => {
|
|
2339
2339
|
if (!options.gameUrl) {
|
|
2340
2340
|
throw error;
|
|
2341
2341
|
}
|
|
2342
2342
|
console.warn(`[Playcademy SDK] embed.launch(): slug '${options.slug}' did not resolve (${errorMessage(error)}). Continuing with gameUrl; the slug stands in as the child's game id.`);
|
|
2343
2343
|
return null;
|
|
2344
2344
|
});
|
|
2345
|
-
const childGameId =
|
|
2346
|
-
const deploymentUrl =
|
|
2345
|
+
const childGameId = game?.id ?? options.slug;
|
|
2346
|
+
const deploymentUrl = game ? game.deploymentUrl : undefined;
|
|
2347
2347
|
const rawUrl = options.gameUrl ?? deploymentUrl;
|
|
2348
2348
|
if (!rawUrl) {
|
|
2349
2349
|
throw new PlaycademyError(`Game '${options.slug}' has no deployment URL. Pass gameUrl to embed.launch() to point at one.`);
|
|
@@ -2433,11 +2433,11 @@ async function initiatePopupFlow(options) {
|
|
|
2433
2433
|
});
|
|
2434
2434
|
return await waitForServerMessage(popup, onStateChange);
|
|
2435
2435
|
} catch (error) {
|
|
2436
|
-
const
|
|
2436
|
+
const errorMessage = error instanceof Error ? error.message : "Authentication failed";
|
|
2437
2437
|
onStateChange?.({
|
|
2438
2438
|
status: "error",
|
|
2439
|
-
message:
|
|
2440
|
-
error: error instanceof Error ? error : new Error(
|
|
2439
|
+
message: errorMessage,
|
|
2440
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
2441
2441
|
});
|
|
2442
2442
|
throw error;
|
|
2443
2443
|
}
|
|
@@ -2540,11 +2540,11 @@ async function initiateRedirectFlow(options) {
|
|
|
2540
2540
|
globalThis.location.href = authUrl;
|
|
2541
2541
|
return new Promise(() => {});
|
|
2542
2542
|
} catch (error) {
|
|
2543
|
-
const
|
|
2543
|
+
const errorMessage = error instanceof Error ? error.message : "Authentication failed";
|
|
2544
2544
|
onStateChange?.({
|
|
2545
2545
|
status: "error",
|
|
2546
|
-
message:
|
|
2547
|
-
error: error instanceof Error ? error : new Error(
|
|
2546
|
+
message: errorMessage,
|
|
2547
|
+
error: error instanceof Error ? error : new Error(errorMessage)
|
|
2548
2548
|
});
|
|
2549
2549
|
throw error;
|
|
2550
2550
|
}
|
|
@@ -3636,7 +3636,7 @@ class DeployPipeline {
|
|
|
3636
3636
|
};
|
|
3637
3637
|
}
|
|
3638
3638
|
async buildRequestBody(args) {
|
|
3639
|
-
const
|
|
3639
|
+
const game = await this.resolveGame(args.slug, args.game);
|
|
3640
3640
|
const requestBody = {};
|
|
3641
3641
|
if (args.target) {
|
|
3642
3642
|
requestBody.target = args.target;
|
|
@@ -3669,7 +3669,7 @@ class DeployPipeline {
|
|
|
3669
3669
|
requestBody.source = args.source;
|
|
3670
3670
|
}
|
|
3671
3671
|
if (!args.worker) {
|
|
3672
|
-
return { game
|
|
3672
|
+
return { game, requestBody };
|
|
3673
3673
|
}
|
|
3674
3674
|
const workerFields = {
|
|
3675
3675
|
config: args.worker.config,
|
|
@@ -3684,7 +3684,7 @@ class DeployPipeline {
|
|
|
3684
3684
|
code: args.worker.code
|
|
3685
3685
|
};
|
|
3686
3686
|
if (this.serializedSize(inlineBody) <= DeployPipeline.MAX_INLINE_REQUEST_BYTES) {
|
|
3687
|
-
return { game
|
|
3687
|
+
return { game, requestBody: inlineBody };
|
|
3688
3688
|
}
|
|
3689
3689
|
const skeletonBody = {
|
|
3690
3690
|
...requestBody,
|
|
@@ -3694,8 +3694,8 @@ class DeployPipeline {
|
|
|
3694
3694
|
if (this.serializedSize(skeletonBody) > DeployPipeline.MAX_INLINE_REQUEST_BYTES) {
|
|
3695
3695
|
throw new Error("Deploy request is too large even after uploading worker code");
|
|
3696
3696
|
}
|
|
3697
|
-
skeletonBody.codeUploadToken = await this.uploadCode(
|
|
3698
|
-
return { game
|
|
3697
|
+
skeletonBody.codeUploadToken = await this.uploadCode(game.id, args.worker.code);
|
|
3698
|
+
return { game, requestBody: skeletonBody };
|
|
3699
3699
|
}
|
|
3700
3700
|
serializedSize(body) {
|
|
3701
3701
|
return DeployPipeline.textEncoder.encode(JSON.stringify(body)).length;
|
|
@@ -3786,8 +3786,8 @@ class DeployPipeline {
|
|
|
3786
3786
|
await new Promise((resolve) => setTimeout(resolve, DeployPipeline.POLL_INTERVAL_MS));
|
|
3787
3787
|
}
|
|
3788
3788
|
}
|
|
3789
|
-
async resolveGame(slug,
|
|
3790
|
-
return
|
|
3789
|
+
async resolveGame(slug, game) {
|
|
3790
|
+
return game ?? await this.client["request"](`/games/${slug}`, "GET");
|
|
3791
3791
|
}
|
|
3792
3792
|
async fetchGameWithRetry(slug) {
|
|
3793
3793
|
const { GAME_FETCH_RETRIES } = DeployPipeline;
|
|
@@ -3869,20 +3869,20 @@ function createDevNamespace(client) {
|
|
|
3869
3869
|
source
|
|
3870
3870
|
} = options;
|
|
3871
3871
|
hooks?.onEvent?.({ type: "init" });
|
|
3872
|
-
let
|
|
3872
|
+
let game;
|
|
3873
3873
|
if (metadata) {
|
|
3874
|
-
|
|
3874
|
+
game = await client["request"](`/games/${slug}`, "PUT", {
|
|
3875
3875
|
body: metadata
|
|
3876
3876
|
});
|
|
3877
3877
|
if (metadata.gameType === "external" && !file && !worker) {
|
|
3878
|
-
return
|
|
3878
|
+
return game;
|
|
3879
3879
|
}
|
|
3880
3880
|
}
|
|
3881
|
-
const uploadToken = file ? await deploy.uploadFile(file,
|
|
3881
|
+
const uploadToken = file ? await deploy.uploadFile(file, game?.id || slug, hooks) : undefined;
|
|
3882
3882
|
if (uploadToken || worker) {
|
|
3883
3883
|
return deploy.submitGame({
|
|
3884
3884
|
slug,
|
|
3885
|
-
game
|
|
3885
|
+
game,
|
|
3886
3886
|
uploadToken,
|
|
3887
3887
|
metadata,
|
|
3888
3888
|
worker,
|
|
@@ -3896,8 +3896,8 @@ function createDevNamespace(client) {
|
|
|
3896
3896
|
source
|
|
3897
3897
|
});
|
|
3898
3898
|
}
|
|
3899
|
-
if (
|
|
3900
|
-
return
|
|
3899
|
+
if (game) {
|
|
3900
|
+
return game;
|
|
3901
3901
|
}
|
|
3902
3902
|
throw new Error("No deployment actions specified (need metadata, file, or worker)");
|
|
3903
3903
|
},
|
|
@@ -3926,9 +3926,9 @@ function createDevNamespace(client) {
|
|
|
3926
3926
|
deploy: async (slug, options) => {
|
|
3927
3927
|
const { file, worker, hooks } = options;
|
|
3928
3928
|
hooks?.onEvent?.({ type: "init" });
|
|
3929
|
-
const
|
|
3930
|
-
const uploadToken = await deploy.uploadFile(file,
|
|
3931
|
-
return deploy.submitDashboard({ slug, game
|
|
3929
|
+
const game = await client["request"](`/games/${slug}`, "GET");
|
|
3930
|
+
const uploadToken = await deploy.uploadFile(file, game.id, hooks);
|
|
3931
|
+
return deploy.submitDashboard({ slug, game, uploadToken, worker, hooks });
|
|
3932
3932
|
},
|
|
3933
3933
|
remove: async (slug) => {
|
|
3934
3934
|
await client["request"](`/games/${slug}/dashboard`, "DELETE");
|
|
@@ -4539,15 +4539,15 @@ var retryRuntime = { wait };
|
|
|
4539
4539
|
function isRetryableStatus(status) {
|
|
4540
4540
|
return status === 429 || status >= 500;
|
|
4541
4541
|
}
|
|
4542
|
-
async function fetchWithRetry(url,
|
|
4542
|
+
async function fetchWithRetry(url, init) {
|
|
4543
4543
|
const startedAt = Date.now();
|
|
4544
|
-
const retryDelaysMs =
|
|
4544
|
+
const retryDelaysMs = init.retryPolicy?.retryDelaysMs ?? RETRY_DELAYS_MS;
|
|
4545
4545
|
for (let attempt = 0;attempt <= retryDelaysMs.length; attempt++) {
|
|
4546
4546
|
const retryDelayMs = retryDelaysMs[attempt];
|
|
4547
|
-
const retryableMethods =
|
|
4548
|
-
const canRetry = retryDelayMs !== undefined && retryableMethods.includes(
|
|
4547
|
+
const retryableMethods = init.retryPolicy?.retryableMethods ?? ["GET"];
|
|
4548
|
+
const canRetry = retryDelayMs !== undefined && retryableMethods.includes(init.method);
|
|
4549
4549
|
try {
|
|
4550
|
-
const response = await fetch(url,
|
|
4550
|
+
const response = await fetch(url, init);
|
|
4551
4551
|
if (canRetry && isRetryableStatus(response.status)) {
|
|
4552
4552
|
await retryRuntime.wait(retryDelayMs);
|
|
4553
4553
|
} else {
|
|
@@ -4672,7 +4672,7 @@ async function request({
|
|
|
4672
4672
|
return rawText && rawText.length > 0 ? rawText : undefined;
|
|
4673
4673
|
}
|
|
4674
4674
|
// src/version.ts
|
|
4675
|
-
var SDK_VERSION = "0.
|
|
4675
|
+
var SDK_VERSION = "0.23.0";
|
|
4676
4676
|
|
|
4677
4677
|
// src/clients/base.ts
|
|
4678
4678
|
class PlaycademyBaseClient {
|
|
@@ -4702,16 +4702,16 @@ class PlaycademyBaseClient {
|
|
|
4702
4702
|
}
|
|
4703
4703
|
getBaseUrl() {
|
|
4704
4704
|
const isRelative = this.baseUrl.startsWith("/");
|
|
4705
|
-
const
|
|
4706
|
-
return isRelative &&
|
|
4705
|
+
const isBrowser = typeof globalThis.window !== "undefined";
|
|
4706
|
+
return isRelative && isBrowser ? `${globalThis.location.origin}${this.baseUrl}` : this.baseUrl;
|
|
4707
4707
|
}
|
|
4708
4708
|
getGameBackendUrl() {
|
|
4709
4709
|
if (!this.gameUrl) {
|
|
4710
4710
|
throw new PlaycademyError("Game backend URL not configured. gameUrl must be set to use game backend features.");
|
|
4711
4711
|
}
|
|
4712
4712
|
const isRelative = this.gameUrl.startsWith("/");
|
|
4713
|
-
const
|
|
4714
|
-
const effectiveGameUrl = isRelative &&
|
|
4713
|
+
const isBrowser = typeof globalThis.window !== "undefined";
|
|
4714
|
+
const effectiveGameUrl = isRelative && isBrowser ? `${globalThis.location.origin}${this.gameUrl}` : this.gameUrl;
|
|
4715
4715
|
return `${effectiveGameUrl}/api`;
|
|
4716
4716
|
}
|
|
4717
4717
|
ping() {
|
|
@@ -4868,16 +4868,16 @@ class PlaycademyInternalClient extends PlaycademyBaseClient {
|
|
|
4868
4868
|
static identity = identity;
|
|
4869
4869
|
}
|
|
4870
4870
|
export {
|
|
4871
|
-
|
|
4872
|
-
isTrustedIframeMessage,
|
|
4873
|
-
extractApiErrorInfo,
|
|
4874
|
-
beginInitHandshake,
|
|
4875
|
-
PlaycademyInternalClient,
|
|
4876
|
-
PlaycademyError,
|
|
4877
|
-
PlaycademyInternalClient as PlaycademyClient,
|
|
4878
|
-
MessageEvents,
|
|
4879
|
-
INIT_WAIT_TIMEOUT_MS,
|
|
4880
|
-
HANDSHAKE_RESEND_INTERVAL_MS,
|
|
4871
|
+
ApiError,
|
|
4881
4872
|
HANDSHAKE_MAX_DURATION_MS,
|
|
4882
|
-
|
|
4873
|
+
HANDSHAKE_RESEND_INTERVAL_MS,
|
|
4874
|
+
INIT_WAIT_TIMEOUT_MS,
|
|
4875
|
+
MessageEvents,
|
|
4876
|
+
PlaycademyInternalClient as PlaycademyClient,
|
|
4877
|
+
PlaycademyError,
|
|
4878
|
+
PlaycademyInternalClient,
|
|
4879
|
+
beginInitHandshake,
|
|
4880
|
+
extractApiErrorInfo,
|
|
4881
|
+
isTrustedIframeMessage,
|
|
4882
|
+
messaging
|
|
4883
4883
|
};
|
package/dist/server/edge.js
CHANGED
|
@@ -1,15 +1,3 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __export = (target, all) => {
|
|
3
|
-
for (var name in all)
|
|
4
|
-
__defProp(target, name, {
|
|
5
|
-
get: all[name],
|
|
6
|
-
enumerable: true,
|
|
7
|
-
configurable: true,
|
|
8
|
-
set: (newValue) => all[name] = () => newValue
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
|
-
|
|
13
1
|
// ../constants/src/auth.ts
|
|
14
2
|
var DEFAULT_PERSONAL_API_KEY_PERMISSIONS = {
|
|
15
3
|
games: ["read", "write", "delete"],
|
|
@@ -67,8 +55,8 @@ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
|
|
|
67
55
|
function normalizeSkillId(skill) {
|
|
68
56
|
return skill.trim().toLowerCase();
|
|
69
57
|
}
|
|
70
|
-
function normalizeSkillIds(
|
|
71
|
-
const normalized = [...new Set(
|
|
58
|
+
function normalizeSkillIds(skills, max) {
|
|
59
|
+
const normalized = [...new Set(skills.map(normalizeSkillId).filter((skill) => skill.length > 0))];
|
|
72
60
|
if (normalized.length === 0) {
|
|
73
61
|
return { ok: false, error: "At least one skill is required" };
|
|
74
62
|
}
|
|
@@ -370,7 +358,7 @@ function extractApiErrorInfo(error) {
|
|
|
370
358
|
}
|
|
371
359
|
|
|
372
360
|
// src/version.ts
|
|
373
|
-
var SDK_VERSION = "0.
|
|
361
|
+
var SDK_VERSION = "0.23.0";
|
|
374
362
|
|
|
375
363
|
// src/server/request.ts
|
|
376
364
|
async function makeApiRequest(opts) {
|
|
@@ -525,6 +513,6 @@ Please set the PLAYCADEMY_BASE_URL environment variable`);
|
|
|
525
513
|
}
|
|
526
514
|
}
|
|
527
515
|
export {
|
|
528
|
-
|
|
529
|
-
|
|
516
|
+
PlaycademyClient,
|
|
517
|
+
verifyGameToken
|
|
530
518
|
};
|
package/dist/server.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
var
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
var __esm = (fn, res, err) => () => {
|
|
2
|
+
if (fn)
|
|
3
|
+
try {
|
|
4
|
+
res = fn(fn = 0);
|
|
5
|
+
} catch (e) {
|
|
6
|
+
err = [e];
|
|
7
|
+
}
|
|
8
|
+
if (err)
|
|
9
|
+
throw err[0];
|
|
10
|
+
return res;
|
|
10
11
|
};
|
|
11
|
-
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
12
12
|
// ../utils/src/file-loader.ts
|
|
13
13
|
import { existsSync, readdirSync, statSync } from "fs";
|
|
14
14
|
import { readFile } from "fs/promises";
|
|
@@ -118,11 +118,6 @@ function stripJsonComments(jsonc) {
|
|
|
118
118
|
var init_file_loader = () => {};
|
|
119
119
|
|
|
120
120
|
// src/server/utils/config-loader.ts
|
|
121
|
-
var exports_config_loader = {};
|
|
122
|
-
__export(exports_config_loader, {
|
|
123
|
-
loadConfig: () => loadConfig,
|
|
124
|
-
findConfigPath: () => findConfigPath
|
|
125
|
-
});
|
|
126
121
|
import { resolve as resolve2 } from "path";
|
|
127
122
|
async function findConfigPath(configPath) {
|
|
128
123
|
if (configPath) {
|
|
@@ -256,8 +251,8 @@ var TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE = {
|
|
|
256
251
|
function normalizeSkillId(skill) {
|
|
257
252
|
return skill.trim().toLowerCase();
|
|
258
253
|
}
|
|
259
|
-
function normalizeSkillIds(
|
|
260
|
-
const normalized = [...new Set(
|
|
254
|
+
function normalizeSkillIds(skills, max) {
|
|
255
|
+
const normalized = [...new Set(skills.map(normalizeSkillId).filter((skill) => skill.length > 0))];
|
|
261
256
|
if (normalized.length === 0) {
|
|
262
257
|
return { ok: false, error: "At least one skill is required" };
|
|
263
258
|
}
|
|
@@ -559,7 +554,7 @@ function extractApiErrorInfo(error) {
|
|
|
559
554
|
}
|
|
560
555
|
|
|
561
556
|
// src/version.ts
|
|
562
|
-
var SDK_VERSION = "0.
|
|
557
|
+
var SDK_VERSION = "0.23.0";
|
|
563
558
|
|
|
564
559
|
// src/server/request.ts
|
|
565
560
|
async function makeApiRequest(opts) {
|
|
@@ -660,8 +655,8 @@ class PlaycademyClient2 extends PlaycademyClient {
|
|
|
660
655
|
if (config.config) {
|
|
661
656
|
return super.init(config);
|
|
662
657
|
}
|
|
663
|
-
|
|
664
|
-
const loadedConfig = await
|
|
658
|
+
await Promise.resolve().then(() => init_config_loader());
|
|
659
|
+
const loadedConfig = await loadConfig(config.configPath);
|
|
665
660
|
return super.init({ ...config, config: loadedConfig });
|
|
666
661
|
}
|
|
667
662
|
}
|
|
@@ -726,6 +721,6 @@ Please set the PLAYCADEMY_BASE_URL environment variable`);
|
|
|
726
721
|
}
|
|
727
722
|
}
|
|
728
723
|
export {
|
|
729
|
-
|
|
730
|
-
|
|
724
|
+
PlaycademyClient2 as PlaycademyClient,
|
|
725
|
+
verifyGameToken
|
|
731
726
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -4432,7 +4432,7 @@ interface PlatformTimebackUserContext {
|
|
|
4432
4432
|
*/
|
|
4433
4433
|
interface PlatformTimebackUser extends PlatformTimebackUserContext {
|
|
4434
4434
|
/**
|
|
4435
|
-
* Fetch TimeBack data from the server (cached for
|
|
4435
|
+
* Fetch TimeBack data from the server (cached for 30 sec).
|
|
4436
4436
|
* Updates the cached values so subsequent property access returns fresh data.
|
|
4437
4437
|
* @param options - Cache options (pass { force: true } to bypass cache)
|
|
4438
4438
|
* @returns Promise resolving to user context with full enrollment data
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcademy/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"typescript": "^5.7.2"
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
|
-
"drizzle-orm": "^0.
|
|
79
|
+
"drizzle-orm": "^0.45.3",
|
|
80
80
|
"typescript": "^5 || ^6"
|
|
81
81
|
}
|
|
82
82
|
}
|