@playcademy/vite-plugin 0.1.26-alpha.6 → 0.1.26-alpha.7
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/README.md +53 -10
- package/dist/index.js +184 -35
- package/dist/lib/logging/utils.d.ts +2 -0
- package/dist/lib/sandbox/server.d.ts +5 -0
- package/dist/server/platform-mode.d.ts +5 -0
- package/dist/types/internal.d.ts +6 -0
- package/dist/types/options.d.ts +270 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -78,13 +78,15 @@ export default defineConfig({
|
|
|
78
78
|
plugins: [
|
|
79
79
|
playcademy({
|
|
80
80
|
export: {
|
|
81
|
-
platform: 'web', // Platform identifier (auto-detected)
|
|
82
81
|
autoZip: true, // Create deployment zip (enabled by default)
|
|
83
82
|
},
|
|
84
83
|
sandbox: {
|
|
85
84
|
autoStart: true, // Start sandbox automatically
|
|
86
|
-
url: 'http://localhost:4321', // Sandbox URL
|
|
87
85
|
verbose: false, // Enable debug logging
|
|
86
|
+
logLevel: 'info', // Log level (debug, info, warn, error)
|
|
87
|
+
seed: true, // Seed database with demo data
|
|
88
|
+
recreateDb: false, // Recreate database on each start
|
|
89
|
+
memoryOnly: false, // Use in-memory database
|
|
88
90
|
realtime: {
|
|
89
91
|
enabled: false, // Disabled by default, enable for multiplayer
|
|
90
92
|
port: 4322, // Defaults to API port + 1
|
|
@@ -143,14 +145,24 @@ Configuration for manifest generation and build output:
|
|
|
143
145
|
|
|
144
146
|
Configuration for the development sandbox server:
|
|
145
147
|
|
|
146
|
-
| Option
|
|
147
|
-
|
|
|
148
|
-
| `autoStart`
|
|
149
|
-
| `url`
|
|
150
|
-
| `verbose`
|
|
151
|
-
| `logLevel`
|
|
152
|
-
| `recreateDb`
|
|
153
|
-
| `
|
|
148
|
+
| Option | Type | Default | Description |
|
|
149
|
+
| -------------- | ---------------------------------------- | ----------------------------- | --------------------------------------- |
|
|
150
|
+
| `autoStart` | `boolean` | `true` | Start sandbox during development |
|
|
151
|
+
| `url` | `string` | `'http://localhost:4321/api'` | Sandbox server URL |
|
|
152
|
+
| `verbose` | `boolean` | `false` | Enable verbose logging |
|
|
153
|
+
| `logLevel` | `'debug' \| 'info' \| 'warn' \| 'error'` | `'info'` | Log level for sandbox server |
|
|
154
|
+
| `recreateDb` | `boolean` | `false` | Recreate database on each start |
|
|
155
|
+
| `seed` | `boolean` | `true` | Seed database with demo data |
|
|
156
|
+
| `memoryOnly` | `boolean` | `false` | Use in-memory database (non-persistent) |
|
|
157
|
+
| `databasePath` | `string` | `undefined` | Custom path for database file |
|
|
158
|
+
| `realtime` | `object` | `{ enabled: false }` | Real-time server configuration |
|
|
159
|
+
|
|
160
|
+
#### Realtime Options (`sandbox.realtime`)
|
|
161
|
+
|
|
162
|
+
| Option | Type | Default | Description |
|
|
163
|
+
| --------- | --------- | ------------ | -------------------------------- |
|
|
164
|
+
| `enabled` | `boolean` | `false` | Enable WebSocket server |
|
|
165
|
+
| `port` | `number` | API port + 1 | Custom port for WebSocket server |
|
|
154
166
|
|
|
155
167
|
## Build Output
|
|
156
168
|
|
|
@@ -317,6 +329,7 @@ export default defineConfig({
|
|
|
317
329
|
sandbox: {
|
|
318
330
|
realtime: {
|
|
319
331
|
enabled: true,
|
|
332
|
+
port: 4322, // Optional: custom WebSocket port
|
|
320
333
|
},
|
|
321
334
|
},
|
|
322
335
|
}),
|
|
@@ -324,6 +337,36 @@ export default defineConfig({
|
|
|
324
337
|
})
|
|
325
338
|
```
|
|
326
339
|
|
|
340
|
+
### Testing with Clean Database
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
// Start with a fresh database on each dev server restart
|
|
344
|
+
export default defineConfig({
|
|
345
|
+
plugins: [
|
|
346
|
+
playcademy({
|
|
347
|
+
sandbox: {
|
|
348
|
+
recreateDb: true, // Recreate database on each start
|
|
349
|
+
},
|
|
350
|
+
}),
|
|
351
|
+
],
|
|
352
|
+
})
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### In-Memory Database for CI
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
// Use in-memory database for fast, ephemeral testing
|
|
359
|
+
export default defineConfig({
|
|
360
|
+
plugins: [
|
|
361
|
+
playcademy({
|
|
362
|
+
sandbox: {
|
|
363
|
+
memoryOnly: true, // Database in RAM only
|
|
364
|
+
},
|
|
365
|
+
}),
|
|
366
|
+
],
|
|
367
|
+
})
|
|
368
|
+
```
|
|
369
|
+
|
|
327
370
|
### Godot Export
|
|
328
371
|
|
|
329
372
|
```typescript
|
package/dist/index.js
CHANGED
|
@@ -41271,7 +41271,11 @@ function printBanner(viteConfig, servers, projectInfo, pluginVersion) {
|
|
|
41271
41271
|
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Game:")} ${import_picocolors3.cyan(projectInfo.slug)}`);
|
|
41272
41272
|
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Sandbox:")} ${import_picocolors3.cyan(`http://localhost:${import_picocolors3.bold(servers.sandbox.toString())}/api`)}`);
|
|
41273
41273
|
if (servers.backend) {
|
|
41274
|
-
|
|
41274
|
+
const backendUrl = servers.vite ? `http://localhost:${import_picocolors3.bold(servers.vite.toString())}/api ${import_picocolors3.dim(`(via ${servers.backend})`)}` : `http://localhost:${import_picocolors3.bold(servers.backend.toString())}/api`;
|
|
41275
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Backend:")} ${import_picocolors3.cyan(backendUrl)}`);
|
|
41276
|
+
}
|
|
41277
|
+
if (servers.realtime) {
|
|
41278
|
+
viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Realtime:")} ${import_picocolors3.cyan(`ws://localhost:${import_picocolors3.bold(servers.realtime.toString())}`)}`);
|
|
41275
41279
|
}
|
|
41276
41280
|
viteConfig.logger.info("");
|
|
41277
41281
|
}
|
|
@@ -41359,7 +41363,7 @@ async function setupCliDevServer(options) {
|
|
|
41359
41363
|
var import_picocolors5 = __toESM(require_picocolors(), 1);
|
|
41360
41364
|
import { DEFAULT_PORTS as DEFAULT_PORTS2 } from "playcademy/constants";
|
|
41361
41365
|
|
|
41362
|
-
// ../sandbox/dist/server
|
|
41366
|
+
// ../sandbox/dist/server.js
|
|
41363
41367
|
import { createRequire as createRequire2 } from "node:module";
|
|
41364
41368
|
import * as s3 from "fs";
|
|
41365
41369
|
import * as o3 from "path";
|
|
@@ -153167,30 +153171,30 @@ var package_default2 = {
|
|
|
153167
153171
|
type: "module",
|
|
153168
153172
|
exports: {
|
|
153169
153173
|
".": {
|
|
153170
|
-
import: "./dist/server
|
|
153171
|
-
types: "./dist/server
|
|
153174
|
+
import: "./dist/server.js",
|
|
153175
|
+
types: "./dist/server.d.ts"
|
|
153172
153176
|
},
|
|
153173
153177
|
"./cli": {
|
|
153174
|
-
import: "./dist/cli
|
|
153175
|
-
types: "./dist/cli
|
|
153178
|
+
import: "./dist/cli.js",
|
|
153179
|
+
types: "./dist/cli.d.ts"
|
|
153176
153180
|
},
|
|
153177
153181
|
"./config": {
|
|
153178
|
-
import: "./dist/config
|
|
153179
|
-
types: "./dist/config
|
|
153182
|
+
import: "./dist/config.js",
|
|
153183
|
+
types: "./dist/config.d.ts"
|
|
153180
153184
|
}
|
|
153181
153185
|
},
|
|
153182
153186
|
bin: {
|
|
153183
|
-
"playcademy-sandbox": "./dist/cli
|
|
153187
|
+
"playcademy-sandbox": "./dist/cli.js"
|
|
153184
153188
|
},
|
|
153185
153189
|
files: [
|
|
153186
153190
|
"dist"
|
|
153187
153191
|
],
|
|
153188
153192
|
scripts: {
|
|
153189
153193
|
build: "bun run build.ts",
|
|
153190
|
-
dev: "bun --watch src/cli
|
|
153194
|
+
dev: "bun --watch src/cli",
|
|
153195
|
+
docs: "typedoc",
|
|
153191
153196
|
pub: "bun publish.ts",
|
|
153192
|
-
start: "bun src/cli.ts"
|
|
153193
|
-
docs: "typedoc"
|
|
153197
|
+
start: "bun src/cli.ts"
|
|
153194
153198
|
},
|
|
153195
153199
|
dependencies: {
|
|
153196
153200
|
"@electric-sql/pglite": "^0.3.2",
|
|
@@ -194511,21 +194515,14 @@ async function getTodayXpFromEvents(db, userId, date4, timezone2) {
|
|
|
194511
194515
|
};
|
|
194512
194516
|
}
|
|
194513
194517
|
init_src();
|
|
194514
|
-
async function
|
|
194518
|
+
async function verifyGameAccessById(gameId, user) {
|
|
194515
194519
|
if (user.role !== "admin") {
|
|
194516
194520
|
const existingGame = await getDatabase().query.games.findFirst({
|
|
194517
194521
|
where: and(eq(games.id, gameId), eq(games.developerId, user.id)),
|
|
194518
194522
|
columns: { id: true }
|
|
194519
194523
|
});
|
|
194520
194524
|
if (!existingGame) {
|
|
194521
|
-
|
|
194522
|
-
where: eq(games.id, gameId),
|
|
194523
|
-
columns: { id: true }
|
|
194524
|
-
});
|
|
194525
|
-
if (!gameExists) {
|
|
194526
|
-
throw ApiError.notFound("Game not found");
|
|
194527
|
-
}
|
|
194528
|
-
throw ApiError.forbidden("You do not own this game");
|
|
194525
|
+
throw ApiError.notFound("Game not found");
|
|
194529
194526
|
}
|
|
194530
194527
|
} else {
|
|
194531
194528
|
const gameExists = await getDatabase().query.games.findFirst({
|
|
@@ -195006,6 +195003,10 @@ async function applyForDeveloperStatus(ctx) {
|
|
|
195006
195003
|
}
|
|
195007
195004
|
try {
|
|
195008
195005
|
await db.update(users).set({ developerStatus: "pending" }).where(eq(users.id, user.id));
|
|
195006
|
+
log2.info("[API] Developer status application submitted", {
|
|
195007
|
+
userId: user.id,
|
|
195008
|
+
newStatus: "pending"
|
|
195009
|
+
});
|
|
195009
195010
|
} catch (error2) {
|
|
195010
195011
|
log2.error(`Error updating developer status for user ${user.id}:`, {
|
|
195011
195012
|
error: error2
|
|
@@ -200375,6 +200376,7 @@ async function initiateUpload(ctx) {
|
|
|
200375
200376
|
throw ApiError.badRequest("Invalid JSON body");
|
|
200376
200377
|
}
|
|
200377
200378
|
const { fileName, gameId } = inputData;
|
|
200379
|
+
await verifyGameAccessById(gameId, user);
|
|
200378
200380
|
if (!uploadDeps) {
|
|
200379
200381
|
throw ApiError.internal("Upload dependencies not configured");
|
|
200380
200382
|
}
|
|
@@ -200511,7 +200513,7 @@ async function deleteGame(ctx) {
|
|
|
200511
200513
|
if (!gameId) {
|
|
200512
200514
|
throw ApiError.badRequest("Missing game ID");
|
|
200513
200515
|
}
|
|
200514
|
-
await
|
|
200516
|
+
await verifyGameAccessById(gameId, user);
|
|
200515
200517
|
try {
|
|
200516
200518
|
const db = getDatabase();
|
|
200517
200519
|
const gameToDelete = await db.query.games.findFirst({
|
|
@@ -200526,6 +200528,11 @@ async function deleteGame(ctx) {
|
|
|
200526
200528
|
if (result.length === 0) {
|
|
200527
200529
|
throw ApiError.notFound("Game not found for deletion");
|
|
200528
200530
|
}
|
|
200531
|
+
log2.info("[API] Game deleted", {
|
|
200532
|
+
gameId: result[0].id,
|
|
200533
|
+
slug: gameToDelete?.slug,
|
|
200534
|
+
hadActiveDeployment: !!activeDeployment
|
|
200535
|
+
});
|
|
200529
200536
|
if (activeDeployment?.provider === "cloudflare") {
|
|
200530
200537
|
try {
|
|
200531
200538
|
const cloudflare2 = getCloudflareProvider();
|
|
@@ -200541,7 +200548,7 @@ async function deleteGame(ctx) {
|
|
|
200541
200548
|
});
|
|
200542
200549
|
}
|
|
200543
200550
|
} catch (cleanupError) {
|
|
200544
|
-
log2.
|
|
200551
|
+
log2.warn("Non-fatal: Failed to cleanup Cloudflare Worker for deleted game", {
|
|
200545
200552
|
gameId,
|
|
200546
200553
|
deploymentId: activeDeployment.deploymentId,
|
|
200547
200554
|
cleanupError
|
|
@@ -200700,11 +200707,17 @@ async function upsertGameBySlug(ctx) {
|
|
|
200700
200707
|
gameId: gameResponse.id
|
|
200701
200708
|
}).where(eq(mapElements.id, metadataInput.mapElementId));
|
|
200702
200709
|
} catch (mapError) {
|
|
200703
|
-
log2.
|
|
200710
|
+
log2.warn(`Failed to update map element ${metadataInput.mapElementId}:`, {
|
|
200704
200711
|
mapError
|
|
200705
200712
|
});
|
|
200706
200713
|
}
|
|
200707
200714
|
}
|
|
200715
|
+
log2.info("[API] Game upserted", {
|
|
200716
|
+
gameId: gameResponse.id,
|
|
200717
|
+
slug: gameResponse.slug,
|
|
200718
|
+
operation: isUpdate ? "update" : "create",
|
|
200719
|
+
displayName: gameResponse.displayName
|
|
200720
|
+
});
|
|
200708
200721
|
return gameResponse;
|
|
200709
200722
|
} catch (error2) {
|
|
200710
200723
|
if (error2 instanceof ApiError) {
|
|
@@ -200925,6 +200938,12 @@ async function createItem(ctx) {
|
|
|
200925
200938
|
if (!newItem) {
|
|
200926
200939
|
throw ApiError.internal("Failed to create item in database");
|
|
200927
200940
|
}
|
|
200941
|
+
log2.info("[API] Item created", {
|
|
200942
|
+
itemId: newItem.id,
|
|
200943
|
+
displayName: newItem.displayName,
|
|
200944
|
+
type: newItem.type,
|
|
200945
|
+
gameId: newItem.gameId
|
|
200946
|
+
});
|
|
200928
200947
|
return newItem;
|
|
200929
200948
|
} catch (error2) {
|
|
200930
200949
|
if (error2 instanceof Error) {
|
|
@@ -200977,6 +200996,11 @@ async function updateItem(ctx) {
|
|
|
200977
200996
|
if (!updatedItem) {
|
|
200978
200997
|
throw ApiError.notFound("Item not found for update");
|
|
200979
200998
|
}
|
|
200999
|
+
log2.info("[API] Item updated", {
|
|
201000
|
+
itemId: updatedItem.id,
|
|
201001
|
+
displayName: updatedItem.displayName,
|
|
201002
|
+
updatedFields: Object.keys(inputData)
|
|
201003
|
+
});
|
|
200980
201004
|
return updatedItem;
|
|
200981
201005
|
} catch (error2) {
|
|
200982
201006
|
if (error2 instanceof ApiError)
|
|
@@ -201012,6 +201036,9 @@ async function deleteItem(ctx) {
|
|
|
201012
201036
|
if (result.length === 0) {
|
|
201013
201037
|
throw ApiError.notFound("Item not found for deletion");
|
|
201014
201038
|
}
|
|
201039
|
+
log2.info("[API] Item deleted", {
|
|
201040
|
+
itemId: result[0].id
|
|
201041
|
+
});
|
|
201015
201042
|
} catch (error2) {
|
|
201016
201043
|
if (error2 instanceof ApiError) {
|
|
201017
201044
|
throw error2;
|
|
@@ -201136,6 +201163,12 @@ async function createGameItem(ctx) {
|
|
|
201136
201163
|
if (!newItem) {
|
|
201137
201164
|
throw ApiError.internal("Failed to create item in database");
|
|
201138
201165
|
}
|
|
201166
|
+
log2.info("[API] Game item created", {
|
|
201167
|
+
itemId: newItem.id,
|
|
201168
|
+
gameId: newItem.gameId,
|
|
201169
|
+
displayName: newItem.displayName,
|
|
201170
|
+
type: newItem.type
|
|
201171
|
+
});
|
|
201139
201172
|
return newItem;
|
|
201140
201173
|
} catch (error2) {
|
|
201141
201174
|
if (error2 instanceof Error) {
|
|
@@ -201199,6 +201232,12 @@ async function updateGameItem(ctx) {
|
|
|
201199
201232
|
if (!updatedItem) {
|
|
201200
201233
|
throw ApiError.notFound("Item not found for update");
|
|
201201
201234
|
}
|
|
201235
|
+
log2.info("[API] Game item updated", {
|
|
201236
|
+
itemId: updatedItem.id,
|
|
201237
|
+
gameId: updatedItem.gameId,
|
|
201238
|
+
displayName: updatedItem.displayName,
|
|
201239
|
+
updatedFields: Object.keys(inputData)
|
|
201240
|
+
});
|
|
201202
201241
|
return updatedItem;
|
|
201203
201242
|
} catch (error2) {
|
|
201204
201243
|
if (error2 instanceof ApiError) {
|
|
@@ -201241,6 +201280,10 @@ async function deleteGameItem(ctx) {
|
|
|
201241
201280
|
if (result.length === 0) {
|
|
201242
201281
|
throw ApiError.notFound("Item not found for this game");
|
|
201243
201282
|
}
|
|
201283
|
+
log2.info("[API] Game item deleted", {
|
|
201284
|
+
itemId: result[0].id,
|
|
201285
|
+
gameId
|
|
201286
|
+
});
|
|
201244
201287
|
} catch (error2) {
|
|
201245
201288
|
if (error2 instanceof ApiError) {
|
|
201246
201289
|
throw error2;
|
|
@@ -201304,6 +201347,13 @@ async function createGameItemShopListing(ctx) {
|
|
|
201304
201347
|
if (!newListing) {
|
|
201305
201348
|
throw ApiError.internal("Failed to create shop listing in database");
|
|
201306
201349
|
}
|
|
201350
|
+
log2.info("[API] Game item shop listing created", {
|
|
201351
|
+
listingId: newListing.id,
|
|
201352
|
+
gameId,
|
|
201353
|
+
itemId,
|
|
201354
|
+
currencyId: newListing.currencyId,
|
|
201355
|
+
price: newListing.price
|
|
201356
|
+
});
|
|
201307
201357
|
return newListing;
|
|
201308
201358
|
} catch (error2) {
|
|
201309
201359
|
if (error2 instanceof ApiError)
|
|
@@ -201412,6 +201462,12 @@ async function updateGameItemShopListing(ctx) {
|
|
|
201412
201462
|
if (!updatedListing) {
|
|
201413
201463
|
throw ApiError.notFound("Shop listing not found for this item");
|
|
201414
201464
|
}
|
|
201465
|
+
log2.info("[API] Game item shop listing updated", {
|
|
201466
|
+
listingId: updatedListing.id,
|
|
201467
|
+
gameId,
|
|
201468
|
+
itemId,
|
|
201469
|
+
updatedFields: Object.keys(inputData)
|
|
201470
|
+
});
|
|
201415
201471
|
return updatedListing;
|
|
201416
201472
|
} catch (error2) {
|
|
201417
201473
|
if (error2 instanceof ApiError)
|
|
@@ -201458,7 +201514,11 @@ async function deleteGameItemShopListing(ctx) {
|
|
|
201458
201514
|
if (result.length === 0) {
|
|
201459
201515
|
throw ApiError.notFound("Shop listing not found for this item");
|
|
201460
201516
|
}
|
|
201461
|
-
log2.info(
|
|
201517
|
+
log2.info("[API] Game item shop listing deleted", {
|
|
201518
|
+
listingId: result[0].id,
|
|
201519
|
+
gameId,
|
|
201520
|
+
itemId
|
|
201521
|
+
});
|
|
201462
201522
|
} catch (error2) {
|
|
201463
201523
|
if (error2 instanceof ApiError) {
|
|
201464
201524
|
throw error2;
|
|
@@ -202300,6 +202360,12 @@ async function addInventoryItem(ctx) {
|
|
|
202300
202360
|
}
|
|
202301
202361
|
return updatedQuantity;
|
|
202302
202362
|
});
|
|
202363
|
+
log2.info("[API] Inventory item added", {
|
|
202364
|
+
userId: user.id,
|
|
202365
|
+
itemId,
|
|
202366
|
+
quantityAdded: qty,
|
|
202367
|
+
newTotal
|
|
202368
|
+
});
|
|
202303
202369
|
return { newTotal };
|
|
202304
202370
|
} catch (error2) {
|
|
202305
202371
|
if (error2 instanceof ApiError) {
|
|
@@ -202350,6 +202416,12 @@ async function removeInventoryItem(ctx) {
|
|
|
202350
202416
|
}
|
|
202351
202417
|
return updatedItemRecord.quantity;
|
|
202352
202418
|
});
|
|
202419
|
+
log2.info("[API] Inventory item removed", {
|
|
202420
|
+
userId: user.id,
|
|
202421
|
+
itemId,
|
|
202422
|
+
quantityRemoved: qty,
|
|
202423
|
+
newTotal
|
|
202424
|
+
});
|
|
202353
202425
|
return { newTotal };
|
|
202354
202426
|
} catch (error2) {
|
|
202355
202427
|
if (error2 instanceof ApiError) {
|
|
@@ -202703,6 +202775,12 @@ async function createCurrency(ctx) {
|
|
|
202703
202775
|
if (!newCurrency) {
|
|
202704
202776
|
throw ApiError.internal("Failed to create currency in database");
|
|
202705
202777
|
}
|
|
202778
|
+
log2.info("[API] Currency created", {
|
|
202779
|
+
currencyId: newCurrency.id,
|
|
202780
|
+
itemId: newCurrency.itemId,
|
|
202781
|
+
symbol: newCurrency.symbol,
|
|
202782
|
+
isPrimary: newCurrency.isPrimary
|
|
202783
|
+
});
|
|
202706
202784
|
return newCurrency;
|
|
202707
202785
|
} catch (error2) {
|
|
202708
202786
|
if (error2 instanceof ApiError)
|
|
@@ -202756,6 +202834,11 @@ async function updateCurrency(ctx) {
|
|
|
202756
202834
|
if (!updatedCurrency) {
|
|
202757
202835
|
throw ApiError.notFound("Currency not found for update");
|
|
202758
202836
|
}
|
|
202837
|
+
log2.info("[API] Currency updated", {
|
|
202838
|
+
currencyId: updatedCurrency.id,
|
|
202839
|
+
itemId: updatedCurrency.itemId,
|
|
202840
|
+
updatedFields: Object.keys(inputData)
|
|
202841
|
+
});
|
|
202759
202842
|
return updatedCurrency;
|
|
202760
202843
|
} catch (error2) {
|
|
202761
202844
|
if (error2 instanceof ApiError)
|
|
@@ -202794,6 +202877,9 @@ async function deleteCurrency(ctx) {
|
|
|
202794
202877
|
if (result.length === 0) {
|
|
202795
202878
|
throw ApiError.notFound("Currency not found for deletion");
|
|
202796
202879
|
}
|
|
202880
|
+
log2.info("[API] Currency deleted", {
|
|
202881
|
+
currencyId: result[0].id
|
|
202882
|
+
});
|
|
202797
202883
|
} catch (error2) {
|
|
202798
202884
|
if (error2 instanceof ApiError)
|
|
202799
202885
|
throw error2;
|
|
@@ -203287,6 +203373,12 @@ async function createShopListing(ctx) {
|
|
|
203287
203373
|
if (!newListing) {
|
|
203288
203374
|
throw ApiError.internal("Failed to create shop listing in database");
|
|
203289
203375
|
}
|
|
203376
|
+
log2.info("[API] Shop listing created", {
|
|
203377
|
+
listingId: newListing.id,
|
|
203378
|
+
itemId: newListing.itemId,
|
|
203379
|
+
currencyId: newListing.currencyId,
|
|
203380
|
+
price: newListing.price
|
|
203381
|
+
});
|
|
203290
203382
|
return newListing;
|
|
203291
203383
|
} catch (error2) {
|
|
203292
203384
|
if (error2 instanceof ApiError)
|
|
@@ -203404,6 +203496,11 @@ async function updateShopListing(ctx) {
|
|
|
203404
203496
|
if (!updatedListing) {
|
|
203405
203497
|
throw ApiError.notFound("Shop listing not found for update");
|
|
203406
203498
|
}
|
|
203499
|
+
log2.info("[API] Shop listing updated", {
|
|
203500
|
+
listingId: updatedListing.id,
|
|
203501
|
+
itemId: updatedListing.itemId,
|
|
203502
|
+
updatedFields: Object.keys(inputData)
|
|
203503
|
+
});
|
|
203407
203504
|
return updatedListing;
|
|
203408
203505
|
} catch (error2) {
|
|
203409
203506
|
if (error2 instanceof ApiError)
|
|
@@ -203447,6 +203544,9 @@ async function deleteShopListing(ctx) {
|
|
|
203447
203544
|
if (result.length === 0) {
|
|
203448
203545
|
throw ApiError.notFound("Shop listing not found for deletion");
|
|
203449
203546
|
}
|
|
203547
|
+
log2.info("[API] Shop listing deleted", {
|
|
203548
|
+
listingId: result[0].id
|
|
203549
|
+
});
|
|
203450
203550
|
} catch (error2) {
|
|
203451
203551
|
if (error2 instanceof ApiError)
|
|
203452
203552
|
throw error2;
|
|
@@ -203715,6 +203815,10 @@ async function createPlayerCharacter(ctx) {
|
|
|
203715
203815
|
await tx.update(users).set({ characterCreated: true }).where(eq(users.id, user.id));
|
|
203716
203816
|
return characterRow;
|
|
203717
203817
|
});
|
|
203818
|
+
log2.info("[API] Player character created", {
|
|
203819
|
+
userId: user.id,
|
|
203820
|
+
characterId: result.id
|
|
203821
|
+
});
|
|
203718
203822
|
return result;
|
|
203719
203823
|
} catch (error2) {
|
|
203720
203824
|
log2.error("Error creating player character", { error: error2 });
|
|
@@ -203749,6 +203853,11 @@ async function updatePlayerCharacter(ctx) {
|
|
|
203749
203853
|
const [row] = await db.update(playerCharacters).set({ ...payload, updatedAt: new Date }).where(eq(playerCharacters.userId, user.id)).returning();
|
|
203750
203854
|
if (!row)
|
|
203751
203855
|
throw ApiError.notFound("Player character not found");
|
|
203856
|
+
log2.info("[API] Player character updated", {
|
|
203857
|
+
userId: user.id,
|
|
203858
|
+
characterId: row.id,
|
|
203859
|
+
updatedFields: Object.keys(payload)
|
|
203860
|
+
});
|
|
203752
203861
|
return row;
|
|
203753
203862
|
} catch (error2) {
|
|
203754
203863
|
log2.error("Error updating player character", { error: error2 });
|
|
@@ -205397,17 +205506,30 @@ function detectTimebackOptions() {
|
|
|
205397
205506
|
}
|
|
205398
205507
|
|
|
205399
205508
|
// src/lib/sandbox/server.ts
|
|
205400
|
-
function printSandboxInfo(viteConfig, apiPort, realtimePort, projectInfo) {
|
|
205509
|
+
function printSandboxInfo(viteConfig, apiPort, realtimePort, projectInfo, realtimeEnabled) {
|
|
205401
205510
|
viteConfig.logger.info("");
|
|
205402
205511
|
viteConfig.logger.info(` ${import_picocolors5.default.green(import_picocolors5.default.bold("PLAYCADEMY"))} ${import_picocolors5.default.green(`v${version3}`)}`);
|
|
205403
205512
|
viteConfig.logger.info("");
|
|
205404
205513
|
viteConfig.logger.info(` ${import_picocolors5.default.green("➜")} ${import_picocolors5.default.bold("Game:")} ${import_picocolors5.default.cyan(projectInfo.slug)}`);
|
|
205405
205514
|
viteConfig.logger.info(` ${import_picocolors5.default.green("➜")} ${import_picocolors5.default.bold("Sandbox:")} ${import_picocolors5.default.cyan(`http://localhost:${import_picocolors5.default.bold(apiPort.toString())}/api`)}`);
|
|
205406
|
-
|
|
205515
|
+
if (realtimeEnabled) {
|
|
205516
|
+
viteConfig.logger.info(` ${import_picocolors5.default.green("➜")} ${import_picocolors5.default.bold("Realtime:")} ${import_picocolors5.default.cyan(`ws://localhost:${import_picocolors5.default.bold(realtimePort.toString())}`)}`);
|
|
205517
|
+
}
|
|
205407
205518
|
viteConfig.logger.info("");
|
|
205408
205519
|
}
|
|
205409
205520
|
async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
205410
|
-
const {
|
|
205521
|
+
const {
|
|
205522
|
+
verbose = false,
|
|
205523
|
+
customUrl,
|
|
205524
|
+
quiet = false,
|
|
205525
|
+
recreateDb = false,
|
|
205526
|
+
seed = true,
|
|
205527
|
+
memoryOnly = false,
|
|
205528
|
+
databasePath,
|
|
205529
|
+
realtimeEnabled = false,
|
|
205530
|
+
realtimePort,
|
|
205531
|
+
logLevel = "info"
|
|
205532
|
+
} = options;
|
|
205411
205533
|
if (!autoStart || viteConfig.command !== "serve") {
|
|
205412
205534
|
const baseUrl = customUrl ?? `http://localhost:${DEFAULT_PORTS2.SANDBOX}`;
|
|
205413
205535
|
const deriveRealtimeUrl = (url) => {
|
|
@@ -205424,6 +205546,7 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
205424
205546
|
baseUrl,
|
|
205425
205547
|
realtimeUrl: deriveRealtimeUrl(baseUrl),
|
|
205426
205548
|
port: DEFAULT_PORTS2.SANDBOX,
|
|
205549
|
+
realtimePort: undefined,
|
|
205427
205550
|
project: null,
|
|
205428
205551
|
cleanup: () => {}
|
|
205429
205552
|
};
|
|
@@ -205431,17 +205554,19 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
205431
205554
|
try {
|
|
205432
205555
|
const sandboxPort = await findAvailablePort(DEFAULT_PORTS2.SANDBOX);
|
|
205433
205556
|
const baseUrl = `http://localhost:${sandboxPort}`;
|
|
205434
|
-
const realtimePort = await findAvailablePort(sandboxPort + 1);
|
|
205435
|
-
const realtimeUrl = `ws://localhost:${realtimePort}`;
|
|
205436
205557
|
const projectInfo = await extractProjectInfo(viteConfig);
|
|
205437
205558
|
const timebackOptions = detectTimebackOptions();
|
|
205559
|
+
const finalRealtimePort = realtimePort ?? await findAvailablePort(sandboxPort + 1);
|
|
205560
|
+
const realtimeUrl = `ws://localhost:${finalRealtimePort}`;
|
|
205438
205561
|
const server = await startServer2(sandboxPort, projectInfo, {
|
|
205439
205562
|
verbose,
|
|
205440
205563
|
quiet,
|
|
205441
|
-
seed
|
|
205564
|
+
seed,
|
|
205565
|
+
memoryOnly,
|
|
205566
|
+
databasePath,
|
|
205442
205567
|
recreateDb,
|
|
205443
205568
|
logLevel,
|
|
205444
|
-
realtime: { enabled:
|
|
205569
|
+
realtime: { enabled: realtimeEnabled, port: finalRealtimePort },
|
|
205445
205570
|
timeback: timebackOptions,
|
|
205446
205571
|
logger: createLoggerAdapter("sandbox")
|
|
205447
205572
|
});
|
|
@@ -205454,13 +205579,14 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
205454
205579
|
});
|
|
205455
205580
|
if (!quiet) {
|
|
205456
205581
|
setTimeout(() => {
|
|
205457
|
-
printSandboxInfo(viteConfig, sandboxPort,
|
|
205582
|
+
printSandboxInfo(viteConfig, sandboxPort, finalRealtimePort, projectInfo, realtimeEnabled);
|
|
205458
205583
|
}, 100);
|
|
205459
205584
|
}
|
|
205460
205585
|
return {
|
|
205461
205586
|
baseUrl,
|
|
205462
205587
|
realtimeUrl,
|
|
205463
205588
|
port: sandboxPort,
|
|
205589
|
+
realtimePort: realtimeEnabled ? finalRealtimePort : undefined,
|
|
205464
205590
|
project: projectInfo,
|
|
205465
205591
|
cleanup: () => {
|
|
205466
205592
|
cleanupServerInfo("sandbox", viteConfig.root, process.pid);
|
|
@@ -205475,6 +205601,7 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
|
|
|
205475
205601
|
baseUrl: `http://localhost:${DEFAULT_PORTS2.SANDBOX}`,
|
|
205476
205602
|
realtimeUrl: "ws://localhost:4322",
|
|
205477
205603
|
port: DEFAULT_PORTS2.SANDBOX,
|
|
205604
|
+
realtimePort: undefined,
|
|
205478
205605
|
project: null,
|
|
205479
205606
|
cleanup: () => {}
|
|
205480
205607
|
};
|
|
@@ -205956,7 +206083,12 @@ async function configurePlatformMode(server, viteConfig, options) {
|
|
|
205956
206083
|
logLevel: options.logLevel,
|
|
205957
206084
|
customUrl: options.sandboxUrl,
|
|
205958
206085
|
quiet: true,
|
|
205959
|
-
recreateDb: options.recreateDb
|
|
206086
|
+
recreateDb: options.recreateDb,
|
|
206087
|
+
seed: options.seed,
|
|
206088
|
+
memoryOnly: options.memoryOnly,
|
|
206089
|
+
databasePath: options.databasePath,
|
|
206090
|
+
realtimeEnabled: options.realtimeEnabled,
|
|
206091
|
+
realtimePort: options.realtimePort
|
|
205960
206092
|
});
|
|
205961
206093
|
serverState.sandbox = sandbox;
|
|
205962
206094
|
const backend = await setupCliDevServer({
|
|
@@ -205972,7 +206104,13 @@ async function configurePlatformMode(server, viteConfig, options) {
|
|
|
205972
206104
|
server.httpServer?.once("listening", () => {
|
|
205973
206105
|
setTimeout(async () => {
|
|
205974
206106
|
const projectInfo = await extractProjectInfo(viteConfig);
|
|
205975
|
-
|
|
206107
|
+
const vitePort = server.config.server.port;
|
|
206108
|
+
printBanner(viteConfig, {
|
|
206109
|
+
sandbox: sandbox.port,
|
|
206110
|
+
backend: backend?.port,
|
|
206111
|
+
realtime: sandbox.realtimePort,
|
|
206112
|
+
vite: vitePort
|
|
206113
|
+
}, projectInfo, package_default.version);
|
|
205976
206114
|
}, 100);
|
|
205977
206115
|
});
|
|
205978
206116
|
}
|
|
@@ -206043,6 +206181,11 @@ async function configureServerHook(server, context) {
|
|
|
206043
206181
|
logLevel: context.options.logLevel,
|
|
206044
206182
|
sandboxUrl: context.options.sandboxUrl,
|
|
206045
206183
|
recreateDb: context.options.recreateDb,
|
|
206184
|
+
seed: context.options.seed,
|
|
206185
|
+
memoryOnly: context.options.memoryOnly,
|
|
206186
|
+
databasePath: context.options.databasePath,
|
|
206187
|
+
realtimeEnabled: context.options.realtimeEnabled,
|
|
206188
|
+
realtimePort: context.options.realtimePort,
|
|
206046
206189
|
showBadge: context.options.showBadge,
|
|
206047
206190
|
preferredBackendPort: preferredPort
|
|
206048
206191
|
};
|
|
@@ -206100,6 +206243,7 @@ function resolveOptions(options) {
|
|
|
206100
206243
|
const exportOptions = options.export ?? {};
|
|
206101
206244
|
const sandboxOptions = options.sandbox ?? {};
|
|
206102
206245
|
const shellOptions = options.shell ?? {};
|
|
206246
|
+
const realtimeOptions = sandboxOptions.realtime ?? {};
|
|
206103
206247
|
return {
|
|
206104
206248
|
mode: options.mode ?? "platform",
|
|
206105
206249
|
autoZip: exportOptions.autoZip ?? true,
|
|
@@ -206108,6 +206252,11 @@ function resolveOptions(options) {
|
|
|
206108
206252
|
verbose: sandboxOptions.verbose ?? false,
|
|
206109
206253
|
logLevel: sandboxOptions.logLevel ?? "info",
|
|
206110
206254
|
recreateDb: sandboxOptions.recreateDb ?? false,
|
|
206255
|
+
seed: sandboxOptions.seed ?? true,
|
|
206256
|
+
memoryOnly: sandboxOptions.memoryOnly ?? false,
|
|
206257
|
+
databasePath: sandboxOptions.databasePath,
|
|
206258
|
+
realtimeEnabled: realtimeOptions.enabled ?? false,
|
|
206259
|
+
realtimePort: realtimeOptions.port,
|
|
206111
206260
|
showBadge: shellOptions.showBadge ?? true
|
|
206112
206261
|
};
|
|
206113
206262
|
}
|
|
@@ -5,5 +5,10 @@ export declare function startSandbox(viteConfig: ResolvedConfig, autoStart?: boo
|
|
|
5
5
|
customUrl?: string;
|
|
6
6
|
quiet?: boolean;
|
|
7
7
|
recreateDb?: boolean;
|
|
8
|
+
seed?: boolean;
|
|
9
|
+
memoryOnly?: boolean;
|
|
10
|
+
databasePath?: string;
|
|
11
|
+
realtimeEnabled?: boolean;
|
|
12
|
+
realtimePort?: number;
|
|
8
13
|
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
9
14
|
}): Promise<SandboxManager>;
|
|
@@ -9,6 +9,11 @@ export interface PlatformModeOptions {
|
|
|
9
9
|
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
10
10
|
sandboxUrl: string;
|
|
11
11
|
recreateDb: boolean;
|
|
12
|
+
seed: boolean;
|
|
13
|
+
memoryOnly: boolean;
|
|
14
|
+
databasePath?: string;
|
|
15
|
+
realtimeEnabled: boolean;
|
|
16
|
+
realtimePort?: number;
|
|
12
17
|
showBadge: boolean;
|
|
13
18
|
preferredBackendPort: number;
|
|
14
19
|
}
|
package/dist/types/internal.d.ts
CHANGED
|
@@ -14,6 +14,11 @@ export interface ResolvedPluginOptions {
|
|
|
14
14
|
verbose: boolean;
|
|
15
15
|
logLevel: 'debug' | 'info' | 'warn' | 'error';
|
|
16
16
|
recreateDb: boolean;
|
|
17
|
+
seed: boolean;
|
|
18
|
+
memoryOnly: boolean;
|
|
19
|
+
databasePath?: string;
|
|
20
|
+
realtimeEnabled: boolean;
|
|
21
|
+
realtimePort?: number;
|
|
17
22
|
showBadge: boolean;
|
|
18
23
|
}
|
|
19
24
|
/**
|
|
@@ -50,6 +55,7 @@ export interface SandboxManager {
|
|
|
50
55
|
baseUrl: string;
|
|
51
56
|
realtimeUrl: string;
|
|
52
57
|
port: number;
|
|
58
|
+
realtimePort?: number;
|
|
53
59
|
project: ProjectInfo | null;
|
|
54
60
|
cleanup: () => void;
|
|
55
61
|
}
|
package/dist/types/options.d.ts
CHANGED
|
@@ -3,58 +3,319 @@
|
|
|
3
3
|
*/
|
|
4
4
|
/**
|
|
5
5
|
* Plugin operation mode
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
*
|
|
7
|
+
* Controls how the Vite plugin operates during development:
|
|
8
|
+
* - `'platform'`: Full Playcademy platform experience with sandbox server, backend bundling, and shell wrapper (default)
|
|
9
|
+
* - `'standalone'`: Backend only, no sandbox or shell
|
|
10
|
+
*
|
|
11
|
+
* @default 'platform'
|
|
8
12
|
*/
|
|
9
13
|
export type PlaycademyMode = 'platform' | 'standalone';
|
|
10
14
|
/**
|
|
11
|
-
* Configuration options for exporting Playcademy games
|
|
15
|
+
* Configuration options for exporting/building Playcademy games
|
|
16
|
+
*
|
|
17
|
+
* Controls how your game is packaged for deployment.
|
|
12
18
|
*/
|
|
13
19
|
export interface PlaycademyExportOptions {
|
|
14
20
|
/**
|
|
15
|
-
* Automatically create a deployment zip archive
|
|
21
|
+
* Automatically create a deployment zip archive after build.
|
|
22
|
+
*
|
|
23
|
+
* The zip file is created at `.playcademy/{project-name}.zip` and contains
|
|
24
|
+
* all files needed for deployment (frontend assets + backend bundle).
|
|
25
|
+
*
|
|
16
26
|
* @default true
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* export: {
|
|
30
|
+
* autoZip: false // Disable auto-zipping
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
17
33
|
*/
|
|
18
34
|
autoZip?: boolean;
|
|
19
35
|
}
|
|
20
36
|
/**
|
|
21
37
|
* Configuration options for the Playcademy sandbox server
|
|
38
|
+
*
|
|
39
|
+
* The sandbox server provides a local development environment that simulates
|
|
40
|
+
* the Playcademy platform, including API endpoints, authentication, and database.
|
|
22
41
|
*/
|
|
23
42
|
export interface PlaycademySandboxOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Automatically start the sandbox server when Vite starts.
|
|
45
|
+
*
|
|
46
|
+
* Set to `false` if you want to start the sandbox server manually.
|
|
47
|
+
*
|
|
48
|
+
* @default true
|
|
49
|
+
* @example
|
|
50
|
+
* ```ts
|
|
51
|
+
* sandbox: {
|
|
52
|
+
* autoStart: false // Start sandbox manually
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
24
56
|
autoStart?: boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Custom URL for the sandbox server.
|
|
59
|
+
*
|
|
60
|
+
* Useful if you need to run the sandbox on a specific host or port.
|
|
61
|
+
* By default, the sandbox uses `http://localhost:{port}` where port
|
|
62
|
+
* is auto-assigned.
|
|
63
|
+
*
|
|
64
|
+
* @example
|
|
65
|
+
* ```ts
|
|
66
|
+
* sandbox: {
|
|
67
|
+
* url: 'http://localhost:8788'
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
25
71
|
url?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Enable verbose logging for the sandbox server.
|
|
74
|
+
*
|
|
75
|
+
* Shows detailed information about requests, database operations, and more.
|
|
76
|
+
* Equivalent to setting `logLevel: 'debug'`.
|
|
77
|
+
*
|
|
78
|
+
* @default false
|
|
79
|
+
* @example
|
|
80
|
+
* ```ts
|
|
81
|
+
* sandbox: {
|
|
82
|
+
* verbose: true
|
|
83
|
+
* }
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
26
86
|
verbose?: boolean;
|
|
27
87
|
/**
|
|
28
|
-
* Log level for the sandbox server
|
|
88
|
+
* Log level for the sandbox server.
|
|
89
|
+
*
|
|
90
|
+
* Controls the verbosity of sandbox server logs:
|
|
91
|
+
* - `'debug'`: Very detailed logs (all operations)
|
|
92
|
+
* - `'info'`: Standard operational logs
|
|
93
|
+
* - `'warn'`: Warnings only
|
|
94
|
+
* - `'error'`: Errors only
|
|
95
|
+
*
|
|
29
96
|
* @default 'info'
|
|
97
|
+
* @example
|
|
98
|
+
* ```ts
|
|
99
|
+
* sandbox: {
|
|
100
|
+
* logLevel: 'debug' // Show all debug info
|
|
101
|
+
* }
|
|
102
|
+
* ```
|
|
30
103
|
*/
|
|
31
104
|
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
32
105
|
/**
|
|
33
|
-
* Recreate the sandbox database on each start
|
|
106
|
+
* Recreate the sandbox database on each server start.
|
|
107
|
+
*
|
|
108
|
+
* When `true`, the database is dropped and recreated with seed data
|
|
109
|
+
* every time the dev server starts. Useful for ensuring a clean state
|
|
110
|
+
* during development.
|
|
111
|
+
*
|
|
112
|
+
* **Warning**: All existing data will be lost on each restart.
|
|
113
|
+
*
|
|
34
114
|
* @default false
|
|
115
|
+
* @example
|
|
116
|
+
* ```ts
|
|
117
|
+
* sandbox: {
|
|
118
|
+
* recreateDb: true // Fresh database on every restart
|
|
119
|
+
* }
|
|
120
|
+
* ```
|
|
35
121
|
*/
|
|
36
122
|
recreateDb?: boolean;
|
|
123
|
+
/**
|
|
124
|
+
* Seed the database with demo data on startup.
|
|
125
|
+
*
|
|
126
|
+
* Creates demo users, games, achievements, and other platform data
|
|
127
|
+
* for testing. Disable if you want to start with an empty database.
|
|
128
|
+
*
|
|
129
|
+
* @default true
|
|
130
|
+
* @example
|
|
131
|
+
* ```ts
|
|
132
|
+
* sandbox: {
|
|
133
|
+
* seed: false // Start with empty database
|
|
134
|
+
* }
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
seed?: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Use an in-memory database instead of a file.
|
|
140
|
+
*
|
|
141
|
+
* The database only exists in RAM and is lost when the server stops.
|
|
142
|
+
* Faster but non-persistent. Useful for testing and CI environments.
|
|
143
|
+
*
|
|
144
|
+
* @default false
|
|
145
|
+
* @example
|
|
146
|
+
* ```ts
|
|
147
|
+
* sandbox: {
|
|
148
|
+
* memoryOnly: true // Database in RAM only
|
|
149
|
+
* }
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
152
|
+
memoryOnly?: boolean;
|
|
153
|
+
/**
|
|
154
|
+
* Custom path for the database file.
|
|
155
|
+
*
|
|
156
|
+
* Specifies where the SQLite database file should be stored.
|
|
157
|
+
* If not provided, defaults to a path based on node_modules location.
|
|
158
|
+
*
|
|
159
|
+
* Special value `':memory:'` creates an in-memory database
|
|
160
|
+
* (equivalent to `memoryOnly: true`).
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* sandbox: {
|
|
165
|
+
* databasePath: './my-game-sandbox.db'
|
|
166
|
+
* }
|
|
167
|
+
* ```
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* sandbox: {
|
|
172
|
+
* databasePath: ':memory:' // In-memory database
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
databasePath?: string;
|
|
177
|
+
/**
|
|
178
|
+
* Real-time/WebSocket server configuration.
|
|
179
|
+
*
|
|
180
|
+
* The real-time server provides WebSocket support for live updates,
|
|
181
|
+
* multiplayer features, and other real-time functionality.
|
|
182
|
+
*
|
|
183
|
+
* @default { enabled: false }
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* sandbox: {
|
|
187
|
+
* realtime: {
|
|
188
|
+
* enabled: true,
|
|
189
|
+
* port: 4322 // Custom WebSocket port
|
|
190
|
+
* }
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
realtime?: {
|
|
195
|
+
/**
|
|
196
|
+
* Enable the real-time/WebSocket server.
|
|
197
|
+
*
|
|
198
|
+
* @default false
|
|
199
|
+
*/
|
|
200
|
+
enabled?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Port for the WebSocket server.
|
|
203
|
+
*
|
|
204
|
+
* By default, uses the HTTP port + 1 (e.g., if HTTP is 4321,
|
|
205
|
+
* WebSocket will be 4322).
|
|
206
|
+
*/
|
|
207
|
+
port?: number;
|
|
208
|
+
};
|
|
37
209
|
}
|
|
38
210
|
/**
|
|
39
211
|
* Configuration options for the development shell wrapper
|
|
212
|
+
*
|
|
213
|
+
* The shell provides the platform UI during development, including the
|
|
214
|
+
* Playcademy badge, game selection, and other platform features.
|
|
40
215
|
*/
|
|
41
216
|
export interface PlaycademyShellOptions {
|
|
42
217
|
/**
|
|
43
|
-
* Show the Playcademy badge in the corner during development
|
|
218
|
+
* Show the Playcademy badge in the corner during development.
|
|
219
|
+
*
|
|
44
220
|
* @default true
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* shell: {
|
|
224
|
+
* showBadge: false // Hide the badge
|
|
225
|
+
* }
|
|
226
|
+
* ```
|
|
45
227
|
*/
|
|
46
228
|
showBadge?: boolean;
|
|
47
229
|
}
|
|
48
230
|
/**
|
|
49
|
-
* Main
|
|
231
|
+
* Main configuration options for the Playcademy Vite plugin
|
|
232
|
+
*
|
|
233
|
+
* Configure how your game integrates with the Playcademy platform during
|
|
234
|
+
* development and build.
|
|
235
|
+
*
|
|
236
|
+
* @example
|
|
237
|
+
* ```ts
|
|
238
|
+
* // vite.config.ts
|
|
239
|
+
* import { playcademy } from '@playcademy/vite-plugin'
|
|
240
|
+
*
|
|
241
|
+
* export default defineConfig({
|
|
242
|
+
* plugins: [
|
|
243
|
+
* playcademy({
|
|
244
|
+
* mode: 'platform',
|
|
245
|
+
* sandbox: {
|
|
246
|
+
* logLevel: 'debug',
|
|
247
|
+
* recreateDb: true
|
|
248
|
+
* },
|
|
249
|
+
* shell: {
|
|
250
|
+
* showBadge: true
|
|
251
|
+
* }
|
|
252
|
+
* })
|
|
253
|
+
* ]
|
|
254
|
+
* })
|
|
255
|
+
* ```
|
|
50
256
|
*/
|
|
51
257
|
export interface PlaycademyPluginOptions {
|
|
52
258
|
/**
|
|
53
|
-
* Plugin operation mode
|
|
259
|
+
* Plugin operation mode.
|
|
260
|
+
*
|
|
261
|
+
* - `'platform'`: Full development experience with sandbox server and shell (recommended)
|
|
262
|
+
* - `'standalone'`: Backend bundling only, no platform features
|
|
263
|
+
*
|
|
264
|
+
* Most games should use `'platform'` mode.
|
|
265
|
+
*
|
|
54
266
|
* @default 'platform'
|
|
267
|
+
* @example
|
|
268
|
+
* ```ts
|
|
269
|
+
* {
|
|
270
|
+
* mode: 'standalone' // For testing backend in isolation
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
55
273
|
*/
|
|
56
274
|
mode?: PlaycademyMode;
|
|
275
|
+
/**
|
|
276
|
+
* Export/build configuration options.
|
|
277
|
+
*
|
|
278
|
+
* Controls how your game is packaged for deployment.
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* ```ts
|
|
282
|
+
* {
|
|
283
|
+
* export: {
|
|
284
|
+
* autoZip: true // Create deployment zip automatically
|
|
285
|
+
* }
|
|
286
|
+
* }
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
57
289
|
export?: PlaycademyExportOptions;
|
|
290
|
+
/**
|
|
291
|
+
* Sandbox server configuration options.
|
|
292
|
+
*
|
|
293
|
+
* The sandbox provides a local Playcademy platform environment for development.
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* ```ts
|
|
297
|
+
* {
|
|
298
|
+
* sandbox: {
|
|
299
|
+
* logLevel: 'debug',
|
|
300
|
+
* recreateDb: true
|
|
301
|
+
* }
|
|
302
|
+
* }
|
|
303
|
+
* ```
|
|
304
|
+
*/
|
|
58
305
|
sandbox?: PlaycademySandboxOptions;
|
|
306
|
+
/**
|
|
307
|
+
* Development shell configuration options.
|
|
308
|
+
*
|
|
309
|
+
* The shell wraps your game with platform UI during development.
|
|
310
|
+
*
|
|
311
|
+
* @example
|
|
312
|
+
* ```ts
|
|
313
|
+
* {
|
|
314
|
+
* shell: {
|
|
315
|
+
* showBadge: false
|
|
316
|
+
* }
|
|
317
|
+
* }
|
|
318
|
+
* ```
|
|
319
|
+
*/
|
|
59
320
|
shell?: PlaycademyShellOptions;
|
|
60
321
|
}
|