@playcademy/sdk 0.1.12 → 0.1.14
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/index.d.ts +319 -101
- package/dist/index.js +144 -37
- package/dist/server.d.ts +3 -1
- package/dist/types.d.ts +299 -100
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -123,9 +123,13 @@ var isBrowser = () => {
|
|
|
123
123
|
return false;
|
|
124
124
|
const minLevel = getMinimumLogLevel();
|
|
125
125
|
return levelPriority[level] >= levelPriority[minLevel];
|
|
126
|
-
}, performLog = (level, message, context) => {
|
|
126
|
+
}, customHandler, performLog = (level, message, context) => {
|
|
127
127
|
if (!shouldLog(level))
|
|
128
128
|
return;
|
|
129
|
+
if (customHandler) {
|
|
130
|
+
customHandler(level, message, context);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
129
133
|
const outputFormat = detectOutputFormat();
|
|
130
134
|
switch (outputFormat) {
|
|
131
135
|
case "browser":
|
|
@@ -278,6 +282,28 @@ function isInIframe() {
|
|
|
278
282
|
}
|
|
279
283
|
|
|
280
284
|
// src/core/errors.ts
|
|
285
|
+
function extractApiErrorInfo(error) {
|
|
286
|
+
if (!(error instanceof ApiError)) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const info = {
|
|
290
|
+
status: error.status,
|
|
291
|
+
statusText: error.message
|
|
292
|
+
};
|
|
293
|
+
if (error.details && typeof error.details === "object") {
|
|
294
|
+
const details = error.details;
|
|
295
|
+
if ("error" in details && typeof details.error === "string") {
|
|
296
|
+
info.error = details.error;
|
|
297
|
+
}
|
|
298
|
+
if ("message" in details && typeof details.message === "string") {
|
|
299
|
+
info.message = details.message;
|
|
300
|
+
}
|
|
301
|
+
if (!info.error && !info.message) {
|
|
302
|
+
info.details = error.details;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
return info;
|
|
306
|
+
}
|
|
281
307
|
var PlaycademyError, ApiError;
|
|
282
308
|
var init_errors = __esm(() => {
|
|
283
309
|
PlaycademyError = class PlaycademyError extends Error {
|
|
@@ -755,7 +781,7 @@ function createRuntimeNamespace(client) {
|
|
|
755
781
|
const forwardKeys = Array.isArray(playcademyConfig?.forwardKeys) ? playcademyConfig.forwardKeys : ["Escape"];
|
|
756
782
|
const keySet = new Set(forwardKeys.map((k) => k.toLowerCase()));
|
|
757
783
|
const keyListener = (event) => {
|
|
758
|
-
if (keySet.has(event.key
|
|
784
|
+
if (keySet.has(event.key?.toLowerCase() ?? "") || keySet.has(event.code?.toLowerCase() ?? "")) {
|
|
759
785
|
messaging.send("PLAYCADEMY_KEY_EVENT" /* KEY_EVENT */, {
|
|
760
786
|
key: event.key,
|
|
761
787
|
code: event.code,
|
|
@@ -839,6 +865,50 @@ function createRuntimeNamespace(client) {
|
|
|
839
865
|
counts[eventType] = handlers.size;
|
|
840
866
|
}
|
|
841
867
|
return counts;
|
|
868
|
+
},
|
|
869
|
+
cdn: {
|
|
870
|
+
url(pathOrStrings, ...values) {
|
|
871
|
+
const gameUrl = client["initPayload"]?.gameUrl;
|
|
872
|
+
let path;
|
|
873
|
+
if (Array.isArray(pathOrStrings) && "raw" in pathOrStrings) {
|
|
874
|
+
const strings = pathOrStrings;
|
|
875
|
+
path = strings.reduce((acc, str, i) => {
|
|
876
|
+
return acc + str + (values[i] != null ? String(values[i]) : "");
|
|
877
|
+
}, "");
|
|
878
|
+
} else {
|
|
879
|
+
path = pathOrStrings;
|
|
880
|
+
}
|
|
881
|
+
if (!gameUrl) {
|
|
882
|
+
return path.startsWith("./") ? path : "./" + path;
|
|
883
|
+
}
|
|
884
|
+
const cleanPath = path.startsWith("./") ? path.slice(2) : path;
|
|
885
|
+
return gameUrl + cleanPath;
|
|
886
|
+
},
|
|
887
|
+
fetch: async (path, options) => {
|
|
888
|
+
const gameUrl = client["initPayload"]?.gameUrl;
|
|
889
|
+
if (!gameUrl) {
|
|
890
|
+
const relativePath = path.startsWith("./") ? path : "./" + path;
|
|
891
|
+
return fetch(relativePath, options);
|
|
892
|
+
}
|
|
893
|
+
const cleanPath = path.startsWith("./") ? path.slice(2) : path;
|
|
894
|
+
return fetch(gameUrl + cleanPath, options);
|
|
895
|
+
},
|
|
896
|
+
json: async (path) => {
|
|
897
|
+
const response = await client.runtime.cdn.fetch(path);
|
|
898
|
+
return response.json();
|
|
899
|
+
},
|
|
900
|
+
blob: async (path) => {
|
|
901
|
+
const response = await client.runtime.cdn.fetch(path);
|
|
902
|
+
return response.blob();
|
|
903
|
+
},
|
|
904
|
+
text: async (path) => {
|
|
905
|
+
const response = await client.runtime.cdn.fetch(path);
|
|
906
|
+
return response.text();
|
|
907
|
+
},
|
|
908
|
+
arrayBuffer: async (path) => {
|
|
909
|
+
const response = await client.runtime.cdn.fetch(path);
|
|
910
|
+
return response.arrayBuffer();
|
|
911
|
+
}
|
|
842
912
|
}
|
|
843
913
|
};
|
|
844
914
|
}
|
|
@@ -1003,8 +1073,8 @@ async function request({
|
|
|
1003
1073
|
const rawText = await res.text().catch(() => "");
|
|
1004
1074
|
return rawText && rawText.length > 0 ? rawText : undefined;
|
|
1005
1075
|
}
|
|
1006
|
-
async function fetchManifest(
|
|
1007
|
-
const manifestUrl = `${
|
|
1076
|
+
async function fetchManifest(deploymentUrl) {
|
|
1077
|
+
const manifestUrl = `${deploymentUrl.replace(/\/$/, "")}/playcademy.manifest.json`;
|
|
1008
1078
|
try {
|
|
1009
1079
|
const response = await fetch(manifestUrl);
|
|
1010
1080
|
if (!response.ok) {
|
|
@@ -1042,8 +1112,8 @@ function createGamesNamespace(client) {
|
|
|
1042
1112
|
const promise = client["request"](`/games/${gameIdOrSlug}`, "GET");
|
|
1043
1113
|
return gameFetchCache.get(gameIdOrSlug, async () => {
|
|
1044
1114
|
const baseGameData = await promise;
|
|
1045
|
-
if (baseGameData.gameType === "hosted" && baseGameData.
|
|
1046
|
-
const manifestData = await fetchManifest(baseGameData.
|
|
1115
|
+
if (baseGameData.gameType === "hosted" && baseGameData.deploymentUrl !== null && baseGameData.deploymentUrl !== "") {
|
|
1116
|
+
const manifestData = await fetchManifest(baseGameData.deploymentUrl);
|
|
1047
1117
|
return { ...baseGameData, manifest: manifestData };
|
|
1048
1118
|
}
|
|
1049
1119
|
return baseGameData;
|
|
@@ -1232,22 +1302,28 @@ function createDevNamespace(client) {
|
|
|
1232
1302
|
}
|
|
1233
1303
|
},
|
|
1234
1304
|
games: {
|
|
1235
|
-
deploy: {
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1305
|
+
deploy: async (slug, options) => {
|
|
1306
|
+
const { metadata, file, backend, hooks } = options;
|
|
1307
|
+
hooks?.onEvent?.({ type: "init" });
|
|
1308
|
+
let game;
|
|
1309
|
+
if (metadata) {
|
|
1310
|
+
game = await client["request"](`/games/${slug}`, "PUT", {
|
|
1239
1311
|
body: metadata
|
|
1240
1312
|
});
|
|
1241
|
-
if (metadata.gameType === "external"
|
|
1313
|
+
if (metadata.gameType === "external" && !file && !backend) {
|
|
1242
1314
|
return game;
|
|
1243
1315
|
}
|
|
1316
|
+
}
|
|
1317
|
+
let uploadToken;
|
|
1318
|
+
if (file) {
|
|
1244
1319
|
const fileName = file instanceof File ? file.name : "game.zip";
|
|
1245
1320
|
const initiateResponse = await client["request"]("/games/uploads/initiate/", "POST", {
|
|
1246
1321
|
body: {
|
|
1247
1322
|
fileName,
|
|
1248
|
-
gameId: game
|
|
1323
|
+
gameId: game?.id || slug
|
|
1249
1324
|
}
|
|
1250
1325
|
});
|
|
1326
|
+
uploadToken = initiateResponse.uploadToken;
|
|
1251
1327
|
if (hooks?.onEvent && typeof XMLHttpRequest !== "undefined") {
|
|
1252
1328
|
await new Promise((resolve, reject) => {
|
|
1253
1329
|
const xhr = new XMLHttpRequest;
|
|
@@ -1288,7 +1364,9 @@ function createDevNamespace(client) {
|
|
|
1288
1364
|
throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
|
|
1289
1365
|
}
|
|
1290
1366
|
}
|
|
1291
|
-
|
|
1367
|
+
}
|
|
1368
|
+
if (uploadToken || backend) {
|
|
1369
|
+
const deployUrl = `${client.baseUrl}/games/${slug}/deploy`;
|
|
1292
1370
|
const authToken = client.getToken();
|
|
1293
1371
|
const tokenType = client.getTokenType();
|
|
1294
1372
|
const headers = {
|
|
@@ -1301,25 +1379,33 @@ function createDevNamespace(client) {
|
|
|
1301
1379
|
headers["Authorization"] = `Bearer ${authToken}`;
|
|
1302
1380
|
}
|
|
1303
1381
|
}
|
|
1304
|
-
const
|
|
1382
|
+
const requestBody = {};
|
|
1383
|
+
if (uploadToken)
|
|
1384
|
+
requestBody.uploadToken = uploadToken;
|
|
1385
|
+
if (metadata)
|
|
1386
|
+
requestBody.metadata = metadata;
|
|
1387
|
+
if (backend) {
|
|
1388
|
+
requestBody.code = backend.code;
|
|
1389
|
+
requestBody.config = backend.config;
|
|
1390
|
+
if (backend.bindings)
|
|
1391
|
+
requestBody.bindings = backend.bindings;
|
|
1392
|
+
if (backend.schema)
|
|
1393
|
+
requestBody.schema = backend.schema;
|
|
1394
|
+
if (backend.secrets)
|
|
1395
|
+
requestBody.secrets = backend.secrets;
|
|
1396
|
+
}
|
|
1397
|
+
const finalizeResponse = await fetch(deployUrl, {
|
|
1305
1398
|
method: "POST",
|
|
1306
1399
|
headers,
|
|
1307
|
-
body: JSON.stringify(
|
|
1308
|
-
tempS3Key: initiateResponse.tempS3Key,
|
|
1309
|
-
gameId: initiateResponse.gameId,
|
|
1310
|
-
version: initiateResponse.version,
|
|
1311
|
-
slug,
|
|
1312
|
-
metadata,
|
|
1313
|
-
originalFileName: fileName
|
|
1314
|
-
}),
|
|
1400
|
+
body: JSON.stringify(requestBody),
|
|
1315
1401
|
credentials: "omit"
|
|
1316
1402
|
});
|
|
1317
1403
|
if (!finalizeResponse.ok) {
|
|
1318
1404
|
const errText = await finalizeResponse.text().catch(() => "");
|
|
1319
|
-
throw new Error(`
|
|
1405
|
+
throw new Error(`Deploy request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
|
|
1320
1406
|
}
|
|
1321
1407
|
if (!finalizeResponse.body) {
|
|
1322
|
-
throw new Error("
|
|
1408
|
+
throw new Error("Deploy response body missing");
|
|
1323
1409
|
}
|
|
1324
1410
|
hooks?.onEvent?.({ type: "finalizeStart" });
|
|
1325
1411
|
let sawAnyServerEvent = false;
|
|
@@ -1374,20 +1460,19 @@ function createDevNamespace(client) {
|
|
|
1374
1460
|
}
|
|
1375
1461
|
}
|
|
1376
1462
|
}
|
|
1377
|
-
throw new Error("
|
|
1378
|
-
},
|
|
1379
|
-
backend: async (slug, bundle) => {
|
|
1380
|
-
return client["request"](`/games/${slug}/backend/deploy`, "POST", { body: bundle });
|
|
1381
|
-
},
|
|
1382
|
-
seed: async (slug, code, environment) => {
|
|
1383
|
-
return client["request"](`/games/${slug}/seed`, "POST", {
|
|
1384
|
-
body: { code, environment }
|
|
1385
|
-
});
|
|
1463
|
+
throw new Error("Deployment completed but no final game data received");
|
|
1386
1464
|
}
|
|
1465
|
+
if (game) {
|
|
1466
|
+
return game;
|
|
1467
|
+
}
|
|
1468
|
+
throw new Error("No deployment actions specified (need metadata, file, or backend)");
|
|
1387
1469
|
},
|
|
1388
|
-
|
|
1389
|
-
return client
|
|
1470
|
+
seed: async (slug, code, environment) => {
|
|
1471
|
+
return client["request"](`/games/${slug}/seed`, "POST", {
|
|
1472
|
+
body: { code, environment }
|
|
1473
|
+
});
|
|
1390
1474
|
},
|
|
1475
|
+
upsert: async (slug, metadata) => client["request"](`/games/${slug}`, "PUT", { body: metadata }),
|
|
1391
1476
|
delete: (gameId) => client["request"](`/games/${gameId}`, "DELETE"),
|
|
1392
1477
|
secrets: {
|
|
1393
1478
|
set: async (slug, secrets) => {
|
|
@@ -1444,6 +1529,24 @@ function createDevNamespace(client) {
|
|
|
1444
1529
|
delete: async (slug, key) => {
|
|
1445
1530
|
await client["request"](`/games/${slug}/bucket/${encodeURIComponent(key)}`, "DELETE");
|
|
1446
1531
|
}
|
|
1532
|
+
},
|
|
1533
|
+
domains: {
|
|
1534
|
+
add: async (slug, hostname) => {
|
|
1535
|
+
return client["request"](`/games/${slug}/domains`, "POST", {
|
|
1536
|
+
body: { hostname }
|
|
1537
|
+
});
|
|
1538
|
+
},
|
|
1539
|
+
list: async (slug) => {
|
|
1540
|
+
const result = await client["request"](`/games/${slug}/domains`, "GET");
|
|
1541
|
+
return result.domains;
|
|
1542
|
+
},
|
|
1543
|
+
status: async (slug, hostname, refresh) => {
|
|
1544
|
+
const params = refresh ? "?refresh=true" : "";
|
|
1545
|
+
return client["request"](`/games/${slug}/domains/${hostname}${params}`, "GET");
|
|
1546
|
+
},
|
|
1547
|
+
delete: async (slug, hostname) => {
|
|
1548
|
+
await client["request"](`/games/${slug}/domains/${hostname}`, "DELETE");
|
|
1549
|
+
}
|
|
1447
1550
|
}
|
|
1448
1551
|
},
|
|
1449
1552
|
items: {
|
|
@@ -2493,7 +2596,7 @@ function createStandaloneConfig() {
|
|
|
2493
2596
|
console.warn("[Playcademy SDK] Standalone mode detected, creating mock context for local development");
|
|
2494
2597
|
const mockConfig = {
|
|
2495
2598
|
baseUrl: "http://localhost:4321",
|
|
2496
|
-
gameUrl:
|
|
2599
|
+
gameUrl: window.location.origin,
|
|
2497
2600
|
token: "mock-game-token-for-local-dev",
|
|
2498
2601
|
gameId: "mock-game-id-from-template",
|
|
2499
2602
|
realtimeUrl: undefined
|
|
@@ -2738,9 +2841,13 @@ var init_client = __esm(() => {
|
|
|
2738
2841
|
|
|
2739
2842
|
// src/index.ts
|
|
2740
2843
|
init_client();
|
|
2844
|
+
init_errors();
|
|
2741
2845
|
init_messaging();
|
|
2742
2846
|
export {
|
|
2743
2847
|
messaging,
|
|
2848
|
+
extractApiErrorInfo,
|
|
2849
|
+
PlaycademyError,
|
|
2744
2850
|
PlaycademyClient,
|
|
2745
|
-
MessageEvents
|
|
2851
|
+
MessageEvents,
|
|
2852
|
+
ApiError
|
|
2746
2853
|
};
|
package/dist/server.d.ts
CHANGED
|
@@ -54,6 +54,8 @@ interface IntegrationsConfig {
|
|
|
54
54
|
kv?: boolean;
|
|
55
55
|
/** Bucket storage (optional) */
|
|
56
56
|
bucket?: boolean;
|
|
57
|
+
/** Authentication (optional) */
|
|
58
|
+
auth?: boolean;
|
|
57
59
|
}
|
|
58
60
|
/**
|
|
59
61
|
* Unified Playcademy configuration
|
|
@@ -371,4 +373,4 @@ declare function verifyGameToken(gameToken: string, options?: {
|
|
|
371
373
|
}>;
|
|
372
374
|
|
|
373
375
|
export { PlaycademyClient, verifyGameToken };
|
|
374
|
-
export type { BackendDeploymentBundle, BackendResourceBindings, IntegrationsConfig, PlaycademyConfig, PlaycademyServerClientConfig, PlaycademyServerClientState, TimebackIntegrationConfig };
|
|
376
|
+
export type { BackendDeploymentBundle, BackendResourceBindings, IntegrationsConfig, PlaycademyConfig, PlaycademyServerClientConfig, PlaycademyServerClientState, TimebackIntegrationConfig, UserInfo };
|