@playcademy/sdk 0.1.13 → 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.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.toLowerCase()) || keySet.has(event.code.toLowerCase())) {
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,
@@ -842,7 +868,7 @@ function createRuntimeNamespace(client) {
842
868
  },
843
869
  cdn: {
844
870
  url(pathOrStrings, ...values) {
845
- const cdnBase = client["initPayload"]?.cdnBase;
871
+ const gameUrl = client["initPayload"]?.gameUrl;
846
872
  let path;
847
873
  if (Array.isArray(pathOrStrings) && "raw" in pathOrStrings) {
848
874
  const strings = pathOrStrings;
@@ -852,20 +878,20 @@ function createRuntimeNamespace(client) {
852
878
  } else {
853
879
  path = pathOrStrings;
854
880
  }
855
- if (!cdnBase) {
881
+ if (!gameUrl) {
856
882
  return path.startsWith("./") ? path : "./" + path;
857
883
  }
858
884
  const cleanPath = path.startsWith("./") ? path.slice(2) : path;
859
- return cdnBase + cleanPath;
885
+ return gameUrl + cleanPath;
860
886
  },
861
887
  fetch: async (path, options) => {
862
- const cdnBase = client["initPayload"]?.cdnBase;
863
- if (!cdnBase) {
888
+ const gameUrl = client["initPayload"]?.gameUrl;
889
+ if (!gameUrl) {
864
890
  const relativePath = path.startsWith("./") ? path : "./" + path;
865
891
  return fetch(relativePath, options);
866
892
  }
867
893
  const cleanPath = path.startsWith("./") ? path.slice(2) : path;
868
- return fetch(cdnBase + cleanPath, options);
894
+ return fetch(gameUrl + cleanPath, options);
869
895
  },
870
896
  json: async (path) => {
871
897
  const response = await client.runtime.cdn.fetch(path);
@@ -1047,8 +1073,8 @@ async function request({
1047
1073
  const rawText = await res.text().catch(() => "");
1048
1074
  return rawText && rawText.length > 0 ? rawText : undefined;
1049
1075
  }
1050
- async function fetchManifest(assetBundleBase) {
1051
- const manifestUrl = `${assetBundleBase.replace(/\/$/, "")}/playcademy.manifest.json`;
1076
+ async function fetchManifest(deploymentUrl) {
1077
+ const manifestUrl = `${deploymentUrl.replace(/\/$/, "")}/playcademy.manifest.json`;
1052
1078
  try {
1053
1079
  const response = await fetch(manifestUrl);
1054
1080
  if (!response.ok) {
@@ -1086,8 +1112,8 @@ function createGamesNamespace(client) {
1086
1112
  const promise = client["request"](`/games/${gameIdOrSlug}`, "GET");
1087
1113
  return gameFetchCache.get(gameIdOrSlug, async () => {
1088
1114
  const baseGameData = await promise;
1089
- if (baseGameData.gameType === "hosted" && baseGameData.assetBundleBase !== null && baseGameData.assetBundleBase !== "") {
1090
- const manifestData = await fetchManifest(baseGameData.assetBundleBase);
1115
+ if (baseGameData.gameType === "hosted" && baseGameData.deploymentUrl !== null && baseGameData.deploymentUrl !== "") {
1116
+ const manifestData = await fetchManifest(baseGameData.deploymentUrl);
1091
1117
  return { ...baseGameData, manifest: manifestData };
1092
1118
  }
1093
1119
  return baseGameData;
@@ -1276,22 +1302,28 @@ function createDevNamespace(client) {
1276
1302
  }
1277
1303
  },
1278
1304
  games: {
1279
- deploy: {
1280
- frontend: async (slug, metadata, file, hooks) => {
1281
- hooks?.onEvent?.({ type: "init" });
1282
- const game = await client["request"](`/games/${slug}`, "PUT", {
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", {
1283
1311
  body: metadata
1284
1312
  });
1285
- if (metadata.gameType === "external" || file === null) {
1313
+ if (metadata.gameType === "external" && !file && !backend) {
1286
1314
  return game;
1287
1315
  }
1316
+ }
1317
+ let uploadToken;
1318
+ if (file) {
1288
1319
  const fileName = file instanceof File ? file.name : "game.zip";
1289
1320
  const initiateResponse = await client["request"]("/games/uploads/initiate/", "POST", {
1290
1321
  body: {
1291
1322
  fileName,
1292
- gameId: game.id
1323
+ gameId: game?.id || slug
1293
1324
  }
1294
1325
  });
1326
+ uploadToken = initiateResponse.uploadToken;
1295
1327
  if (hooks?.onEvent && typeof XMLHttpRequest !== "undefined") {
1296
1328
  await new Promise((resolve, reject) => {
1297
1329
  const xhr = new XMLHttpRequest;
@@ -1332,7 +1364,9 @@ function createDevNamespace(client) {
1332
1364
  throw new Error(`File upload failed: ${uploadResponse.status} ${uploadResponse.statusText}`);
1333
1365
  }
1334
1366
  }
1335
- const finalizeUrl = `${client.baseUrl}/games/uploads/finalize/`;
1367
+ }
1368
+ if (uploadToken || backend) {
1369
+ const deployUrl = `${client.baseUrl}/games/${slug}/deploy`;
1336
1370
  const authToken = client.getToken();
1337
1371
  const tokenType = client.getTokenType();
1338
1372
  const headers = {
@@ -1345,25 +1379,33 @@ function createDevNamespace(client) {
1345
1379
  headers["Authorization"] = `Bearer ${authToken}`;
1346
1380
  }
1347
1381
  }
1348
- const finalizeResponse = await fetch(finalizeUrl, {
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, {
1349
1398
  method: "POST",
1350
1399
  headers,
1351
- body: JSON.stringify({
1352
- tempS3Key: initiateResponse.tempS3Key,
1353
- gameId: initiateResponse.gameId,
1354
- version: initiateResponse.version,
1355
- slug,
1356
- metadata,
1357
- originalFileName: fileName
1358
- }),
1400
+ body: JSON.stringify(requestBody),
1359
1401
  credentials: "omit"
1360
1402
  });
1361
1403
  if (!finalizeResponse.ok) {
1362
1404
  const errText = await finalizeResponse.text().catch(() => "");
1363
- throw new Error(`Finalize request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
1405
+ throw new Error(`Deploy request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
1364
1406
  }
1365
1407
  if (!finalizeResponse.body) {
1366
- throw new Error("Finalize response body missing");
1408
+ throw new Error("Deploy response body missing");
1367
1409
  }
1368
1410
  hooks?.onEvent?.({ type: "finalizeStart" });
1369
1411
  let sawAnyServerEvent = false;
@@ -1418,20 +1460,19 @@ function createDevNamespace(client) {
1418
1460
  }
1419
1461
  }
1420
1462
  }
1421
- throw new Error("Upload completed but no final game data received");
1422
- },
1423
- backend: async (slug, bundle) => {
1424
- return client["request"](`/games/${slug}/backend/deploy`, "POST", { body: bundle });
1425
- },
1426
- seed: async (slug, code, environment) => {
1427
- return client["request"](`/games/${slug}/seed`, "POST", {
1428
- body: { code, environment }
1429
- });
1463
+ throw new Error("Deployment completed but no final game data received");
1430
1464
  }
1465
+ if (game) {
1466
+ return game;
1467
+ }
1468
+ throw new Error("No deployment actions specified (need metadata, file, or backend)");
1431
1469
  },
1432
- upsert: async (slug, metadata) => {
1433
- return client.dev.games.deploy.frontend(slug, metadata, null);
1470
+ seed: async (slug, code, environment) => {
1471
+ return client["request"](`/games/${slug}/seed`, "POST", {
1472
+ body: { code, environment }
1473
+ });
1434
1474
  },
1475
+ upsert: async (slug, metadata) => client["request"](`/games/${slug}`, "PUT", { body: metadata }),
1435
1476
  delete: (gameId) => client["request"](`/games/${gameId}`, "DELETE"),
1436
1477
  secrets: {
1437
1478
  set: async (slug, secrets) => {
@@ -1488,6 +1529,24 @@ function createDevNamespace(client) {
1488
1529
  delete: async (slug, key) => {
1489
1530
  await client["request"](`/games/${slug}/bucket/${encodeURIComponent(key)}`, "DELETE");
1490
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
+ }
1491
1550
  }
1492
1551
  },
1493
1552
  items: {
@@ -2537,7 +2596,7 @@ function createStandaloneConfig() {
2537
2596
  console.warn("[Playcademy SDK] Standalone mode detected, creating mock context for local development");
2538
2597
  const mockConfig = {
2539
2598
  baseUrl: "http://localhost:4321",
2540
- gameUrl: "http://localhost:8788",
2599
+ gameUrl: window.location.origin,
2541
2600
  token: "mock-game-token-for-local-dev",
2542
2601
  gameId: "mock-game-id-from-template",
2543
2602
  realtimeUrl: undefined
@@ -2782,9 +2841,13 @@ var init_client = __esm(() => {
2782
2841
 
2783
2842
  // src/index.ts
2784
2843
  init_client();
2844
+ init_errors();
2785
2845
  init_messaging();
2786
2846
  export {
2787
2847
  messaging,
2848
+ extractApiErrorInfo,
2849
+ PlaycademyError,
2788
2850
  PlaycademyClient,
2789
- MessageEvents
2851
+ MessageEvents,
2852
+ ApiError
2790
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 };