@playcademy/vite-plugin 0.2.20 → 0.2.21
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 +154 -84
- package/package.json +4 -4
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.
|
|
25337
|
+
version: "0.3.15",
|
|
25338
25338
|
description: "Local development server for Playcademy game development",
|
|
25339
25339
|
type: "module",
|
|
25340
25340
|
exports: {
|
|
@@ -45219,10 +45219,33 @@ class DeployJobService {
|
|
|
45219
45219
|
}
|
|
45220
45220
|
await this.deps.storage.deleteObject(bucketName, this.getCodeBundleObjectKey(jobId)).catch(() => {});
|
|
45221
45221
|
}
|
|
45222
|
+
assertValidCodeUploadToken(token, gameId) {
|
|
45223
|
+
const expectedPrefix = `uploads-temp/${gameId}/`;
|
|
45224
|
+
if (!token.startsWith(expectedPrefix) || !token.endsWith(".js")) {
|
|
45225
|
+
throw new ValidationError("Invalid backend code upload token");
|
|
45226
|
+
}
|
|
45227
|
+
}
|
|
45228
|
+
async loadUploadedCode(codeUploadToken, gameId) {
|
|
45229
|
+
this.assertValidCodeUploadToken(codeUploadToken, gameId);
|
|
45230
|
+
const bucketName = this.getUploadBucket();
|
|
45231
|
+
const bytes = await this.deps.storage.getObjectBytes(bucketName, codeUploadToken);
|
|
45232
|
+
if (!bytes) {
|
|
45233
|
+
throw new ValidationError("Backend code upload not found");
|
|
45234
|
+
}
|
|
45235
|
+
return new TextDecoder().decode(bytes);
|
|
45236
|
+
}
|
|
45237
|
+
deleteUploadedCode(codeUploadToken, gameId) {
|
|
45238
|
+
this.assertValidCodeUploadToken(codeUploadToken, gameId);
|
|
45239
|
+
const bucketName = this.getUploadBucket();
|
|
45240
|
+
this.deps.storage.deleteObject(bucketName, codeUploadToken).catch(() => {
|
|
45241
|
+
logger2.warn("Failed to delete temp code upload", { key: codeUploadToken });
|
|
45242
|
+
});
|
|
45243
|
+
}
|
|
45222
45244
|
sanitizeRequestForPersistence(request) {
|
|
45223
45245
|
const sanitized = { ...request };
|
|
45224
45246
|
delete sanitized._headers;
|
|
45225
45247
|
delete sanitized.code;
|
|
45248
|
+
delete sanitized.codeUploadToken;
|
|
45226
45249
|
return sanitized;
|
|
45227
45250
|
}
|
|
45228
45251
|
getLeaseExpiry() {
|
|
@@ -45260,6 +45283,16 @@ class DeployJobService {
|
|
|
45260
45283
|
async create(slug, request, user) {
|
|
45261
45284
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
45262
45285
|
const jobId = crypto.randomUUID();
|
|
45286
|
+
let codeSource = "none";
|
|
45287
|
+
if (request.code) {
|
|
45288
|
+
codeSource = "inline";
|
|
45289
|
+
} else if (request.codeUploadToken) {
|
|
45290
|
+
codeSource = "presigned-upload";
|
|
45291
|
+
}
|
|
45292
|
+
logger2.info("Deploy job backend code source", { slug, codeSource });
|
|
45293
|
+
if (codeSource === "presigned-upload") {
|
|
45294
|
+
request.code = await this.loadUploadedCode(request.codeUploadToken, game.id);
|
|
45295
|
+
}
|
|
45263
45296
|
const sanitizedRequest = this.sanitizeRequestForPersistence(request);
|
|
45264
45297
|
if (request.code) {
|
|
45265
45298
|
await this.storeCodeBundle(jobId, request.code);
|
|
@@ -45288,6 +45321,9 @@ class DeployJobService {
|
|
|
45288
45321
|
throw new ValidationError("Failed to create deploy job");
|
|
45289
45322
|
}
|
|
45290
45323
|
logger2.info("Deploy job created", { jobId: job.id, gameId: game.id, slug, userId: user.id });
|
|
45324
|
+
if (codeSource === "presigned-upload") {
|
|
45325
|
+
this.deleteUploadedCode(request.codeUploadToken, game.id);
|
|
45326
|
+
}
|
|
45291
45327
|
return this.toResponse(job);
|
|
45292
45328
|
}
|
|
45293
45329
|
async get(jobId, slug, user) {
|
|
@@ -54126,6 +54162,12 @@ class UploadService {
|
|
|
54126
54162
|
constructor(deps) {
|
|
54127
54163
|
this.deps = deps;
|
|
54128
54164
|
}
|
|
54165
|
+
static getContentType(fileName) {
|
|
54166
|
+
if (fileName.endsWith(".js")) {
|
|
54167
|
+
return "application/javascript";
|
|
54168
|
+
}
|
|
54169
|
+
return "application/zip";
|
|
54170
|
+
}
|
|
54129
54171
|
async initiate(request, user) {
|
|
54130
54172
|
const { fileName, gameId } = request;
|
|
54131
54173
|
const bucketName = this.deps.uploadBucket;
|
|
@@ -54137,7 +54179,7 @@ class UploadService {
|
|
|
54137
54179
|
const version2 = ulid();
|
|
54138
54180
|
const tempS3Key = `uploads-temp/${gameId}/${version2}/${fileName}`;
|
|
54139
54181
|
logger17.debug("Initiating upload", { userId: user.id, gameId, fileName, version: version2 });
|
|
54140
|
-
const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key,
|
|
54182
|
+
const presignedUrl = await this.deps.generatePresignedPutUrl(bucketName, tempS3Key, UploadService.getContentType(fileName));
|
|
54141
54183
|
logger17.info("Presigned URL generated", {
|
|
54142
54184
|
userId: user.id,
|
|
54143
54185
|
gameId,
|
|
@@ -117650,6 +117692,7 @@ var UpdateGameStateSchema;
|
|
|
117650
117692
|
var InsertGameDeploymentSchema;
|
|
117651
117693
|
var InsertGameDeployJobSchema;
|
|
117652
117694
|
var UpsertGameMetadataSchema;
|
|
117695
|
+
var ALLOWED_UPLOAD_EXTENSIONS;
|
|
117653
117696
|
var InitiateUploadSchema;
|
|
117654
117697
|
var AddCustomHostnameSchema;
|
|
117655
117698
|
var SetSecretsRequestSchema;
|
|
@@ -117750,9 +117793,10 @@ var init_schemas2 = __esm(() => {
|
|
|
117750
117793
|
message: "External games require an externalUrl",
|
|
117751
117794
|
path: ["externalUrl"]
|
|
117752
117795
|
});
|
|
117796
|
+
ALLOWED_UPLOAD_EXTENSIONS = [".zip", ".js"];
|
|
117753
117797
|
InitiateUploadSchema = exports_external.object({
|
|
117754
|
-
fileName: exports_external.string().min(1).refine((name4) => name4.endsWith(
|
|
117755
|
-
message:
|
|
117798
|
+
fileName: exports_external.string().min(1).refine((name4) => ALLOWED_UPLOAD_EXTENSIONS.some((ext2) => name4.endsWith(ext2)), {
|
|
117799
|
+
message: `File must be one of: ${ALLOWED_UPLOAD_EXTENSIONS.join(", ")}`
|
|
117756
117800
|
}),
|
|
117757
117801
|
gameId: exports_external.string().uuid()
|
|
117758
117802
|
});
|
|
@@ -117785,6 +117829,7 @@ var init_schemas2 = __esm(() => {
|
|
|
117785
117829
|
DeployRequestSchema = exports_external.object({
|
|
117786
117830
|
uploadToken: exports_external.string().optional(),
|
|
117787
117831
|
code: exports_external.string().optional(),
|
|
117832
|
+
codeUploadToken: exports_external.string().optional(),
|
|
117788
117833
|
config: exports_external.unknown().optional(),
|
|
117789
117834
|
bindings: exports_external.object({
|
|
117790
117835
|
database: exports_external.array(exports_external.string()).optional(),
|
|
@@ -117812,6 +117857,9 @@ var init_schemas2 = __esm(() => {
|
|
|
117812
117857
|
platform: exports_external.string().optional(),
|
|
117813
117858
|
metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
|
|
117814
117859
|
}).optional()
|
|
117860
|
+
}).refine((data) => !(data.code && data.codeUploadToken), {
|
|
117861
|
+
message: "Specify either code or codeUploadToken, not both",
|
|
117862
|
+
path: ["codeUploadToken"]
|
|
117815
117863
|
});
|
|
117816
117864
|
});
|
|
117817
117865
|
function validateRelativePath(path22, fieldName) {
|
|
@@ -120773,6 +120821,97 @@ var init_crud = __esm(() => {
|
|
|
120773
120821
|
gameCrudRouter.put("/:slug", handle2(games2.upsertBySlug));
|
|
120774
120822
|
gameCrudRouter.delete("/:gameId", handle2(games2.remove, { status: 204 }));
|
|
120775
120823
|
});
|
|
120824
|
+
async function initiateHandler(c2) {
|
|
120825
|
+
const user = c2.get("user");
|
|
120826
|
+
if (!user) {
|
|
120827
|
+
return c2.json({ error: { code: "UNAUTHORIZED", message: "Authentication required" } }, 401);
|
|
120828
|
+
}
|
|
120829
|
+
let body2;
|
|
120830
|
+
try {
|
|
120831
|
+
body2 = await c2.req.json();
|
|
120832
|
+
} catch {
|
|
120833
|
+
return c2.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
120834
|
+
}
|
|
120835
|
+
if (!body2.fileName || !body2.gameId) {
|
|
120836
|
+
return c2.json({
|
|
120837
|
+
error: {
|
|
120838
|
+
code: "VALIDATION_FAILED",
|
|
120839
|
+
message: "Validation failed: fileName and gameId are required"
|
|
120840
|
+
}
|
|
120841
|
+
}, 422);
|
|
120842
|
+
}
|
|
120843
|
+
const uploadToken = `sandbox-upload-${randomUUID3()}`;
|
|
120844
|
+
const version4 = `v${Date.now()}`;
|
|
120845
|
+
pendingUploads.set(uploadToken, {
|
|
120846
|
+
gameId: body2.gameId,
|
|
120847
|
+
fileName: body2.fileName
|
|
120848
|
+
});
|
|
120849
|
+
const host = c2.req.header("host") || "localhost:3000";
|
|
120850
|
+
const protocol = host.startsWith("localhost") || host.startsWith("127.0.0.1") ? "http" : "https";
|
|
120851
|
+
const presignedUrl = `${protocol}://${host}/api/games/uploads/sandbox/${uploadToken}`;
|
|
120852
|
+
return c2.json({
|
|
120853
|
+
uploadToken,
|
|
120854
|
+
presignedUrl,
|
|
120855
|
+
gameId: body2.gameId,
|
|
120856
|
+
version: version4
|
|
120857
|
+
});
|
|
120858
|
+
}
|
|
120859
|
+
function consumeUploadedCode(token) {
|
|
120860
|
+
const upload = pendingUploads.get(token);
|
|
120861
|
+
if (!upload?.data) {
|
|
120862
|
+
return;
|
|
120863
|
+
}
|
|
120864
|
+
const { data } = upload;
|
|
120865
|
+
pendingUploads.delete(token);
|
|
120866
|
+
return data;
|
|
120867
|
+
}
|
|
120868
|
+
async function finalizeHandler(c2) {
|
|
120869
|
+
const user = c2.get("user");
|
|
120870
|
+
if (!user) {
|
|
120871
|
+
return c2.json({ error: "Authentication required" }, 401);
|
|
120872
|
+
}
|
|
120873
|
+
let body2;
|
|
120874
|
+
try {
|
|
120875
|
+
body2 = await c2.req.json();
|
|
120876
|
+
} catch {
|
|
120877
|
+
return c2.json({ error: "Invalid JSON body" }, 400);
|
|
120878
|
+
}
|
|
120879
|
+
if (!body2.uploadToken) {
|
|
120880
|
+
return c2.json({ error: "uploadToken is required" }, 400);
|
|
120881
|
+
}
|
|
120882
|
+
const upload = pendingUploads.get(body2.uploadToken);
|
|
120883
|
+
if (!upload) {
|
|
120884
|
+
return c2.json({ error: "Invalid or expired upload token" }, 400);
|
|
120885
|
+
}
|
|
120886
|
+
pendingUploads.delete(body2.uploadToken);
|
|
120887
|
+
return c2.json({
|
|
120888
|
+
success: true,
|
|
120889
|
+
gameId: upload.gameId,
|
|
120890
|
+
fileName: upload.fileName
|
|
120891
|
+
});
|
|
120892
|
+
}
|
|
120893
|
+
var gameUploadsRouter;
|
|
120894
|
+
var pendingUploads;
|
|
120895
|
+
var init_uploads = __esm(() => {
|
|
120896
|
+
init_dist4();
|
|
120897
|
+
gameUploadsRouter = new Hono2;
|
|
120898
|
+
pendingUploads = new Map;
|
|
120899
|
+
gameUploadsRouter.post("/uploads/initiate", initiateHandler);
|
|
120900
|
+
gameUploadsRouter.post("/uploads/initiate/", initiateHandler);
|
|
120901
|
+
gameUploadsRouter.put("/uploads/sandbox/:token", async (c2) => {
|
|
120902
|
+
const token = c2.req.param("token");
|
|
120903
|
+
const upload = pendingUploads.get(token);
|
|
120904
|
+
if (!upload) {
|
|
120905
|
+
return c2.json({ error: "Invalid upload token" }, 400);
|
|
120906
|
+
}
|
|
120907
|
+
if (upload.fileName.endsWith(".js")) {
|
|
120908
|
+
upload.data = await c2.req.text();
|
|
120909
|
+
}
|
|
120910
|
+
return c2.text("", 200);
|
|
120911
|
+
});
|
|
120912
|
+
gameUploadsRouter.post("/uploads/finalize", finalizeHandler);
|
|
120913
|
+
gameUploadsRouter.post("/uploads/finalize/", finalizeHandler);
|
|
120914
|
+
});
|
|
120776
120915
|
var logger66;
|
|
120777
120916
|
var gameDeployRouter;
|
|
120778
120917
|
var init_deploy = __esm(() => {
|
|
@@ -120783,6 +120922,7 @@ var init_deploy = __esm(() => {
|
|
|
120783
120922
|
init_tables_index();
|
|
120784
120923
|
init_src2();
|
|
120785
120924
|
init_api();
|
|
120925
|
+
init_uploads();
|
|
120786
120926
|
logger66 = log.scope("SandboxDeploy");
|
|
120787
120927
|
gameDeployRouter = new Hono2;
|
|
120788
120928
|
gameDeployRouter.post("/:slug/deploy", async (c2) => {
|
|
@@ -120853,6 +120993,14 @@ var init_deploy = __esm(() => {
|
|
|
120853
120993
|
}).returning();
|
|
120854
120994
|
game = inserted[0];
|
|
120855
120995
|
}
|
|
120996
|
+
let backendCode = body2.code;
|
|
120997
|
+
if (!backendCode && body2.codeUploadToken) {
|
|
120998
|
+
backendCode = consumeUploadedCode(body2.codeUploadToken);
|
|
120999
|
+
if (!backendCode) {
|
|
121000
|
+
return c2.json({ error: { code: "BAD_REQUEST", message: "Backend code upload not found" } }, 400);
|
|
121001
|
+
}
|
|
121002
|
+
}
|
|
121003
|
+
const hasBackend = Boolean(backendCode);
|
|
120856
121004
|
const events = [
|
|
120857
121005
|
{ type: "status", message: "Deployment queued", createdAt: now2.toISOString() },
|
|
120858
121006
|
{ type: "status", message: "Deployment started", createdAt: now2.toISOString() },
|
|
@@ -120864,7 +121012,7 @@ var init_deploy = __esm(() => {
|
|
|
120864
121012
|
},
|
|
120865
121013
|
{ type: "status", message: "Extracting assets", createdAt: now2.toISOString() }
|
|
120866
121014
|
] : [],
|
|
120867
|
-
...
|
|
121015
|
+
...hasBackend ? [
|
|
120868
121016
|
{
|
|
120869
121017
|
type: "status",
|
|
120870
121018
|
message: "Deploying backend code",
|
|
@@ -121114,84 +121262,6 @@ var init_shop = __esm(() => {
|
|
|
121114
121262
|
gameShopRouter.patch("/:gameId/items/:itemId/shop-listing", handle2(shopListings2.updateForGameItem));
|
|
121115
121263
|
gameShopRouter.delete("/:gameId/items/:itemId/shop-listing", handle2(shopListings2.deleteForGameItem, { status: 204 }));
|
|
121116
121264
|
});
|
|
121117
|
-
async function initiateHandler(c2) {
|
|
121118
|
-
const user = c2.get("user");
|
|
121119
|
-
if (!user) {
|
|
121120
|
-
return c2.json({ error: { code: "UNAUTHORIZED", message: "Authentication required" } }, 401);
|
|
121121
|
-
}
|
|
121122
|
-
let body2;
|
|
121123
|
-
try {
|
|
121124
|
-
body2 = await c2.req.json();
|
|
121125
|
-
} catch {
|
|
121126
|
-
return c2.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
121127
|
-
}
|
|
121128
|
-
if (!body2.fileName || !body2.gameId) {
|
|
121129
|
-
return c2.json({
|
|
121130
|
-
error: {
|
|
121131
|
-
code: "VALIDATION_FAILED",
|
|
121132
|
-
message: "Validation failed: fileName and gameId are required"
|
|
121133
|
-
}
|
|
121134
|
-
}, 422);
|
|
121135
|
-
}
|
|
121136
|
-
const uploadToken = `sandbox-upload-${randomUUID3()}`;
|
|
121137
|
-
const version4 = `v${Date.now()}`;
|
|
121138
|
-
pendingUploads.set(uploadToken, {
|
|
121139
|
-
gameId: body2.gameId,
|
|
121140
|
-
fileName: body2.fileName
|
|
121141
|
-
});
|
|
121142
|
-
const host = c2.req.header("host") || "localhost:3000";
|
|
121143
|
-
const protocol = host.startsWith("localhost") || host.startsWith("127.0.0.1") ? "http" : "https";
|
|
121144
|
-
const presignedUrl = `${protocol}://${host}/api/games/uploads/sandbox/${uploadToken}`;
|
|
121145
|
-
return c2.json({
|
|
121146
|
-
uploadToken,
|
|
121147
|
-
presignedUrl,
|
|
121148
|
-
gameId: body2.gameId,
|
|
121149
|
-
version: version4
|
|
121150
|
-
});
|
|
121151
|
-
}
|
|
121152
|
-
async function finalizeHandler(c2) {
|
|
121153
|
-
const user = c2.get("user");
|
|
121154
|
-
if (!user) {
|
|
121155
|
-
return c2.json({ error: "Authentication required" }, 401);
|
|
121156
|
-
}
|
|
121157
|
-
let body2;
|
|
121158
|
-
try {
|
|
121159
|
-
body2 = await c2.req.json();
|
|
121160
|
-
} catch {
|
|
121161
|
-
return c2.json({ error: "Invalid JSON body" }, 400);
|
|
121162
|
-
}
|
|
121163
|
-
if (!body2.uploadToken) {
|
|
121164
|
-
return c2.json({ error: "uploadToken is required" }, 400);
|
|
121165
|
-
}
|
|
121166
|
-
const upload = pendingUploads.get(body2.uploadToken);
|
|
121167
|
-
if (!upload) {
|
|
121168
|
-
return c2.json({ error: "Invalid or expired upload token" }, 400);
|
|
121169
|
-
}
|
|
121170
|
-
pendingUploads.delete(body2.uploadToken);
|
|
121171
|
-
return c2.json({
|
|
121172
|
-
success: true,
|
|
121173
|
-
gameId: upload.gameId,
|
|
121174
|
-
fileName: upload.fileName
|
|
121175
|
-
});
|
|
121176
|
-
}
|
|
121177
|
-
var gameUploadsRouter;
|
|
121178
|
-
var pendingUploads;
|
|
121179
|
-
var init_uploads = __esm(() => {
|
|
121180
|
-
init_dist4();
|
|
121181
|
-
gameUploadsRouter = new Hono2;
|
|
121182
|
-
pendingUploads = new Map;
|
|
121183
|
-
gameUploadsRouter.post("/uploads/initiate", initiateHandler);
|
|
121184
|
-
gameUploadsRouter.post("/uploads/initiate/", initiateHandler);
|
|
121185
|
-
gameUploadsRouter.put("/uploads/sandbox/:token", async (c2) => {
|
|
121186
|
-
const token = c2.req.param("token");
|
|
121187
|
-
if (!pendingUploads.has(token)) {
|
|
121188
|
-
return c2.json({ error: "Invalid upload token" }, 400);
|
|
121189
|
-
}
|
|
121190
|
-
return c2.text("", 200);
|
|
121191
|
-
});
|
|
121192
|
-
gameUploadsRouter.post("/uploads/finalize", finalizeHandler);
|
|
121193
|
-
gameUploadsRouter.post("/uploads/finalize/", finalizeHandler);
|
|
121194
|
-
});
|
|
121195
121265
|
var gameVerifyRouter;
|
|
121196
121266
|
var init_verify2 = __esm(() => {
|
|
121197
121267
|
init_dist4();
|
|
@@ -123059,7 +123129,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
|
123059
123129
|
// package.json
|
|
123060
123130
|
var package_default2 = {
|
|
123061
123131
|
name: "@playcademy/vite-plugin",
|
|
123062
|
-
version: "0.2.
|
|
123132
|
+
version: "0.2.21",
|
|
123063
123133
|
type: "module",
|
|
123064
123134
|
exports: {
|
|
123065
123135
|
".": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playcademy/vite-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.21",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -21,14 +21,14 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"archiver": "^7.0.1",
|
|
23
23
|
"picocolors": "^1.1.1",
|
|
24
|
-
"playcademy": "0.18.
|
|
24
|
+
"playcademy": "0.18.3"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"@electric-sql/pglite": "^0.3.16",
|
|
28
28
|
"@inquirer/prompts": "^7.8.6",
|
|
29
29
|
"@playcademy/constants": "0.0.1",
|
|
30
|
-
"@playcademy/sandbox": "0.3.
|
|
31
|
-
"@playcademy/sdk": "0.3.
|
|
30
|
+
"@playcademy/sandbox": "0.3.15",
|
|
31
|
+
"@playcademy/sdk": "0.3.2",
|
|
32
32
|
"@playcademy/types": "0.0.1",
|
|
33
33
|
"@playcademy/utils": "0.0.1",
|
|
34
34
|
"@types/archiver": "^6.0.3",
|