@playcademy/vite-plugin 0.2.22-beta.4 → 0.2.22-beta.5
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 +152 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -25334,7 +25334,7 @@ var package_default;
|
|
|
25334
25334
|
var init_package = __esm(() => {
|
|
25335
25335
|
package_default = {
|
|
25336
25336
|
name: "@playcademy/sandbox",
|
|
25337
|
-
version: "0.3.16-beta.
|
|
25337
|
+
version: "0.3.16-beta.5",
|
|
25338
25338
|
description: "Local development server for Playcademy game development",
|
|
25339
25339
|
type: "module",
|
|
25340
25340
|
exports: {
|
|
@@ -50501,9 +50501,37 @@ var init_developer_service = __esm(() => {
|
|
|
50501
50501
|
|
|
50502
50502
|
class GameService {
|
|
50503
50503
|
deps;
|
|
50504
|
+
static MANIFEST_FETCH_TIMEOUT_MS = 5000;
|
|
50505
|
+
static MAX_FETCH_ERROR_MESSAGE_LENGTH = 512;
|
|
50504
50506
|
constructor(deps) {
|
|
50505
50507
|
this.deps = deps;
|
|
50506
50508
|
}
|
|
50509
|
+
static getManifestHost(manifestUrl) {
|
|
50510
|
+
try {
|
|
50511
|
+
return new URL(manifestUrl).host;
|
|
50512
|
+
} catch {
|
|
50513
|
+
return manifestUrl;
|
|
50514
|
+
}
|
|
50515
|
+
}
|
|
50516
|
+
static getFetchErrorMessage(error) {
|
|
50517
|
+
let raw;
|
|
50518
|
+
if (error instanceof Error) {
|
|
50519
|
+
raw = error.message;
|
|
50520
|
+
} else if (typeof error === "string") {
|
|
50521
|
+
raw = error;
|
|
50522
|
+
}
|
|
50523
|
+
if (!raw) {
|
|
50524
|
+
return;
|
|
50525
|
+
}
|
|
50526
|
+
const normalized = raw.replace(/\s+/g, " ").trim();
|
|
50527
|
+
if (!normalized) {
|
|
50528
|
+
return;
|
|
50529
|
+
}
|
|
50530
|
+
return normalized.slice(0, GameService.MAX_FETCH_ERROR_MESSAGE_LENGTH);
|
|
50531
|
+
}
|
|
50532
|
+
static isRetryableStatus(status) {
|
|
50533
|
+
return status === 429 || status >= 500;
|
|
50534
|
+
}
|
|
50507
50535
|
async list(caller) {
|
|
50508
50536
|
const db2 = this.deps.db;
|
|
50509
50537
|
const isAdmin = caller?.role === "admin";
|
|
@@ -50565,6 +50593,109 @@ class GameService {
|
|
|
50565
50593
|
this.enforceVisibility(game, caller, slug);
|
|
50566
50594
|
return game;
|
|
50567
50595
|
}
|
|
50596
|
+
async getManifest(gameId, caller) {
|
|
50597
|
+
const game = await this.getById(gameId, caller);
|
|
50598
|
+
if (game.gameType !== "hosted" || !game.deploymentUrl) {
|
|
50599
|
+
throw new BadRequestError("Game does not have a deployment manifest");
|
|
50600
|
+
}
|
|
50601
|
+
const deploymentUrl = game.deploymentUrl;
|
|
50602
|
+
const manifestUrl = `${deploymentUrl.replace(/\/$/, "")}/playcademy.manifest.json`;
|
|
50603
|
+
const manifestHost = GameService.getManifestHost(manifestUrl);
|
|
50604
|
+
const startedAt = Date.now();
|
|
50605
|
+
const controller = new AbortController;
|
|
50606
|
+
const timeout = setTimeout(() => controller.abort(), GameService.MANIFEST_FETCH_TIMEOUT_MS);
|
|
50607
|
+
function buildDetails(fetchOutcome, manifestErrorKind, extra = {}) {
|
|
50608
|
+
return {
|
|
50609
|
+
manifestUrl,
|
|
50610
|
+
manifestHost,
|
|
50611
|
+
deploymentUrl,
|
|
50612
|
+
fetchOutcome,
|
|
50613
|
+
retryCount: 0,
|
|
50614
|
+
durationMs: Date.now() - startedAt,
|
|
50615
|
+
manifestErrorKind,
|
|
50616
|
+
...extra
|
|
50617
|
+
};
|
|
50618
|
+
}
|
|
50619
|
+
let response;
|
|
50620
|
+
try {
|
|
50621
|
+
response = await fetch(manifestUrl, {
|
|
50622
|
+
method: "GET",
|
|
50623
|
+
headers: {
|
|
50624
|
+
Accept: "application/json"
|
|
50625
|
+
},
|
|
50626
|
+
signal: controller.signal
|
|
50627
|
+
});
|
|
50628
|
+
} catch (error) {
|
|
50629
|
+
clearTimeout(timeout);
|
|
50630
|
+
const fetchErrorMessage = GameService.getFetchErrorMessage(error);
|
|
50631
|
+
const details = buildDetails("network_error", "temporary", fetchErrorMessage ? { fetchErrorMessage } : {});
|
|
50632
|
+
logger5.error("Failed to fetch game manifest", {
|
|
50633
|
+
gameId,
|
|
50634
|
+
manifestUrl,
|
|
50635
|
+
error,
|
|
50636
|
+
details
|
|
50637
|
+
});
|
|
50638
|
+
if (error instanceof Error && error.name === "AbortError") {
|
|
50639
|
+
throw new TimeoutError("Timed out loading game manifest", details);
|
|
50640
|
+
}
|
|
50641
|
+
throw new ServiceUnavailableError("Failed to load game manifest", details);
|
|
50642
|
+
} finally {
|
|
50643
|
+
clearTimeout(timeout);
|
|
50644
|
+
}
|
|
50645
|
+
if (!response.ok) {
|
|
50646
|
+
const resolvedManifestUrl = response.url || manifestUrl;
|
|
50647
|
+
const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
|
|
50648
|
+
const manifestErrorKind = GameService.isRetryableStatus(response.status) ? "temporary" : "permanent";
|
|
50649
|
+
const details = buildDetails("bad_status", manifestErrorKind, {
|
|
50650
|
+
manifestUrl: resolvedManifestUrl,
|
|
50651
|
+
manifestHost: resolvedManifestHost,
|
|
50652
|
+
status: response.status,
|
|
50653
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
50654
|
+
cfRay: response.headers.get("cf-ray") ?? undefined,
|
|
50655
|
+
redirected: response.redirected,
|
|
50656
|
+
...response.redirected ? {
|
|
50657
|
+
originalManifestUrl: manifestUrl,
|
|
50658
|
+
originalManifestHost: manifestHost
|
|
50659
|
+
} : {}
|
|
50660
|
+
});
|
|
50661
|
+
const message = `Failed to fetch manifest: ${response.status} ${response.statusText}`;
|
|
50662
|
+
logger5.error("Game manifest returned non-ok response", {
|
|
50663
|
+
gameId,
|
|
50664
|
+
manifestUrl,
|
|
50665
|
+
status: response.status,
|
|
50666
|
+
details
|
|
50667
|
+
});
|
|
50668
|
+
if (manifestErrorKind === "temporary") {
|
|
50669
|
+
throw new ServiceUnavailableError(message, details);
|
|
50670
|
+
}
|
|
50671
|
+
throw new BadRequestError(message, details);
|
|
50672
|
+
}
|
|
50673
|
+
try {
|
|
50674
|
+
return await response.json();
|
|
50675
|
+
} catch (error) {
|
|
50676
|
+
const resolvedManifestUrl = response.url || manifestUrl;
|
|
50677
|
+
const resolvedManifestHost = GameService.getManifestHost(resolvedManifestUrl);
|
|
50678
|
+
const details = buildDetails("invalid_body", "permanent", {
|
|
50679
|
+
manifestUrl: resolvedManifestUrl,
|
|
50680
|
+
manifestHost: resolvedManifestHost,
|
|
50681
|
+
status: response.status,
|
|
50682
|
+
contentType: response.headers.get("content-type") ?? undefined,
|
|
50683
|
+
cfRay: response.headers.get("cf-ray") ?? undefined,
|
|
50684
|
+
redirected: response.redirected,
|
|
50685
|
+
...response.redirected ? {
|
|
50686
|
+
originalManifestUrl: manifestUrl,
|
|
50687
|
+
originalManifestHost: manifestHost
|
|
50688
|
+
} : {}
|
|
50689
|
+
});
|
|
50690
|
+
logger5.error("Failed to parse game manifest", {
|
|
50691
|
+
gameId,
|
|
50692
|
+
manifestUrl,
|
|
50693
|
+
error,
|
|
50694
|
+
details
|
|
50695
|
+
});
|
|
50696
|
+
throw new BadRequestError("Failed to parse game manifest", details);
|
|
50697
|
+
}
|
|
50698
|
+
}
|
|
50568
50699
|
enforceVisibility(game, caller, lookupIdentifier) {
|
|
50569
50700
|
if (game.visibility !== "internal") {
|
|
50570
50701
|
return;
|
|
@@ -120722,6 +120853,7 @@ var listManageable;
|
|
|
120722
120853
|
var getSubjects;
|
|
120723
120854
|
var getById2;
|
|
120724
120855
|
var getBySlug;
|
|
120856
|
+
var getManifest;
|
|
120725
120857
|
var upsertBySlug;
|
|
120726
120858
|
var remove3;
|
|
120727
120859
|
var games2;
|
|
@@ -120764,6 +120896,21 @@ var init_game_controller = __esm(() => {
|
|
|
120764
120896
|
logger46.debug("Getting game by slug", { userId: ctx.user.id, slug: slug2, launchId: ctx.launchId });
|
|
120765
120897
|
return ctx.services.game.getBySlug(slug2, ctx.user);
|
|
120766
120898
|
});
|
|
120899
|
+
getManifest = requireAuth(async (ctx) => {
|
|
120900
|
+
const gameId = ctx.params.gameId;
|
|
120901
|
+
if (!gameId) {
|
|
120902
|
+
throw ApiError.badRequest("Missing game ID");
|
|
120903
|
+
}
|
|
120904
|
+
if (!isValidUUID(gameId)) {
|
|
120905
|
+
throw ApiError.unprocessableEntity("gameId must be a valid UUID format");
|
|
120906
|
+
}
|
|
120907
|
+
logger46.debug("Getting game manifest by ID", {
|
|
120908
|
+
userId: ctx.user.id,
|
|
120909
|
+
gameId,
|
|
120910
|
+
launchId: ctx.launchId
|
|
120911
|
+
});
|
|
120912
|
+
return ctx.services.game.getManifest(gameId, ctx.user);
|
|
120913
|
+
});
|
|
120767
120914
|
upsertBySlug = requireAuth(async (ctx) => {
|
|
120768
120915
|
const slug2 = ctx.params.slug;
|
|
120769
120916
|
if (!slug2) {
|
|
@@ -120800,6 +120947,7 @@ var init_game_controller = __esm(() => {
|
|
|
120800
120947
|
listManageable,
|
|
120801
120948
|
getSubjects,
|
|
120802
120949
|
getById: getById2,
|
|
120950
|
+
getManifest,
|
|
120803
120951
|
getBySlug,
|
|
120804
120952
|
upsertBySlug,
|
|
120805
120953
|
remove: remove3
|
|
@@ -122674,7 +122822,8 @@ var init_crud = __esm(() => {
|
|
|
122674
122822
|
init_api();
|
|
122675
122823
|
gameCrudRouter = new Hono2;
|
|
122676
122824
|
gameCrudRouter.get("/", handle2(games2.list));
|
|
122677
|
-
gameCrudRouter.get("/:gameId{[0-9a-
|
|
122825
|
+
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));
|
|
122826
|
+
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));
|
|
122678
122827
|
gameCrudRouter.get("/:slug", handle2(games2.getBySlug));
|
|
122679
122828
|
gameCrudRouter.put("/:slug", handle2(games2.upsertBySlug));
|
|
122680
122829
|
gameCrudRouter.delete("/:gameId", handle2(games2.remove, { status: 204 }));
|
|
@@ -124987,7 +125136,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
|
124987
125136
|
// package.json
|
|
124988
125137
|
var package_default2 = {
|
|
124989
125138
|
name: "@playcademy/vite-plugin",
|
|
124990
|
-
version: "0.2.22-beta.
|
|
125139
|
+
version: "0.2.22-beta.5",
|
|
124991
125140
|
type: "module",
|
|
124992
125141
|
exports: {
|
|
124993
125142
|
".": {
|