@playcademy/sandbox 0.3.16-beta.4 → 0.3.16-beta.6
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/cli.js +152 -4
- package/dist/server.js +152 -4
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1310,7 +1310,7 @@ var package_default;
|
|
|
1310
1310
|
var init_package = __esm(() => {
|
|
1311
1311
|
package_default = {
|
|
1312
1312
|
name: "@playcademy/sandbox",
|
|
1313
|
-
version: "0.3.16-beta.
|
|
1313
|
+
version: "0.3.16-beta.6",
|
|
1314
1314
|
description: "Local development server for Playcademy game development",
|
|
1315
1315
|
type: "module",
|
|
1316
1316
|
exports: {
|
|
@@ -26650,9 +26650,37 @@ var init_developer_service = __esm(() => {
|
|
|
26650
26650
|
// ../api-core/src/services/game.service.ts
|
|
26651
26651
|
class GameService {
|
|
26652
26652
|
deps;
|
|
26653
|
+
static MANIFEST_FETCH_TIMEOUT_MS = 5000;
|
|
26654
|
+
static MAX_FETCH_ERROR_MESSAGE_LENGTH = 512;
|
|
26653
26655
|
constructor(deps) {
|
|
26654
26656
|
this.deps = deps;
|
|
26655
26657
|
}
|
|
26658
|
+
static getManifestHost(manifestUrl) {
|
|
26659
|
+
try {
|
|
26660
|
+
return new URL(manifestUrl).host;
|
|
26661
|
+
} catch {
|
|
26662
|
+
return manifestUrl;
|
|
26663
|
+
}
|
|
26664
|
+
}
|
|
26665
|
+
static getFetchErrorMessage(error) {
|
|
26666
|
+
let raw;
|
|
26667
|
+
if (error instanceof Error) {
|
|
26668
|
+
raw = error.message;
|
|
26669
|
+
} else if (typeof error === "string") {
|
|
26670
|
+
raw = error;
|
|
26671
|
+
}
|
|
26672
|
+
if (!raw) {
|
|
26673
|
+
return;
|
|
26674
|
+
}
|
|
26675
|
+
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
26676
|
+
if (!normalized) {
|
|
26677
|
+
return;
|
|
26678
|
+
}
|
|
26679
|
+
return normalized.slice(0, GameService.MAX_FETCH_ERROR_MESSAGE_LENGTH);
|
|
26680
|
+
}
|
|
26681
|
+
static isRetryableStatus(status) {
|
|
26682
|
+
return status === 429 || status >= 500;
|
|
26683
|
+
}
|
|
26656
26684
|
async list(caller) {
|
|
26657
26685
|
const db2 = this.deps.db;
|
|
26658
26686
|
const isAdmin = caller?.role === "admin";
|
|
@@ -26714,6 +26742,109 @@ class GameService {
|
|
|
26714
26742
|
this.enforceVisibility(game, caller, slug);
|
|
26715
26743
|
return game;
|
|
26716
26744
|
}
|
|
26745
|
+
async getManifest(gameId, caller) {
|
|
26746
|
+
const game = await this.getById(gameId, caller);
|
|
26747
|
+
if (game.gameType !== "hosted" || !game.deploymentUrl) {
|
|
26748
|
+
throw new BadRequestError("Game does not have a deployment manifest");
|
|
26749
|
+
}
|
|
26750
|
+
const deploymentUrl = game.deploymentUrl;
|
|
26751
|
+
const manifestUrl = `${deploymentUrl.replace(/\/$/, "")}/playcademy.manifest.json`;
|
|
26752
|
+
const manifestHost = GameService.getManifestHost(manifestUrl);
|
|
26753
|
+
const startedAt = Date.now();
|
|
26754
|
+
const controller = new AbortController;
|
|
26755
|
+
const timeout = setTimeout(() => controller.abort(), GameService.MANIFEST_FETCH_TIMEOUT_MS);
|
|
26756
|
+
function buildDetails(fetchOutcome, manifestErrorKind, extra = {}) {
|
|
26757
|
+
return {
|
|
26758
|
+
manifestUrl,
|
|
26759
|
+
manifestHost,
|
|
26760
|
+
deploymentUrl,
|
|
26761
|
+
fetchOutcome,
|
|
26762
|
+
retryCount: 0,
|
|
26763
|
+
durationMs: Date.now() - startedAt,
|
|
26764
|
+
manifestErrorKind,
|
|
26765
|
+
...extra
|
|
26766
|
+
};
|
|
26767
|
+
}
|
|
26768
|
+
let response;
|
|
26769
|
+
try {
|
|
26770
|
+
response = await fetch(manifestUrl, {
|
|
26771
|
+
method: "GET",
|
|
26772
|
+
headers: {
|
|
26773
|
+
Accept: "application/json"
|
|
26774
|
+
},
|
|
26775
|
+
signal: controller.signal
|
|
26776
|
+
});
|
|
26777
|
+
} catch (error) {
|
|
26778
|
+
clearTimeout(timeout);
|
|
26779
|
+
const fetchErrorMessage = GameService.getFetchErrorMessage(error);
|
|
26780
|
+
const details = buildDetails("network_error", "temporary", fetchErrorMessage ? { fetchErrorMessage } : {});
|
|
26781
|
+
logger5.error("Failed to fetch game manifest", {
|
|
26782
|
+
gameId,
|
|
26783
|
+
manifestUrl,
|
|
26784
|
+
error,
|
|
26785
|
+
details
|
|
26786
|
+
});
|
|
26787
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
26788
|
+
throw new TimeoutError("Timed out loading game manifest", details);
|
|
26789
|
+
}
|
|
26790
|
+
throw new ServiceUnavailableError("Failed to load game manifest", details);
|
|
26791
|
+
} finally {
|
|
26792
|
+
clearTimeout(timeout);
|
|
26793
|
+
}
|
|
26794
|
+
if (!response.ok) {
|
|
26795
|
+
const resolvedManifestUrl = response.url || manifestUrl;
|
|
26796
|
+
const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
|
|
26797
|
+
const manifestErrorKind = GameService.isRetryableStatus(response.status) ? "temporary" : "permanent";
|
|
26798
|
+
const details = buildDetails("bad_status", manifestErrorKind, {
|
|
26799
|
+
manifestUrl: resolvedManifestUrl,
|
|
26800
|
+
manifestHost: resolvedManifestHost,
|
|
26801
|
+
status: response.status,
|
|
26802
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
26803
|
+
cfRay: response.headers.get("cf-ray") ?? undefined,
|
|
26804
|
+
redirected: response.redirected,
|
|
26805
|
+
...response.redirected ? {
|
|
26806
|
+
originalManifestUrl: manifestUrl,
|
|
26807
|
+
originalManifestHost: manifestHost
|
|
26808
|
+
} : {}
|
|
26809
|
+
});
|
|
26810
|
+
const message = `Failed to fetch manifest: ${response.status} ${response.statusText}`;
|
|
26811
|
+
logger5.error("Game manifest returned non-ok response", {
|
|
26812
|
+
gameId,
|
|
26813
|
+
manifestUrl,
|
|
26814
|
+
status: response.status,
|
|
26815
|
+
details
|
|
26816
|
+
});
|
|
26817
|
+
if (manifestErrorKind === "temporary") {
|
|
26818
|
+
throw new ServiceUnavailableError(message, details);
|
|
26819
|
+
}
|
|
26820
|
+
throw new BadRequestError(message, details);
|
|
26821
|
+
}
|
|
26822
|
+
try {
|
|
26823
|
+
return await response.json();
|
|
26824
|
+
} catch (error) {
|
|
26825
|
+
const resolvedManifestUrl = response.url || manifestUrl;
|
|
26826
|
+
const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
|
|
26827
|
+
const details = buildDetails("invalid_body", "permanent", {
|
|
26828
|
+
manifestUrl: resolvedManifestUrl,
|
|
26829
|
+
manifestHost: resolvedManifestHost,
|
|
26830
|
+
status: response.status,
|
|
26831
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
26832
|
+
cfRay: response.headers.get("cf-ray") ?? undefined,
|
|
26833
|
+
redirected: response.redirected,
|
|
26834
|
+
...response.redirected ? {
|
|
26835
|
+
originalManifestUrl: manifestUrl,
|
|
26836
|
+
originalManifestHost: manifestHost
|
|
26837
|
+
} : {}
|
|
26838
|
+
});
|
|
26839
|
+
logger5.error("Failed to parse game manifest", {
|
|
26840
|
+
gameId,
|
|
26841
|
+
manifestUrl,
|
|
26842
|
+
error,
|
|
26843
|
+
details
|
|
26844
|
+
});
|
|
26845
|
+
throw new BadRequestError("Failed to parse game manifest", details);
|
|
26846
|
+
}
|
|
26847
|
+
}
|
|
26717
26848
|
enforceVisibility(game, caller, lookupIdentifier) {
|
|
26718
26849
|
if (game.visibility !== "internal") {
|
|
26719
26850
|
return;
|
|
@@ -35041,7 +35172,7 @@ function createOneRosterNamespace(client) {
|
|
|
35041
35172
|
limit: options?.limit,
|
|
35042
35173
|
offset: options?.offset,
|
|
35043
35174
|
fields: options?.fields,
|
|
35044
|
-
filter: `student.sourcedId='${studentSourcedId}'`
|
|
35175
|
+
filter: `student.sourcedId='${escapeFilterValue2(studentSourcedId)}'`
|
|
35045
35176
|
});
|
|
35046
35177
|
} catch (error) {
|
|
35047
35178
|
logTimebackError("list assessment results for student", error, {
|
|
@@ -94178,7 +94309,7 @@ var init_domain_controller = __esm(() => {
|
|
|
94178
94309
|
});
|
|
94179
94310
|
|
|
94180
94311
|
// ../api-core/src/controllers/game.controller.ts
|
|
94181
|
-
var logger46, list3, listManageable, getSubjects, getById2, getBySlug, upsertBySlug, remove3, games2;
|
|
94312
|
+
var logger46, list3, listManageable, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
|
|
94182
94313
|
var init_game_controller = __esm(() => {
|
|
94183
94314
|
init_esm();
|
|
94184
94315
|
init_schemas_index();
|
|
@@ -94218,6 +94349,21 @@ var init_game_controller = __esm(() => {
|
|
|
94218
94349
|
logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
|
|
94219
94350
|
return ctx.services.game.getBySlug(slug2, ctx.user);
|
|
94220
94351
|
});
|
|
94352
|
+
getManifest = requireAuth(async (ctx) => {
|
|
94353
|
+
const gameId = ctx.params.gameId;
|
|
94354
|
+
if (!gameId) {
|
|
94355
|
+
throw ApiError.badRequest("Missing game ID");
|
|
94356
|
+
}
|
|
94357
|
+
if (!isValidUUID(gameId)) {
|
|
94358
|
+
throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
|
|
94359
|
+
}
|
|
94360
|
+
logger46.debug("Getting game manifest by ID", {
|
|
94361
|
+
userId: ctx.user.id,
|
|
94362
|
+
gameId,
|
|
94363
|
+
launchId: ctx.launchId
|
|
94364
|
+
});
|
|
94365
|
+
return ctx.services.game.getManifest(gameId, ctx.user);
|
|
94366
|
+
});
|
|
94221
94367
|
upsertBySlug = requireAuth(async (ctx) => {
|
|
94222
94368
|
const slug2 = ctx.params.slug;
|
|
94223
94369
|
if (!slug2) {
|
|
@@ -94254,6 +94400,7 @@ var init_game_controller = __esm(() => {
|
|
|
94254
94400
|
listManageable,
|
|
94255
94401
|
getSubjects,
|
|
94256
94402
|
getById: getById2,
|
|
94403
|
+
getManifest,
|
|
94257
94404
|
getBySlug,
|
|
94258
94405
|
upsertBySlug,
|
|
94259
94406
|
remove: remove3
|
|
@@ -96081,7 +96228,8 @@ var init_crud = __esm(() => {
|
|
|
96081
96228
|
init_api();
|
|
96082
96229
|
gameCrudRouter = new Hono2;
|
|
96083
96230
|
gameCrudRouter.get("/", handle2(games2.list));
|
|
96084
|
-
gameCrudRouter.get("/:gameId{[0-9a-
|
|
96231
|
+
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}/manifest", handle2(games2.getManifest));
|
|
96232
|
+
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}", handle2(games2.getById));
|
|
96085
96233
|
gameCrudRouter.get("/:slug", handle2(games2.getBySlug));
|
|
96086
96234
|
gameCrudRouter.put("/:slug", handle2(games2.upsertBySlug));
|
|
96087
96235
|
gameCrudRouter.delete("/:gameId", handle2(games2.remove, { status: 204 }));
|
package/dist/server.js
CHANGED
|
@@ -1309,7 +1309,7 @@ var package_default;
|
|
|
1309
1309
|
var init_package = __esm(() => {
|
|
1310
1310
|
package_default = {
|
|
1311
1311
|
name: "@playcademy/sandbox",
|
|
1312
|
-
version: "0.3.16-beta.
|
|
1312
|
+
version: "0.3.16-beta.6",
|
|
1313
1313
|
description: "Local development server for Playcademy game development",
|
|
1314
1314
|
type: "module",
|
|
1315
1315
|
exports: {
|
|
@@ -26649,9 +26649,37 @@ var init_developer_service = __esm(() => {
|
|
|
26649
26649
|
// ../api-core/src/services/game.service.ts
|
|
26650
26650
|
class GameService {
|
|
26651
26651
|
deps;
|
|
26652
|
+
static MANIFEST_FETCH_TIMEOUT_MS = 5000;
|
|
26653
|
+
static MAX_FETCH_ERROR_MESSAGE_LENGTH = 512;
|
|
26652
26654
|
constructor(deps) {
|
|
26653
26655
|
this.deps = deps;
|
|
26654
26656
|
}
|
|
26657
|
+
static getManifestHost(manifestUrl) {
|
|
26658
|
+
try {
|
|
26659
|
+
return new URL(manifestUrl).host;
|
|
26660
|
+
} catch {
|
|
26661
|
+
return manifestUrl;
|
|
26662
|
+
}
|
|
26663
|
+
}
|
|
26664
|
+
static getFetchErrorMessage(error) {
|
|
26665
|
+
let raw;
|
|
26666
|
+
if (error instanceof Error) {
|
|
26667
|
+
raw = error.message;
|
|
26668
|
+
} else if (typeof error === "string") {
|
|
26669
|
+
raw = error;
|
|
26670
|
+
}
|
|
26671
|
+
if (!raw) {
|
|
26672
|
+
return;
|
|
26673
|
+
}
|
|
26674
|
+
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
26675
|
+
if (!normalized) {
|
|
26676
|
+
return;
|
|
26677
|
+
}
|
|
26678
|
+
return normalized.slice(0, GameService.MAX_FETCH_ERROR_MESSAGE_LENGTH);
|
|
26679
|
+
}
|
|
26680
|
+
static isRetryableStatus(status) {
|
|
26681
|
+
return status === 429 || status >= 500;
|
|
26682
|
+
}
|
|
26655
26683
|
async list(caller) {
|
|
26656
26684
|
const db2 = this.deps.db;
|
|
26657
26685
|
const isAdmin = caller?.role === "admin";
|
|
@@ -26713,6 +26741,109 @@ class GameService {
|
|
|
26713
26741
|
this.enforceVisibility(game, caller, slug);
|
|
26714
26742
|
return game;
|
|
26715
26743
|
}
|
|
26744
|
+
async getManifest(gameId, caller) {
|
|
26745
|
+
const game = await this.getById(gameId, caller);
|
|
26746
|
+
if (game.gameType !== "hosted" || !game.deploymentUrl) {
|
|
26747
|
+
throw new BadRequestError("Game does not have a deployment manifest");
|
|
26748
|
+
}
|
|
26749
|
+
const deploymentUrl = game.deploymentUrl;
|
|
26750
|
+
const manifestUrl = `${deploymentUrl.replace(/\/$/, "")}/playcademy.manifest.json`;
|
|
26751
|
+
const manifestHost = GameService.getManifestHost(manifestUrl);
|
|
26752
|
+
const startedAt = Date.now();
|
|
26753
|
+
const controller = new AbortController;
|
|
26754
|
+
const timeout = setTimeout(() => controller.abort(), GameService.MANIFEST_FETCH_TIMEOUT_MS);
|
|
26755
|
+
function buildDetails(fetchOutcome, manifestErrorKind, extra = {}) {
|
|
26756
|
+
return {
|
|
26757
|
+
manifestUrl,
|
|
26758
|
+
manifestHost,
|
|
26759
|
+
deploymentUrl,
|
|
26760
|
+
fetchOutcome,
|
|
26761
|
+
retryCount: 0,
|
|
26762
|
+
durationMs: Date.now() - startedAt,
|
|
26763
|
+
manifestErrorKind,
|
|
26764
|
+
...extra
|
|
26765
|
+
};
|
|
26766
|
+
}
|
|
26767
|
+
let response;
|
|
26768
|
+
try {
|
|
26769
|
+
response = await fetch(manifestUrl, {
|
|
26770
|
+
method: "GET",
|
|
26771
|
+
headers: {
|
|
26772
|
+
Accept: "application/json"
|
|
26773
|
+
},
|
|
26774
|
+
signal: controller.signal
|
|
26775
|
+
});
|
|
26776
|
+
} catch (error) {
|
|
26777
|
+
clearTimeout(timeout);
|
|
26778
|
+
const fetchErrorMessage = GameService.getFetchErrorMessage(error);
|
|
26779
|
+
const details = buildDetails("network_error", "temporary", fetchErrorMessage ? { fetchErrorMessage } : {});
|
|
26780
|
+
logger5.error("Failed to fetch game manifest", {
|
|
26781
|
+
gameId,
|
|
26782
|
+
manifestUrl,
|
|
26783
|
+
error,
|
|
26784
|
+
details
|
|
26785
|
+
});
|
|
26786
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
26787
|
+
throw new TimeoutError("Timed out loading game manifest", details);
|
|
26788
|
+
}
|
|
26789
|
+
throw new ServiceUnavailableError("Failed to load game manifest", details);
|
|
26790
|
+
} finally {
|
|
26791
|
+
clearTimeout(timeout);
|
|
26792
|
+
}
|
|
26793
|
+
if (!response.ok) {
|
|
26794
|
+
const resolvedManifestUrl = response.url || manifestUrl;
|
|
26795
|
+
const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
|
|
26796
|
+
const manifestErrorKind = GameService.isRetryableStatus(response.status) ? "temporary" : "permanent";
|
|
26797
|
+
const details = buildDetails("bad_status", manifestErrorKind, {
|
|
26798
|
+
manifestUrl: resolvedManifestUrl,
|
|
26799
|
+
manifestHost: resolvedManifestHost,
|
|
26800
|
+
status: response.status,
|
|
26801
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
26802
|
+
cfRay: response.headers.get("cf-ray") ?? undefined,
|
|
26803
|
+
redirected: response.redirected,
|
|
26804
|
+
...response.redirected ? {
|
|
26805
|
+
originalManifestUrl: manifestUrl,
|
|
26806
|
+
originalManifestHost: manifestHost
|
|
26807
|
+
} : {}
|
|
26808
|
+
});
|
|
26809
|
+
const message = `Failed to fetch manifest: ${response.status} ${response.statusText}`;
|
|
26810
|
+
logger5.error("Game manifest returned non-ok response", {
|
|
26811
|
+
gameId,
|
|
26812
|
+
manifestUrl,
|
|
26813
|
+
status: response.status,
|
|
26814
|
+
details
|
|
26815
|
+
});
|
|
26816
|
+
if (manifestErrorKind === "temporary") {
|
|
26817
|
+
throw new ServiceUnavailableError(message, details);
|
|
26818
|
+
}
|
|
26819
|
+
throw new BadRequestError(message, details);
|
|
26820
|
+
}
|
|
26821
|
+
try {
|
|
26822
|
+
return await response.json();
|
|
26823
|
+
} catch (error) {
|
|
26824
|
+
const resolvedManifestUrl = response.url || manifestUrl;
|
|
26825
|
+
const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
|
|
26826
|
+
const details = buildDetails("invalid_body", "permanent", {
|
|
26827
|
+
manifestUrl: resolvedManifestUrl,
|
|
26828
|
+
manifestHost: resolvedManifestHost,
|
|
26829
|
+
status: response.status,
|
|
26830
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
26831
|
+
cfRay: response.headers.get("cf-ray") ?? undefined,
|
|
26832
|
+
redirected: response.redirected,
|
|
26833
|
+
...response.redirected ? {
|
|
26834
|
+
originalManifestUrl: manifestUrl,
|
|
26835
|
+
originalManifestHost: manifestHost
|
|
26836
|
+
} : {}
|
|
26837
|
+
});
|
|
26838
|
+
logger5.error("Failed to parse game manifest", {
|
|
26839
|
+
gameId,
|
|
26840
|
+
manifestUrl,
|
|
26841
|
+
error,
|
|
26842
|
+
details
|
|
26843
|
+
});
|
|
26844
|
+
throw new BadRequestError("Failed to parse game manifest", details);
|
|
26845
|
+
}
|
|
26846
|
+
}
|
|
26716
26847
|
enforceVisibility(game, caller, lookupIdentifier) {
|
|
26717
26848
|
if (game.visibility !== "internal") {
|
|
26718
26849
|
return;
|
|
@@ -35040,7 +35171,7 @@ function createOneRosterNamespace(client) {
|
|
|
35040
35171
|
limit: options?.limit,
|
|
35041
35172
|
offset: options?.offset,
|
|
35042
35173
|
fields: options?.fields,
|
|
35043
|
-
filter: `student.sourcedId='${studentSourcedId}'`
|
|
35174
|
+
filter: `student.sourcedId='${escapeFilterValue2(studentSourcedId)}'`
|
|
35044
35175
|
});
|
|
35045
35176
|
} catch (error) {
|
|
35046
35177
|
logTimebackError("list assessment results for student", error, {
|
|
@@ -94177,7 +94308,7 @@ var init_domain_controller = __esm(() => {
|
|
|
94177
94308
|
});
|
|
94178
94309
|
|
|
94179
94310
|
// ../api-core/src/controllers/game.controller.ts
|
|
94180
|
-
var logger46, list3, listManageable, getSubjects, getById2, getBySlug, upsertBySlug, remove3, games2;
|
|
94311
|
+
var logger46, list3, listManageable, getSubjects, getById2, getBySlug, getManifest, upsertBySlug, remove3, games2;
|
|
94181
94312
|
var init_game_controller = __esm(() => {
|
|
94182
94313
|
init_esm();
|
|
94183
94314
|
init_schemas_index();
|
|
@@ -94217,6 +94348,21 @@ var init_game_controller = __esm(() => {
|
|
|
94217
94348
|
logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
|
|
94218
94349
|
return ctx.services.game.getBySlug(slug2, ctx.user);
|
|
94219
94350
|
});
|
|
94351
|
+
getManifest = requireAuth(async (ctx) => {
|
|
94352
|
+
const gameId = ctx.params.gameId;
|
|
94353
|
+
if (!gameId) {
|
|
94354
|
+
throw ApiError.badRequest("Missing game ID");
|
|
94355
|
+
}
|
|
94356
|
+
if (!isValidUUID(gameId)) {
|
|
94357
|
+
throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
|
|
94358
|
+
}
|
|
94359
|
+
logger46.debug("Getting game manifest by ID", {
|
|
94360
|
+
userId: ctx.user.id,
|
|
94361
|
+
gameId,
|
|
94362
|
+
launchId: ctx.launchId
|
|
94363
|
+
});
|
|
94364
|
+
return ctx.services.game.getManifest(gameId, ctx.user);
|
|
94365
|
+
});
|
|
94220
94366
|
upsertBySlug = requireAuth(async (ctx) => {
|
|
94221
94367
|
const slug2 = ctx.params.slug;
|
|
94222
94368
|
if (!slug2) {
|
|
@@ -94253,6 +94399,7 @@ var init_game_controller = __esm(() => {
|
|
|
94253
94399
|
listManageable,
|
|
94254
94400
|
getSubjects,
|
|
94255
94401
|
getById: getById2,
|
|
94402
|
+
getManifest,
|
|
94256
94403
|
getBySlug,
|
|
94257
94404
|
upsertBySlug,
|
|
94258
94405
|
remove: remove3
|
|
@@ -96080,7 +96227,8 @@ var init_crud = __esm(() => {
|
|
|
96080
96227
|
init_api();
|
|
96081
96228
|
gameCrudRouter = new Hono2;
|
|
96082
96229
|
gameCrudRouter.get("/", handle2(games2.list));
|
|
96083
|
-
gameCrudRouter.get("/:gameId{[0-9a-
|
|
96230
|
+
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}/manifest", handle2(games2.getManifest));
|
|
96231
|
+
gameCrudRouter.get("/:gameId{[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}}", handle2(games2.getById));
|
|
96084
96232
|
gameCrudRouter.get("/:slug", handle2(games2.getBySlug));
|
|
96085
96233
|
gameCrudRouter.put("/:slug", handle2(games2.upsertBySlug));
|
|
96086
96234
|
gameCrudRouter.delete("/:gameId", handle2(games2.remove, { status: 204 }));
|