@playcademy/sdk 0.2.2 → 0.2.4

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/internal.js CHANGED
@@ -241,6 +241,7 @@ var detectOutputFormat = () => {
241
241
  };
242
242
  var colors = {
243
243
  reset: "\x1B[0m",
244
+ bold: "\x1B[1m",
244
245
  dim: "\x1B[2m",
245
246
  red: "\x1B[31m",
246
247
  yellow: "\x1B[33m",
@@ -262,44 +263,48 @@ var getLevelColor = (level) => {
262
263
  return colors.reset;
263
264
  }
264
265
  };
265
- var formatBrowserOutput = (level, message, context) => {
266
+ var formatBrowserOutput = (level, message, context, scope) => {
266
267
  const timestamp = new Date().toISOString();
267
268
  const levelUpper = level.toUpperCase();
268
269
  const consoleMethod = getConsoleMethod(level);
270
+ const scopePrefix = scope ? `[${scope}] ` : "";
269
271
  if (context && Object.keys(context).length > 0) {
270
- consoleMethod(`[${timestamp}] ${levelUpper}`, message, context);
272
+ consoleMethod(`[${timestamp}] ${levelUpper}`, `${scopePrefix}${message}`, context);
271
273
  } else {
272
- consoleMethod(`[${timestamp}] ${levelUpper}`, message);
274
+ consoleMethod(`[${timestamp}] ${levelUpper}`, `${scopePrefix}${message}`);
273
275
  }
274
276
  };
275
- var formatColorTTY = (level, message, context) => {
277
+ var formatColorTTY = (level, message, context, scope) => {
276
278
  const timestamp = new Date().toISOString();
277
279
  const levelColor = getLevelColor(level);
278
280
  const levelUpper = level.toUpperCase().padEnd(5);
279
281
  const consoleMethod = getConsoleMethod(level);
280
282
  const coloredPrefix = `${colors.dim}[${timestamp}]${colors.reset} ${levelColor}${levelUpper}${colors.reset}`;
283
+ const scopePrefix = scope ? `${colors.bold}[${scope}]${colors.reset} ` : "";
281
284
  if (context && Object.keys(context).length > 0) {
282
- consoleMethod(`${coloredPrefix} ${message}`, context);
285
+ consoleMethod(`${coloredPrefix} ${scopePrefix}${message}`, context);
283
286
  } else {
284
- consoleMethod(`${coloredPrefix} ${message}`);
287
+ consoleMethod(`${coloredPrefix} ${scopePrefix}${message}`);
285
288
  }
286
289
  };
287
- var formatJSONSingleLine = (level, message, context) => {
290
+ var formatJSONSingleLine = (level, message, context, scope) => {
288
291
  const timestamp = new Date().toISOString();
289
292
  const logEntry = {
290
293
  timestamp,
291
294
  level: level.toUpperCase(),
295
+ ...scope && { scope },
292
296
  message,
293
297
  ...context && Object.keys(context).length > 0 && { context }
294
298
  };
295
299
  const consoleMethod = getConsoleMethod(level);
296
300
  consoleMethod(JSON.stringify(logEntry));
297
301
  };
298
- var formatJSONPretty = (level, message, context) => {
302
+ var formatJSONPretty = (level, message, context, scope) => {
299
303
  const timestamp = new Date().toISOString();
300
304
  const logEntry = {
301
305
  timestamp,
302
306
  level: level.toUpperCase(),
307
+ ...scope && { scope },
303
308
  message,
304
309
  ...context && Object.keys(context).length > 0 && { context }
305
310
  };
@@ -340,36 +345,37 @@ var shouldLog = (level) => {
340
345
  return levelPriority[level] >= levelPriority[minLevel];
341
346
  };
342
347
  var customHandler;
343
- var performLog = (level, message, context) => {
348
+ var performLog = (level, message, context, scope) => {
344
349
  if (!shouldLog(level))
345
350
  return;
346
351
  if (customHandler) {
347
- customHandler(level, message, context);
352
+ customHandler(level, message, context, scope);
348
353
  return;
349
354
  }
350
355
  const outputFormat = detectOutputFormat();
351
356
  switch (outputFormat) {
352
357
  case "browser":
353
- formatBrowserOutput(level, message, context);
358
+ formatBrowserOutput(level, message, context, scope);
354
359
  break;
355
360
  case "color-tty":
356
- formatColorTTY(level, message, context);
361
+ formatColorTTY(level, message, context, scope);
357
362
  break;
358
363
  case "json-single-line":
359
- formatJSONSingleLine(level, message, context);
364
+ formatJSONSingleLine(level, message, context, scope);
360
365
  break;
361
366
  case "json-pretty":
362
- formatJSONPretty(level, message, context);
367
+ formatJSONPretty(level, message, context, scope);
363
368
  break;
364
369
  }
365
370
  };
366
- var createLogger = () => {
371
+ var createLogger = (scopeName) => {
367
372
  return {
368
- debug: (message, context) => performLog("debug", message, context),
369
- info: (message, context) => performLog("info", message, context),
370
- warn: (message, context) => performLog("warn", message, context),
371
- error: (message, context) => performLog("error", message, context),
372
- log: performLog
373
+ debug: (message, context) => performLog("debug", message, context, scopeName),
374
+ info: (message, context) => performLog("info", message, context, scopeName),
375
+ warn: (message, context) => performLog("warn", message, context, scopeName),
376
+ error: (message, context) => performLog("error", message, context, scopeName),
377
+ log: (level, message, context) => performLog(level, message, context, scopeName),
378
+ scope: (name) => createLogger(scopeName ? `${scopeName}.${name}` : name)
373
379
  };
374
380
  };
375
381
  var log = createLogger();
@@ -384,26 +390,87 @@ class PlaycademyError extends Error {
384
390
 
385
391
  class ApiError extends Error {
386
392
  status;
393
+ code;
387
394
  details;
388
- constructor(status, message, details) {
389
- super(`${status} ${message}`);
395
+ rawBody;
396
+ constructor(status, code, message, details, rawBody) {
397
+ super(message);
390
398
  this.status = status;
399
+ this.name = "ApiError";
400
+ this.code = code;
391
401
  this.details = details;
402
+ this.rawBody = rawBody;
392
403
  Object.setPrototypeOf(this, ApiError.prototype);
393
404
  }
405
+ static fromResponse(status, statusText, body) {
406
+ if (body && typeof body === "object" && "error" in body) {
407
+ const errorBody = body;
408
+ const err = errorBody.error;
409
+ if (err && typeof err === "object") {
410
+ return new ApiError(status, err.code ?? statusCodeToErrorCode(status), err.message ?? statusText, err.details, body);
411
+ }
412
+ }
413
+ return new ApiError(status, statusCodeToErrorCode(status), statusText, undefined, body);
414
+ }
415
+ is(code) {
416
+ return this.code === code;
417
+ }
418
+ isClientError() {
419
+ return this.status >= 400 && this.status < 500;
420
+ }
421
+ isServerError() {
422
+ return this.status >= 500;
423
+ }
424
+ isRetryable() {
425
+ return this.isServerError() || this.code === "TOO_MANY_REQUESTS" || this.code === "RATE_LIMITED" || this.code === "TIMEOUT";
426
+ }
427
+ }
428
+ function statusCodeToErrorCode(status) {
429
+ switch (status) {
430
+ case 400:
431
+ return "BAD_REQUEST";
432
+ case 401:
433
+ return "UNAUTHORIZED";
434
+ case 403:
435
+ return "FORBIDDEN";
436
+ case 404:
437
+ return "NOT_FOUND";
438
+ case 405:
439
+ return "METHOD_NOT_ALLOWED";
440
+ case 409:
441
+ return "CONFLICT";
442
+ case 410:
443
+ return "GONE";
444
+ case 412:
445
+ return "PRECONDITION_FAILED";
446
+ case 413:
447
+ return "PAYLOAD_TOO_LARGE";
448
+ case 422:
449
+ return "VALIDATION_FAILED";
450
+ case 429:
451
+ return "TOO_MANY_REQUESTS";
452
+ case 500:
453
+ return "INTERNAL_ERROR";
454
+ case 501:
455
+ return "NOT_IMPLEMENTED";
456
+ case 503:
457
+ return "SERVICE_UNAVAILABLE";
458
+ case 504:
459
+ return "TIMEOUT";
460
+ default:
461
+ return status >= 500 ? "INTERNAL_ERROR" : "BAD_REQUEST";
462
+ }
394
463
  }
395
464
  function extractApiErrorInfo(error) {
396
465
  if (!(error instanceof ApiError)) {
397
466
  return null;
398
467
  }
399
- const info = {
468
+ return {
400
469
  status: error.status,
401
- statusText: error.message
470
+ code: error.code,
471
+ message: error.message,
472
+ ...error.details !== undefined && { details: error.details }
402
473
  };
403
- if (error.details) {
404
- info.details = error.details;
405
- }
406
- return info;
407
474
  }
408
475
 
409
476
  // src/core/static/login.ts
@@ -1030,6 +1097,96 @@ function createUsersNamespace(client) {
1030
1097
  }
1031
1098
  };
1032
1099
  }
1100
+ // ../constants/src/achievements.ts
1101
+ var ACHIEVEMENT_IDS = {
1102
+ PLAY_ANY_GAME_DAILY: "play_any_game_daily",
1103
+ DAILY_CHEST_OPEN: "daily_chest_open",
1104
+ LEADERBOARD_TOP3_DAILY: "leaderboard_top3_daily",
1105
+ LEADERBOARD_TOP3_WEEKLY: "leaderboard_top3_weekly",
1106
+ FIRST_SCORE_ANY_GAME: "first_score_any_game",
1107
+ PERSONAL_BEST_ANY_GAME: "personal_best_any_game"
1108
+ };
1109
+ var ACHIEVEMENT_DEFINITIONS = [
1110
+ {
1111
+ id: ACHIEVEMENT_IDS.PLAY_ANY_GAME_DAILY,
1112
+ title: "Play any game",
1113
+ description: "Play any arcade game for at least 60 seconds in a single session.",
1114
+ scope: "daily",
1115
+ rewardCredits: 10,
1116
+ limit: 1,
1117
+ completionType: "time_played_session",
1118
+ completionConfig: { minSeconds: 60 },
1119
+ target: { anyArcadeGame: true },
1120
+ active: true
1121
+ },
1122
+ {
1123
+ id: ACHIEVEMENT_IDS.DAILY_CHEST_OPEN,
1124
+ title: "Opened the daily chest",
1125
+ description: "Find the chest on the map and open it to claim your reward.",
1126
+ scope: "daily",
1127
+ rewardCredits: 10,
1128
+ limit: 1,
1129
+ completionType: "interaction",
1130
+ completionConfig: { triggerId: "bunny_chest" },
1131
+ target: { map: "arcade" },
1132
+ active: true
1133
+ },
1134
+ {
1135
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_DAILY,
1136
+ title: "Daily Champion",
1137
+ description: "Finish in the top 3 of any game leaderboard for the day.",
1138
+ scope: "daily",
1139
+ rewardCredits: 25,
1140
+ limit: 1,
1141
+ completionType: "leaderboard_rank",
1142
+ completionConfig: {
1143
+ rankRewards: [50, 30, 15]
1144
+ },
1145
+ target: { anyArcadeGame: true },
1146
+ active: true
1147
+ },
1148
+ {
1149
+ id: ACHIEVEMENT_IDS.LEADERBOARD_TOP3_WEEKLY,
1150
+ title: "Weekly Legend",
1151
+ description: "Finish in the top 3 of any game leaderboard for the week.",
1152
+ scope: "weekly",
1153
+ rewardCredits: 50,
1154
+ limit: 1,
1155
+ completionType: "leaderboard_rank",
1156
+ completionConfig: {
1157
+ rankRewards: [100, 60, 30]
1158
+ },
1159
+ target: { anyArcadeGame: true },
1160
+ active: true
1161
+ },
1162
+ {
1163
+ id: ACHIEVEMENT_IDS.FIRST_SCORE_ANY_GAME,
1164
+ title: "First Score",
1165
+ description: "Submit your first score in any arcade game.",
1166
+ scope: "game",
1167
+ rewardCredits: 5,
1168
+ limit: 1,
1169
+ completionType: "first_score",
1170
+ completionConfig: {},
1171
+ target: { anyArcadeGame: true },
1172
+ active: true
1173
+ },
1174
+ {
1175
+ id: ACHIEVEMENT_IDS.PERSONAL_BEST_ANY_GAME,
1176
+ title: "Personal Best",
1177
+ description: "Beat your personal best score in any arcade game.",
1178
+ scope: "daily",
1179
+ rewardCredits: 5,
1180
+ limit: 3,
1181
+ completionType: "personal_best",
1182
+ completionConfig: {},
1183
+ target: { anyArcadeGame: true },
1184
+ active: true
1185
+ }
1186
+ ];
1187
+ // ../constants/src/typescript.ts
1188
+ var TSC_PACKAGE = "@typescript/native-preview";
1189
+ var USE_NATIVE_TSC = TSC_PACKAGE.includes("native-preview");
1033
1190
  // ../constants/src/overworld.ts
1034
1191
  var ITEM_SLUGS = {
1035
1192
  PLAYCADEMY_CREDITS: "PLAYCADEMY_CREDITS",
@@ -1407,7 +1564,9 @@ function createAdminNamespace(client) {
1407
1564
  create: (props) => client["request"]("/currencies", "POST", { body: props }),
1408
1565
  get: (currencyId) => client["request"](`/currencies/${currencyId}`, "GET"),
1409
1566
  list: () => client["request"]("/currencies", "GET"),
1410
- update: (currencyId, props) => client["request"](`/currencies/${currencyId}`, "PATCH", { body: props }),
1567
+ update: (currencyId, props) => client["request"](`/currencies/${currencyId}`, "PATCH", {
1568
+ body: props
1569
+ }),
1411
1570
  delete: (currencyId) => client["request"](`/currencies/${currencyId}`, "DELETE")
1412
1571
  },
1413
1572
  shopListings: {
@@ -1541,16 +1700,7 @@ function createDevNamespace(client) {
1541
1700
  let sawAnyServerEvent = false;
1542
1701
  const reader = finalizeResponse.body.pipeThrough(new TextDecoderStream).getReader();
1543
1702
  let buffer = "";
1544
- while (true) {
1545
- const { done, value } = await reader.read();
1546
- if (done) {
1547
- if (!sawAnyServerEvent) {
1548
- hooks?.onClose?.();
1549
- hooks?.onEvent?.({ type: "close" });
1550
- }
1551
- break;
1552
- }
1553
- buffer += value;
1703
+ const processBuffer = () => {
1554
1704
  let eolIndex;
1555
1705
  while ((eolIndex = buffer.indexOf(`
1556
1706
 
@@ -1583,19 +1733,34 @@ function createDevNamespace(client) {
1583
1733
  break;
1584
1734
  }
1585
1735
  case "complete": {
1736
+ hooks?.onEvent?.({ type: "complete" });
1586
1737
  reader.cancel();
1587
1738
  return eventData;
1588
1739
  }
1589
1740
  case "error": {
1590
1741
  reader.cancel();
1591
- const status = typeof eventData.status === "number" ? eventData.status : 500;
1592
- const message2 = eventData.message ?? "Deployment error";
1593
- const details = eventData.details;
1594
- throw new ApiError(status, message2, details);
1742
+ throw ApiError.fromResponse(eventData?.error?.status ?? 500, "Deployment error", eventData);
1595
1743
  }
1596
1744
  }
1597
1745
  }
1598
1746
  }
1747
+ return null;
1748
+ };
1749
+ while (true) {
1750
+ const { done, value } = await reader.read();
1751
+ if (value) {
1752
+ buffer += value;
1753
+ }
1754
+ const result = processBuffer();
1755
+ if (result)
1756
+ return result;
1757
+ if (done) {
1758
+ if (!sawAnyServerEvent) {
1759
+ hooks?.onClose?.();
1760
+ hooks?.onEvent?.({ type: "close" });
1761
+ }
1762
+ break;
1763
+ }
1599
1764
  }
1600
1765
  throw new Error("Deployment completed but no final game data received");
1601
1766
  }
@@ -1644,16 +1809,11 @@ function createDevNamespace(client) {
1644
1809
  get: async (slug, key) => {
1645
1810
  const res = await client["request"](`/games/${slug}/bucket/${encodeURIComponent(key)}`, "GET", { raw: true });
1646
1811
  if (!res.ok) {
1647
- let errorMessage = res.statusText;
1812
+ let errorBody;
1648
1813
  try {
1649
- const errorData = await res.json();
1650
- if (errorData.error) {
1651
- errorMessage = errorData.error;
1652
- } else if (errorData.message) {
1653
- errorMessage = errorData.message;
1654
- }
1814
+ errorBody = await res.json();
1655
1815
  } catch {}
1656
- throw new Error(errorMessage);
1816
+ throw ApiError.fromResponse(res.status, res.statusText, errorBody);
1657
1817
  }
1658
1818
  return res.arrayBuffer();
1659
1819
  },
@@ -1684,6 +1844,11 @@ function createDevNamespace(client) {
1684
1844
  delete: async (slug, hostname) => {
1685
1845
  await client["request"](`/games/${slug}/domains/${hostname}`, "DELETE");
1686
1846
  }
1847
+ },
1848
+ logs: {
1849
+ getToken: async (slug, environment) => {
1850
+ return client["request"](`/games/${slug}/logs/token`, "POST", { body: { environment } });
1851
+ }
1687
1852
  }
1688
1853
  },
1689
1854
  items: {
@@ -1787,7 +1952,7 @@ async function request({
1787
1952
  const errorBody = await clonedRes.json().catch(() => clonedRes.text().catch(() => {
1788
1953
  return;
1789
1954
  })) ?? undefined;
1790
- throw new ApiError(res.status, res.statusText, errorBody);
1955
+ throw ApiError.fromResponse(res.status, res.statusText, errorBody);
1791
1956
  }
1792
1957
  if (res.status === 204)
1793
1958
  return;
@@ -1841,7 +2006,7 @@ function createGamesNamespace(client) {
1841
2006
  const promise = client["request"](`/games/${gameIdOrSlug}`, "GET");
1842
2007
  return gameFetchCache.get(gameIdOrSlug, async () => {
1843
2008
  const baseGameData = await promise;
1844
- if (baseGameData.gameType === "hosted" && baseGameData.deploymentUrl !== null && baseGameData.deploymentUrl !== "") {
2009
+ if (baseGameData.gameType === "hosted" && baseGameData.deploymentUrl !== null && baseGameData.deploymentUrl !== undefined && baseGameData.deploymentUrl !== "") {
1845
2010
  const manifestData = await fetchManifest(baseGameData.deploymentUrl);
1846
2011
  return { ...baseGameData, manifest: manifestData };
1847
2012
  }
@@ -1851,14 +2016,6 @@ function createGamesNamespace(client) {
1851
2016
  list: (options) => {
1852
2017
  return gamesListCache.get("all", () => client["request"]("/games", "GET"), options);
1853
2018
  },
1854
- saveState: async (state) => {
1855
- const gameId = client["_ensureGameId"]();
1856
- await client["request"](`/games/${gameId}/state`, "POST", { body: state });
1857
- },
1858
- loadState: async () => {
1859
- const gameId = client["_ensureGameId"]();
1860
- return client["request"](`/games/${gameId}/state`, "GET");
1861
- },
1862
2019
  startSession: async (gameId) => {
1863
2020
  const idToUse = gameId ?? client["_ensureGameId"]();
1864
2021
  return client["request"](`/games/${idToUse}/sessions`, "POST", {});
@@ -1914,7 +2071,9 @@ function createCharacterNamespace(client) {
1914
2071
  }
1915
2072
  },
1916
2073
  create: async (characterData) => {
1917
- return client["request"]("/character", "POST", { body: characterData });
2074
+ return client["request"]("/character", "POST", {
2075
+ body: characterData
2076
+ });
1918
2077
  },
1919
2078
  update: async (updates) => {
1920
2079
  return client["request"]("/character", "PATCH", { body: updates });
@@ -1939,8 +2098,8 @@ function createCharacterNamespace(client) {
1939
2098
  return client["request"](`/character/accessories/${slot}`, "DELETE");
1940
2099
  },
1941
2100
  list: async () => {
1942
- const character = await client.character.get();
1943
- return character?.accessories || [];
2101
+ const character2 = await client.character.get();
2102
+ return character2?.accessories || [];
1944
2103
  }
1945
2104
  }
1946
2105
  };