@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.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,
@@ -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(assetBundleBase) {
1007
- const manifestUrl = `${assetBundleBase.replace(/\/$/, "")}/playcademy.manifest.json`;
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.assetBundleBase !== null && baseGameData.assetBundleBase !== "") {
1046
- const manifestData = await fetchManifest(baseGameData.assetBundleBase);
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
- frontend: async (slug, metadata, file, hooks) => {
1237
- hooks?.onEvent?.({ type: "init" });
1238
- 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", {
1239
1311
  body: metadata
1240
1312
  });
1241
- if (metadata.gameType === "external" || file === null) {
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.id
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
- const finalizeUrl = `${client.baseUrl}/games/uploads/finalize/`;
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 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, {
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(`Finalize request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
1405
+ throw new Error(`Deploy request failed: ${finalizeResponse.status} ${finalizeResponse.statusText}${errText ? ` - ${errText}` : ""}`);
1320
1406
  }
1321
1407
  if (!finalizeResponse.body) {
1322
- throw new Error("Finalize response body missing");
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("Upload completed but no final game data received");
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
- upsert: async (slug, metadata) => {
1389
- 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
+ });
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: "http://localhost:8788",
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 };