@playcademy/sandbox 0.3.17-beta.37 → 0.3.17-beta.38
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 +87 -47
- package/dist/constants.js +4 -2
- package/dist/server.js +87 -47
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -339,7 +339,8 @@ var PLAYCADEMY_BASE_URLS, GAME_WORKER_DOMAINS;
|
|
|
339
339
|
var init_domains = __esm(() => {
|
|
340
340
|
PLAYCADEMY_BASE_URLS = {
|
|
341
341
|
production: "https://hub.playcademy.net",
|
|
342
|
-
staging: "https://hub.dev.playcademy.net"
|
|
342
|
+
staging: "https://hub.dev.playcademy.net",
|
|
343
|
+
local: "http://localhost:5174"
|
|
343
344
|
};
|
|
344
345
|
GAME_WORKER_DOMAINS = {
|
|
345
346
|
production: "playcademy.gg",
|
|
@@ -447,7 +448,8 @@ var WORKER_NAMING, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE =
|
|
|
447
448
|
var init_workers = __esm(() => {
|
|
448
449
|
WORKER_NAMING = {
|
|
449
450
|
STAGING_PREFIX: "staging-",
|
|
450
|
-
STAGING_SUFFIX: "-staging"
|
|
451
|
+
STAGING_SUFFIX: "-staging",
|
|
452
|
+
LOCAL_PREFIX: "local-"
|
|
451
453
|
};
|
|
452
454
|
});
|
|
453
455
|
|
|
@@ -1330,7 +1332,7 @@ var package_default;
|
|
|
1330
1332
|
var init_package = __esm(() => {
|
|
1331
1333
|
package_default = {
|
|
1332
1334
|
name: "@playcademy/sandbox",
|
|
1333
|
-
version: "0.3.17-beta.
|
|
1335
|
+
version: "0.3.17-beta.38",
|
|
1334
1336
|
description: "Local development server for Playcademy game development",
|
|
1335
1337
|
type: "module",
|
|
1336
1338
|
exports: {
|
|
@@ -5875,21 +5877,16 @@ var init_esm = __esm(() => {
|
|
|
5875
5877
|
// ../api-core/src/config/schema.ts
|
|
5876
5878
|
function createMinimalConfig(overrides) {
|
|
5877
5879
|
return apiConfigSchema.parse({
|
|
5878
|
-
|
|
5879
|
-
isLocal: false,
|
|
5880
|
+
sstStage: "test",
|
|
5880
5881
|
...overrides
|
|
5881
5882
|
});
|
|
5882
5883
|
}
|
|
5883
5884
|
function getPlatformEnvironment(config2) {
|
|
5884
|
-
return config2.
|
|
5885
|
+
return config2.sstStage === "production" ? "production" : "staging";
|
|
5885
5886
|
}
|
|
5886
|
-
|
|
5887
|
-
return config2.stage === "production";
|
|
5888
|
-
}
|
|
5889
|
-
var stageSchema, ltiConfigSchema, realtimeConfigSchema, apiConfigSchema;
|
|
5887
|
+
var ltiConfigSchema, realtimeConfigSchema, apiConfigSchema;
|
|
5890
5888
|
var init_schema = __esm(() => {
|
|
5891
5889
|
init_esm();
|
|
5892
|
-
stageSchema = exports_external.enum(["production", "dev", "local"]);
|
|
5893
5890
|
ltiConfigSchema = exports_external.object({
|
|
5894
5891
|
audience: exports_external.string(),
|
|
5895
5892
|
jwksUrl: exports_external.string().url(),
|
|
@@ -5900,7 +5897,7 @@ var init_schema = __esm(() => {
|
|
|
5900
5897
|
publishSecret: exports_external.string()
|
|
5901
5898
|
});
|
|
5902
5899
|
apiConfigSchema = exports_external.object({
|
|
5903
|
-
|
|
5900
|
+
sstStage: exports_external.string(),
|
|
5904
5901
|
isLocal: exports_external.boolean().default(false),
|
|
5905
5902
|
baseUrl: exports_external.string().url().optional(),
|
|
5906
5903
|
gameDomain: exports_external.string().optional(),
|
|
@@ -26292,9 +26289,38 @@ var init_playcademy = __esm(() => {
|
|
|
26292
26289
|
init_infra();
|
|
26293
26290
|
});
|
|
26294
26291
|
|
|
26292
|
+
// ../utils/src/tunnel.ts
|
|
26293
|
+
async function getTunnelUrl() {
|
|
26294
|
+
let response;
|
|
26295
|
+
try {
|
|
26296
|
+
response = await fetch(`${METRICS_BASE}/config`);
|
|
26297
|
+
} catch {
|
|
26298
|
+
throw new Error("Local tunnel is not running. Start it with `bun dev` or `bun scripts/infra/tunnel.ts`.");
|
|
26299
|
+
}
|
|
26300
|
+
if (!response.ok) {
|
|
26301
|
+
throw new Error(`Tunnel metrics endpoint returned ${response.status}`);
|
|
26302
|
+
}
|
|
26303
|
+
const data = await response.json();
|
|
26304
|
+
const hostname = data.config.ingress.find((r) => r.hostname)?.hostname;
|
|
26305
|
+
if (!hostname) {
|
|
26306
|
+
throw new Error("Tunnel is running but no hostname found in ingress config");
|
|
26307
|
+
}
|
|
26308
|
+
return `https://${hostname}`;
|
|
26309
|
+
}
|
|
26310
|
+
var TUNNEL_METRICS_PORT = 20241, METRICS_BASE;
|
|
26311
|
+
var init_tunnel = __esm(() => {
|
|
26312
|
+
METRICS_BASE = `http://127.0.0.1:${TUNNEL_METRICS_PORT}`;
|
|
26313
|
+
});
|
|
26314
|
+
|
|
26295
26315
|
// ../api-core/src/utils/deployment.util.ts
|
|
26296
|
-
function getDeploymentId(gameSlug,
|
|
26297
|
-
|
|
26316
|
+
function getDeploymentId(gameSlug, sstStage) {
|
|
26317
|
+
if (sstStage === "production") {
|
|
26318
|
+
return gameSlug;
|
|
26319
|
+
}
|
|
26320
|
+
if (sstStage === "dev") {
|
|
26321
|
+
return `${WORKER_NAMING.STAGING_PREFIX}${gameSlug}`;
|
|
26322
|
+
}
|
|
26323
|
+
return `${WORKER_NAMING.LOCAL_PREFIX}${sstStage}-${gameSlug}`;
|
|
26298
26324
|
}
|
|
26299
26325
|
function getGameWorkerApiKeyName(slug) {
|
|
26300
26326
|
return `game-worker-${slug}`.substring(0, 32);
|
|
@@ -26519,8 +26545,7 @@ class DeployService {
|
|
|
26519
26545
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
26520
26546
|
const flags2 = this.validateDeployRequest(request, slug);
|
|
26521
26547
|
const { hasBackend, hasFrontend } = flags2;
|
|
26522
|
-
const
|
|
26523
|
-
const deploymentId = getDeploymentId(slug, isProd);
|
|
26548
|
+
const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
|
|
26524
26549
|
let frontendAssetsPath;
|
|
26525
26550
|
let tempDir;
|
|
26526
26551
|
if (hasFrontend) {
|
|
@@ -26534,7 +26559,15 @@ class DeployService {
|
|
|
26534
26559
|
frontendAssetsPath = extracted.assetsPath;
|
|
26535
26560
|
yield { type: "status", data: { message: "Extracting assets" } };
|
|
26536
26561
|
}
|
|
26537
|
-
|
|
26562
|
+
let platformBaseUrl = this.deps.config.baseUrl;
|
|
26563
|
+
if (this.deps.config.isLocal) {
|
|
26564
|
+
try {
|
|
26565
|
+
platformBaseUrl = await getTunnelUrl();
|
|
26566
|
+
} catch {
|
|
26567
|
+
throw new ValidationError("Local tunnel is not running. Ensure cloudflared is installed (`brew install cloudflared`) and the tunnel DevCommand started successfully.");
|
|
26568
|
+
}
|
|
26569
|
+
}
|
|
26570
|
+
const env = { GAME_ID: game.id, PLAYCADEMY_BASE_URL: platformBaseUrl };
|
|
26538
26571
|
yield {
|
|
26539
26572
|
type: "status",
|
|
26540
26573
|
data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
|
|
@@ -26583,14 +26616,14 @@ class DeployService {
|
|
|
26583
26616
|
return;
|
|
26584
26617
|
}
|
|
26585
26618
|
const workerBindings = {};
|
|
26586
|
-
if (bindings?.database
|
|
26587
|
-
workerBindings.d1 =
|
|
26619
|
+
if (bindings?.database) {
|
|
26620
|
+
workerBindings.d1 = [deploymentId];
|
|
26588
26621
|
}
|
|
26589
|
-
if (bindings?.keyValue
|
|
26590
|
-
workerBindings.kv =
|
|
26622
|
+
if (bindings?.keyValue) {
|
|
26623
|
+
workerBindings.kv = [deploymentId];
|
|
26591
26624
|
}
|
|
26592
|
-
if (bindings?.bucket
|
|
26593
|
-
workerBindings.r2 =
|
|
26625
|
+
if (bindings?.bucket) {
|
|
26626
|
+
workerBindings.r2 = [deploymentId];
|
|
26594
26627
|
}
|
|
26595
26628
|
if (bindings?.queues) {
|
|
26596
26629
|
let toQueueName = function(queueKey) {
|
|
@@ -26657,7 +26690,7 @@ var init_deploy_service = __esm(() => {
|
|
|
26657
26690
|
init_src();
|
|
26658
26691
|
init_tables_index();
|
|
26659
26692
|
init_src2();
|
|
26660
|
-
|
|
26693
|
+
init_tunnel();
|
|
26661
26694
|
init_errors();
|
|
26662
26695
|
init_deployment_util();
|
|
26663
26696
|
logger3 = log.scope("DeployService");
|
|
@@ -28217,8 +28250,7 @@ class DatabaseService {
|
|
|
28217
28250
|
async reset(slug, user, schema2) {
|
|
28218
28251
|
const d1 = this.getD1();
|
|
28219
28252
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
28220
|
-
const
|
|
28221
|
-
const deploymentId = getDeploymentId(slug, isProd);
|
|
28253
|
+
const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
|
|
28222
28254
|
logger9.debug("Resetting database", {
|
|
28223
28255
|
userId: user.id,
|
|
28224
28256
|
gameId: game.id,
|
|
@@ -28294,7 +28326,6 @@ var init_database_service = __esm(() => {
|
|
|
28294
28326
|
init_drizzle_orm();
|
|
28295
28327
|
init_tables_index();
|
|
28296
28328
|
init_src2();
|
|
28297
|
-
init_config2();
|
|
28298
28329
|
init_errors();
|
|
28299
28330
|
init_deployment_util();
|
|
28300
28331
|
logger9 = log.scope("DatabaseService");
|
|
@@ -28813,8 +28844,7 @@ class SecretsService {
|
|
|
28813
28844
|
return this.deps.cloudflare;
|
|
28814
28845
|
}
|
|
28815
28846
|
getDeploymentId(slug) {
|
|
28816
|
-
|
|
28817
|
-
return getDeploymentId(slug, isProd);
|
|
28847
|
+
return getDeploymentId(slug, this.deps.config.sstStage);
|
|
28818
28848
|
}
|
|
28819
28849
|
async listKeys(slug, user) {
|
|
28820
28850
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
@@ -28942,7 +28972,6 @@ var logger13, INTERNAL_SECRET_KEYS;
|
|
|
28942
28972
|
var init_secrets_service = __esm(() => {
|
|
28943
28973
|
init_src();
|
|
28944
28974
|
init_src2();
|
|
28945
|
-
init_config2();
|
|
28946
28975
|
init_errors();
|
|
28947
28976
|
init_deployment_util();
|
|
28948
28977
|
logger13 = log.scope("SecretsService");
|
|
@@ -28994,8 +29023,7 @@ class SeedService {
|
|
|
28994
29023
|
async seed(slug, code, user, secrets) {
|
|
28995
29024
|
const cf = this.getCloudflare();
|
|
28996
29025
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
28997
|
-
const
|
|
28998
|
-
const deploymentId = getDeploymentId(slug, isProd);
|
|
29026
|
+
const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
|
|
28999
29027
|
const uniqueSuffix = Date.now().toString(36);
|
|
29000
29028
|
const seedDeploymentId = `seed-${deploymentId}-${uniqueSuffix}`;
|
|
29001
29029
|
logger14.debug("Seeding database", {
|
|
@@ -29234,7 +29262,6 @@ var init_seed_service = __esm(() => {
|
|
|
29234
29262
|
init_src();
|
|
29235
29263
|
init_setup2();
|
|
29236
29264
|
init_src2();
|
|
29237
|
-
init_config2();
|
|
29238
29265
|
init_errors();
|
|
29239
29266
|
init_deployment_util();
|
|
29240
29267
|
logger14 = log.scope("SeedService");
|
|
@@ -34885,7 +34912,7 @@ class LogsService {
|
|
|
34885
34912
|
constructor(deps) {
|
|
34886
34913
|
this.deps = deps;
|
|
34887
34914
|
}
|
|
34888
|
-
async generateToken(user, slug2,
|
|
34915
|
+
async generateToken(user, slug2, sstStage) {
|
|
34889
34916
|
const db2 = this.deps.db;
|
|
34890
34917
|
if (user.role === "admin") {
|
|
34891
34918
|
const game = await db2.query.games.findFirst({
|
|
@@ -34895,7 +34922,7 @@ class LogsService {
|
|
|
34895
34922
|
if (!game) {
|
|
34896
34923
|
throw new NotFoundError("Game", slug2);
|
|
34897
34924
|
}
|
|
34898
|
-
logger28.info("Admin accessing game logs", { adminId: user.id, slug: slug2,
|
|
34925
|
+
logger28.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
|
|
34899
34926
|
} else {
|
|
34900
34927
|
const isApprovedDev = user.developerStatus === "approved";
|
|
34901
34928
|
if (!isApprovedDev) {
|
|
@@ -34921,8 +34948,7 @@ class LogsService {
|
|
|
34921
34948
|
throw new NotFoundError("Game", slug2);
|
|
34922
34949
|
}
|
|
34923
34950
|
}
|
|
34924
|
-
const
|
|
34925
|
-
const workerId = getDeploymentId(slug2, isProduction3);
|
|
34951
|
+
const workerId = getDeploymentId(slug2, sstStage);
|
|
34926
34952
|
const token = await this.deps.mintLogStreamToken(user.id, workerId);
|
|
34927
34953
|
logger28.debug("Generated log stream token", {
|
|
34928
34954
|
userId: user.id,
|
|
@@ -35668,7 +35694,7 @@ function createServices(ctx) {
|
|
|
35668
35694
|
discord,
|
|
35669
35695
|
cloudflare,
|
|
35670
35696
|
storage,
|
|
35671
|
-
stage: config2.
|
|
35697
|
+
stage: config2.sstStage
|
|
35672
35698
|
});
|
|
35673
35699
|
const player = createPlayerServices({
|
|
35674
35700
|
db: db2,
|
|
@@ -38902,7 +38928,7 @@ var init_providers = __esm(() => {
|
|
|
38902
38928
|
function buildConfig(options) {
|
|
38903
38929
|
const baseUrl = `http://localhost:${options.port ?? 3000}`;
|
|
38904
38930
|
return createMinimalConfig({
|
|
38905
|
-
|
|
38931
|
+
sstStage: "sandbox",
|
|
38906
38932
|
baseUrl,
|
|
38907
38933
|
gameDomain: "localhost",
|
|
38908
38934
|
uploadBucket: "sandbox-uploads",
|
|
@@ -38939,7 +38965,7 @@ function createSandboxContext(options) {
|
|
|
38939
38965
|
Object.assign(services, createServices(ctx));
|
|
38940
38966
|
cachedServiceContext = ctx;
|
|
38941
38967
|
log.debug("[Sandbox] ServiceContext initialized", {
|
|
38942
|
-
|
|
38968
|
+
sstStage: config2.sstStage,
|
|
38943
38969
|
baseUrl: config2.baseUrl,
|
|
38944
38970
|
hasTimeback: Boolean(timeback2)
|
|
38945
38971
|
});
|
|
@@ -94941,9 +94967,9 @@ var init_schemas2 = __esm(() => {
|
|
|
94941
94967
|
compatibilityDate: exports_external.string().optional(),
|
|
94942
94968
|
compatibilityFlags: exports_external.array(exports_external.string()).optional(),
|
|
94943
94969
|
bindings: exports_external.object({
|
|
94944
|
-
database: exports_external.array(exports_external.string()).optional(),
|
|
94945
|
-
keyValue: exports_external.array(exports_external.string()).optional(),
|
|
94946
|
-
bucket: exports_external.array(exports_external.string()).optional(),
|
|
94970
|
+
database: exports_external.union([exports_external.literal(true), exports_external.array(exports_external.string())]).optional(),
|
|
94971
|
+
keyValue: exports_external.union([exports_external.literal(true), exports_external.array(exports_external.string())]).optional(),
|
|
94972
|
+
bucket: exports_external.union([exports_external.literal(true), exports_external.array(exports_external.string())]).optional(),
|
|
94947
94973
|
queues: exports_external.record(exports_external.string(), exports_external.union([
|
|
94948
94974
|
exports_external.literal(true),
|
|
94949
94975
|
exports_external.object({
|
|
@@ -96914,8 +96940,8 @@ var init_logs_controller = __esm(() => {
|
|
|
96914
96940
|
let body2;
|
|
96915
96941
|
try {
|
|
96916
96942
|
const json4 = await ctx.request.json();
|
|
96917
|
-
if (json4.environment !== "staging" && json4.environment !== "production") {
|
|
96918
|
-
throw ApiError.badRequest('Invalid environment. Must be "staging" or "production".');
|
|
96943
|
+
if (json4.environment !== "local" && json4.environment !== "staging" && json4.environment !== "production") {
|
|
96944
|
+
throw ApiError.badRequest('Invalid environment. Must be "local", "staging", or "production".');
|
|
96919
96945
|
}
|
|
96920
96946
|
body2 = json4;
|
|
96921
96947
|
} catch (error2) {
|
|
@@ -96929,7 +96955,13 @@ var init_logs_controller = __esm(() => {
|
|
|
96929
96955
|
slug: slug2,
|
|
96930
96956
|
environment: body2.environment
|
|
96931
96957
|
});
|
|
96932
|
-
|
|
96958
|
+
const envToSstStage = {
|
|
96959
|
+
local: ctx.config.sstStage,
|
|
96960
|
+
staging: "dev",
|
|
96961
|
+
production: "production"
|
|
96962
|
+
};
|
|
96963
|
+
const sstStage = envToSstStage[body2.environment] ?? "dev";
|
|
96964
|
+
return ctx.services.logs.generateToken(ctx.user, slug2, sstStage);
|
|
96933
96965
|
});
|
|
96934
96966
|
logs = {
|
|
96935
96967
|
generateToken
|
|
@@ -97281,6 +97313,7 @@ var init_seed_controller = __esm(() => {
|
|
|
97281
97313
|
var logger61, start2, end, mintToken, sessions2;
|
|
97282
97314
|
var init_session_controller = __esm(() => {
|
|
97283
97315
|
init_src2();
|
|
97316
|
+
init_tunnel();
|
|
97284
97317
|
init_errors();
|
|
97285
97318
|
init_utils11();
|
|
97286
97319
|
logger61 = log.scope("SessionController");
|
|
@@ -97315,7 +97348,14 @@ var init_session_controller = __esm(() => {
|
|
|
97315
97348
|
throw ApiError.badRequest("Missing game ID or slug");
|
|
97316
97349
|
}
|
|
97317
97350
|
logger61.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
|
|
97318
|
-
|
|
97351
|
+
const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
|
|
97352
|
+
let baseUrl;
|
|
97353
|
+
if (ctx.config.isLocal) {
|
|
97354
|
+
try {
|
|
97355
|
+
baseUrl = await getTunnelUrl();
|
|
97356
|
+
} catch {}
|
|
97357
|
+
}
|
|
97358
|
+
return { token, exp, baseUrl };
|
|
97319
97359
|
});
|
|
97320
97360
|
sessions2 = {
|
|
97321
97361
|
start: start2,
|
package/dist/constants.js
CHANGED
|
@@ -174,7 +174,8 @@ var PLAYCADEMY_BASE_URLS, GAME_WORKER_DOMAINS;
|
|
|
174
174
|
var init_domains = __esm(() => {
|
|
175
175
|
PLAYCADEMY_BASE_URLS = {
|
|
176
176
|
production: "https://hub.playcademy.net",
|
|
177
|
-
staging: "https://hub.dev.playcademy.net"
|
|
177
|
+
staging: "https://hub.dev.playcademy.net",
|
|
178
|
+
local: "http://localhost:5174"
|
|
178
179
|
};
|
|
179
180
|
GAME_WORKER_DOMAINS = {
|
|
180
181
|
production: "playcademy.gg",
|
|
@@ -282,7 +283,8 @@ var WORKER_NAMING, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE =
|
|
|
282
283
|
var init_workers = __esm(() => {
|
|
283
284
|
WORKER_NAMING = {
|
|
284
285
|
STAGING_PREFIX: "staging-",
|
|
285
|
-
STAGING_SUFFIX: "-staging"
|
|
286
|
+
STAGING_SUFFIX: "-staging",
|
|
287
|
+
LOCAL_PREFIX: "local-"
|
|
286
288
|
};
|
|
287
289
|
});
|
|
288
290
|
|
package/dist/server.js
CHANGED
|
@@ -338,7 +338,8 @@ var PLAYCADEMY_BASE_URLS, GAME_WORKER_DOMAINS;
|
|
|
338
338
|
var init_domains = __esm(() => {
|
|
339
339
|
PLAYCADEMY_BASE_URLS = {
|
|
340
340
|
production: "https://hub.playcademy.net",
|
|
341
|
-
staging: "https://hub.dev.playcademy.net"
|
|
341
|
+
staging: "https://hub.dev.playcademy.net",
|
|
342
|
+
local: "http://localhost:5174"
|
|
342
343
|
};
|
|
343
344
|
GAME_WORKER_DOMAINS = {
|
|
344
345
|
production: "playcademy.gg",
|
|
@@ -446,7 +447,8 @@ var WORKER_NAMING, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE =
|
|
|
446
447
|
var init_workers = __esm(() => {
|
|
447
448
|
WORKER_NAMING = {
|
|
448
449
|
STAGING_PREFIX: "staging-",
|
|
449
|
-
STAGING_SUFFIX: "-staging"
|
|
450
|
+
STAGING_SUFFIX: "-staging",
|
|
451
|
+
LOCAL_PREFIX: "local-"
|
|
450
452
|
};
|
|
451
453
|
});
|
|
452
454
|
|
|
@@ -1329,7 +1331,7 @@ var package_default;
|
|
|
1329
1331
|
var init_package = __esm(() => {
|
|
1330
1332
|
package_default = {
|
|
1331
1333
|
name: "@playcademy/sandbox",
|
|
1332
|
-
version: "0.3.17-beta.
|
|
1334
|
+
version: "0.3.17-beta.38",
|
|
1333
1335
|
description: "Local development server for Playcademy game development",
|
|
1334
1336
|
type: "module",
|
|
1335
1337
|
exports: {
|
|
@@ -5874,21 +5876,16 @@ var init_esm = __esm(() => {
|
|
|
5874
5876
|
// ../api-core/src/config/schema.ts
|
|
5875
5877
|
function createMinimalConfig(overrides) {
|
|
5876
5878
|
return apiConfigSchema.parse({
|
|
5877
|
-
|
|
5878
|
-
isLocal: false,
|
|
5879
|
+
sstStage: "test",
|
|
5879
5880
|
...overrides
|
|
5880
5881
|
});
|
|
5881
5882
|
}
|
|
5882
5883
|
function getPlatformEnvironment(config2) {
|
|
5883
|
-
return config2.
|
|
5884
|
+
return config2.sstStage === "production" ? "production" : "staging";
|
|
5884
5885
|
}
|
|
5885
|
-
|
|
5886
|
-
return config2.stage === "production";
|
|
5887
|
-
}
|
|
5888
|
-
var stageSchema, ltiConfigSchema, realtimeConfigSchema, apiConfigSchema;
|
|
5886
|
+
var ltiConfigSchema, realtimeConfigSchema, apiConfigSchema;
|
|
5889
5887
|
var init_schema = __esm(() => {
|
|
5890
5888
|
init_esm();
|
|
5891
|
-
stageSchema = exports_external.enum(["production", "dev", "local"]);
|
|
5892
5889
|
ltiConfigSchema = exports_external.object({
|
|
5893
5890
|
audience: exports_external.string(),
|
|
5894
5891
|
jwksUrl: exports_external.string().url(),
|
|
@@ -5899,7 +5896,7 @@ var init_schema = __esm(() => {
|
|
|
5899
5896
|
publishSecret: exports_external.string()
|
|
5900
5897
|
});
|
|
5901
5898
|
apiConfigSchema = exports_external.object({
|
|
5902
|
-
|
|
5899
|
+
sstStage: exports_external.string(),
|
|
5903
5900
|
isLocal: exports_external.boolean().default(false),
|
|
5904
5901
|
baseUrl: exports_external.string().url().optional(),
|
|
5905
5902
|
gameDomain: exports_external.string().optional(),
|
|
@@ -26291,9 +26288,38 @@ var init_playcademy = __esm(() => {
|
|
|
26291
26288
|
init_infra();
|
|
26292
26289
|
});
|
|
26293
26290
|
|
|
26291
|
+
// ../utils/src/tunnel.ts
|
|
26292
|
+
async function getTunnelUrl() {
|
|
26293
|
+
let response;
|
|
26294
|
+
try {
|
|
26295
|
+
response = await fetch(`${METRICS_BASE}/config`);
|
|
26296
|
+
} catch {
|
|
26297
|
+
throw new Error("Local tunnel is not running. Start it with `bun dev` or `bun scripts/infra/tunnel.ts`.");
|
|
26298
|
+
}
|
|
26299
|
+
if (!response.ok) {
|
|
26300
|
+
throw new Error(`Tunnel metrics endpoint returned ${response.status}`);
|
|
26301
|
+
}
|
|
26302
|
+
const data = await response.json();
|
|
26303
|
+
const hostname = data.config.ingress.find((r) => r.hostname)?.hostname;
|
|
26304
|
+
if (!hostname) {
|
|
26305
|
+
throw new Error("Tunnel is running but no hostname found in ingress config");
|
|
26306
|
+
}
|
|
26307
|
+
return `https://${hostname}`;
|
|
26308
|
+
}
|
|
26309
|
+
var TUNNEL_METRICS_PORT = 20241, METRICS_BASE;
|
|
26310
|
+
var init_tunnel = __esm(() => {
|
|
26311
|
+
METRICS_BASE = `http://127.0.0.1:${TUNNEL_METRICS_PORT}`;
|
|
26312
|
+
});
|
|
26313
|
+
|
|
26294
26314
|
// ../api-core/src/utils/deployment.util.ts
|
|
26295
|
-
function getDeploymentId(gameSlug,
|
|
26296
|
-
|
|
26315
|
+
function getDeploymentId(gameSlug, sstStage) {
|
|
26316
|
+
if (sstStage === "production") {
|
|
26317
|
+
return gameSlug;
|
|
26318
|
+
}
|
|
26319
|
+
if (sstStage === "dev") {
|
|
26320
|
+
return `${WORKER_NAMING.STAGING_PREFIX}${gameSlug}`;
|
|
26321
|
+
}
|
|
26322
|
+
return `${WORKER_NAMING.LOCAL_PREFIX}${sstStage}-${gameSlug}`;
|
|
26297
26323
|
}
|
|
26298
26324
|
function getGameWorkerApiKeyName(slug) {
|
|
26299
26325
|
return `game-worker-${slug}`.substring(0, 32);
|
|
@@ -26518,8 +26544,7 @@ class DeployService {
|
|
|
26518
26544
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
26519
26545
|
const flags2 = this.validateDeployRequest(request, slug);
|
|
26520
26546
|
const { hasBackend, hasFrontend } = flags2;
|
|
26521
|
-
const
|
|
26522
|
-
const deploymentId = getDeploymentId(slug, isProd);
|
|
26547
|
+
const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
|
|
26523
26548
|
let frontendAssetsPath;
|
|
26524
26549
|
let tempDir;
|
|
26525
26550
|
if (hasFrontend) {
|
|
@@ -26533,7 +26558,15 @@ class DeployService {
|
|
|
26533
26558
|
frontendAssetsPath = extracted.assetsPath;
|
|
26534
26559
|
yield { type: "status", data: { message: "Extracting assets" } };
|
|
26535
26560
|
}
|
|
26536
|
-
|
|
26561
|
+
let platformBaseUrl = this.deps.config.baseUrl;
|
|
26562
|
+
if (this.deps.config.isLocal) {
|
|
26563
|
+
try {
|
|
26564
|
+
platformBaseUrl = await getTunnelUrl();
|
|
26565
|
+
} catch {
|
|
26566
|
+
throw new ValidationError("Local tunnel is not running. Ensure cloudflared is installed (`brew install cloudflared`) and the tunnel DevCommand started successfully.");
|
|
26567
|
+
}
|
|
26568
|
+
}
|
|
26569
|
+
const env = { GAME_ID: game.id, PLAYCADEMY_BASE_URL: platformBaseUrl };
|
|
26537
26570
|
yield {
|
|
26538
26571
|
type: "status",
|
|
26539
26572
|
data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
|
|
@@ -26582,14 +26615,14 @@ class DeployService {
|
|
|
26582
26615
|
return;
|
|
26583
26616
|
}
|
|
26584
26617
|
const workerBindings = {};
|
|
26585
|
-
if (bindings?.database
|
|
26586
|
-
workerBindings.d1 =
|
|
26618
|
+
if (bindings?.database) {
|
|
26619
|
+
workerBindings.d1 = [deploymentId];
|
|
26587
26620
|
}
|
|
26588
|
-
if (bindings?.keyValue
|
|
26589
|
-
workerBindings.kv =
|
|
26621
|
+
if (bindings?.keyValue) {
|
|
26622
|
+
workerBindings.kv = [deploymentId];
|
|
26590
26623
|
}
|
|
26591
|
-
if (bindings?.bucket
|
|
26592
|
-
workerBindings.r2 =
|
|
26624
|
+
if (bindings?.bucket) {
|
|
26625
|
+
workerBindings.r2 = [deploymentId];
|
|
26593
26626
|
}
|
|
26594
26627
|
if (bindings?.queues) {
|
|
26595
26628
|
let toQueueName = function(queueKey) {
|
|
@@ -26656,7 +26689,7 @@ var init_deploy_service = __esm(() => {
|
|
|
26656
26689
|
init_src();
|
|
26657
26690
|
init_tables_index();
|
|
26658
26691
|
init_src2();
|
|
26659
|
-
|
|
26692
|
+
init_tunnel();
|
|
26660
26693
|
init_errors();
|
|
26661
26694
|
init_deployment_util();
|
|
26662
26695
|
logger3 = log.scope("DeployService");
|
|
@@ -28216,8 +28249,7 @@ class DatabaseService {
|
|
|
28216
28249
|
async reset(slug, user, schema2) {
|
|
28217
28250
|
const d1 = this.getD1();
|
|
28218
28251
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
28219
|
-
const
|
|
28220
|
-
const deploymentId = getDeploymentId(slug, isProd);
|
|
28252
|
+
const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
|
|
28221
28253
|
logger9.debug("Resetting database", {
|
|
28222
28254
|
userId: user.id,
|
|
28223
28255
|
gameId: game.id,
|
|
@@ -28293,7 +28325,6 @@ var init_database_service = __esm(() => {
|
|
|
28293
28325
|
init_drizzle_orm();
|
|
28294
28326
|
init_tables_index();
|
|
28295
28327
|
init_src2();
|
|
28296
|
-
init_config2();
|
|
28297
28328
|
init_errors();
|
|
28298
28329
|
init_deployment_util();
|
|
28299
28330
|
logger9 = log.scope("DatabaseService");
|
|
@@ -28812,8 +28843,7 @@ class SecretsService {
|
|
|
28812
28843
|
return this.deps.cloudflare;
|
|
28813
28844
|
}
|
|
28814
28845
|
getDeploymentId(slug) {
|
|
28815
|
-
|
|
28816
|
-
return getDeploymentId(slug, isProd);
|
|
28846
|
+
return getDeploymentId(slug, this.deps.config.sstStage);
|
|
28817
28847
|
}
|
|
28818
28848
|
async listKeys(slug, user) {
|
|
28819
28849
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
@@ -28941,7 +28971,6 @@ var logger13, INTERNAL_SECRET_KEYS;
|
|
|
28941
28971
|
var init_secrets_service = __esm(() => {
|
|
28942
28972
|
init_src();
|
|
28943
28973
|
init_src2();
|
|
28944
|
-
init_config2();
|
|
28945
28974
|
init_errors();
|
|
28946
28975
|
init_deployment_util();
|
|
28947
28976
|
logger13 = log.scope("SecretsService");
|
|
@@ -28993,8 +29022,7 @@ class SeedService {
|
|
|
28993
29022
|
async seed(slug, code, user, secrets) {
|
|
28994
29023
|
const cf = this.getCloudflare();
|
|
28995
29024
|
const game = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
28996
|
-
const
|
|
28997
|
-
const deploymentId = getDeploymentId(slug, isProd);
|
|
29025
|
+
const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
|
|
28998
29026
|
const uniqueSuffix = Date.now().toString(36);
|
|
28999
29027
|
const seedDeploymentId = `seed-${deploymentId}-${uniqueSuffix}`;
|
|
29000
29028
|
logger14.debug("Seeding database", {
|
|
@@ -29233,7 +29261,6 @@ var init_seed_service = __esm(() => {
|
|
|
29233
29261
|
init_src();
|
|
29234
29262
|
init_setup2();
|
|
29235
29263
|
init_src2();
|
|
29236
|
-
init_config2();
|
|
29237
29264
|
init_errors();
|
|
29238
29265
|
init_deployment_util();
|
|
29239
29266
|
logger14 = log.scope("SeedService");
|
|
@@ -34884,7 +34911,7 @@ class LogsService {
|
|
|
34884
34911
|
constructor(deps) {
|
|
34885
34912
|
this.deps = deps;
|
|
34886
34913
|
}
|
|
34887
|
-
async generateToken(user, slug2,
|
|
34914
|
+
async generateToken(user, slug2, sstStage) {
|
|
34888
34915
|
const db2 = this.deps.db;
|
|
34889
34916
|
if (user.role === "admin") {
|
|
34890
34917
|
const game = await db2.query.games.findFirst({
|
|
@@ -34894,7 +34921,7 @@ class LogsService {
|
|
|
34894
34921
|
if (!game) {
|
|
34895
34922
|
throw new NotFoundError("Game", slug2);
|
|
34896
34923
|
}
|
|
34897
|
-
logger28.info("Admin accessing game logs", { adminId: user.id, slug: slug2,
|
|
34924
|
+
logger28.info("Admin accessing game logs", { adminId: user.id, slug: slug2, sstStage });
|
|
34898
34925
|
} else {
|
|
34899
34926
|
const isApprovedDev = user.developerStatus === "approved";
|
|
34900
34927
|
if (!isApprovedDev) {
|
|
@@ -34920,8 +34947,7 @@ class LogsService {
|
|
|
34920
34947
|
throw new NotFoundError("Game", slug2);
|
|
34921
34948
|
}
|
|
34922
34949
|
}
|
|
34923
|
-
const
|
|
34924
|
-
const workerId = getDeploymentId(slug2, isProduction3);
|
|
34950
|
+
const workerId = getDeploymentId(slug2, sstStage);
|
|
34925
34951
|
const token = await this.deps.mintLogStreamToken(user.id, workerId);
|
|
34926
34952
|
logger28.debug("Generated log stream token", {
|
|
34927
34953
|
userId: user.id,
|
|
@@ -35667,7 +35693,7 @@ function createServices(ctx) {
|
|
|
35667
35693
|
discord,
|
|
35668
35694
|
cloudflare,
|
|
35669
35695
|
storage,
|
|
35670
|
-
stage: config2.
|
|
35696
|
+
stage: config2.sstStage
|
|
35671
35697
|
});
|
|
35672
35698
|
const player = createPlayerServices({
|
|
35673
35699
|
db: db2,
|
|
@@ -38901,7 +38927,7 @@ var init_providers = __esm(() => {
|
|
|
38901
38927
|
function buildConfig(options) {
|
|
38902
38928
|
const baseUrl = `http://localhost:${options.port ?? 3000}`;
|
|
38903
38929
|
return createMinimalConfig({
|
|
38904
|
-
|
|
38930
|
+
sstStage: "sandbox",
|
|
38905
38931
|
baseUrl,
|
|
38906
38932
|
gameDomain: "localhost",
|
|
38907
38933
|
uploadBucket: "sandbox-uploads",
|
|
@@ -38938,7 +38964,7 @@ function createSandboxContext(options) {
|
|
|
38938
38964
|
Object.assign(services, createServices(ctx));
|
|
38939
38965
|
cachedServiceContext = ctx;
|
|
38940
38966
|
log.debug("[Sandbox] ServiceContext initialized", {
|
|
38941
|
-
|
|
38967
|
+
sstStage: config2.sstStage,
|
|
38942
38968
|
baseUrl: config2.baseUrl,
|
|
38943
38969
|
hasTimeback: Boolean(timeback2)
|
|
38944
38970
|
});
|
|
@@ -94940,9 +94966,9 @@ var init_schemas2 = __esm(() => {
|
|
|
94940
94966
|
compatibilityDate: exports_external.string().optional(),
|
|
94941
94967
|
compatibilityFlags: exports_external.array(exports_external.string()).optional(),
|
|
94942
94968
|
bindings: exports_external.object({
|
|
94943
|
-
database: exports_external.array(exports_external.string()).optional(),
|
|
94944
|
-
keyValue: exports_external.array(exports_external.string()).optional(),
|
|
94945
|
-
bucket: exports_external.array(exports_external.string()).optional(),
|
|
94969
|
+
database: exports_external.union([exports_external.literal(true), exports_external.array(exports_external.string())]).optional(),
|
|
94970
|
+
keyValue: exports_external.union([exports_external.literal(true), exports_external.array(exports_external.string())]).optional(),
|
|
94971
|
+
bucket: exports_external.union([exports_external.literal(true), exports_external.array(exports_external.string())]).optional(),
|
|
94946
94972
|
queues: exports_external.record(exports_external.string(), exports_external.union([
|
|
94947
94973
|
exports_external.literal(true),
|
|
94948
94974
|
exports_external.object({
|
|
@@ -96913,8 +96939,8 @@ var init_logs_controller = __esm(() => {
|
|
|
96913
96939
|
let body2;
|
|
96914
96940
|
try {
|
|
96915
96941
|
const json4 = await ctx.request.json();
|
|
96916
|
-
if (json4.environment !== "staging" && json4.environment !== "production") {
|
|
96917
|
-
throw ApiError.badRequest('Invalid environment. Must be "staging" or "production".');
|
|
96942
|
+
if (json4.environment !== "local" && json4.environment !== "staging" && json4.environment !== "production") {
|
|
96943
|
+
throw ApiError.badRequest('Invalid environment. Must be "local", "staging", or "production".');
|
|
96918
96944
|
}
|
|
96919
96945
|
body2 = json4;
|
|
96920
96946
|
} catch (error2) {
|
|
@@ -96928,7 +96954,13 @@ var init_logs_controller = __esm(() => {
|
|
|
96928
96954
|
slug: slug2,
|
|
96929
96955
|
environment: body2.environment
|
|
96930
96956
|
});
|
|
96931
|
-
|
|
96957
|
+
const envToSstStage = {
|
|
96958
|
+
local: ctx.config.sstStage,
|
|
96959
|
+
staging: "dev",
|
|
96960
|
+
production: "production"
|
|
96961
|
+
};
|
|
96962
|
+
const sstStage = envToSstStage[body2.environment] ?? "dev";
|
|
96963
|
+
return ctx.services.logs.generateToken(ctx.user, slug2, sstStage);
|
|
96932
96964
|
});
|
|
96933
96965
|
logs = {
|
|
96934
96966
|
generateToken
|
|
@@ -97280,6 +97312,7 @@ var init_seed_controller = __esm(() => {
|
|
|
97280
97312
|
var logger61, start2, end, mintToken, sessions2;
|
|
97281
97313
|
var init_session_controller = __esm(() => {
|
|
97282
97314
|
init_src2();
|
|
97315
|
+
init_tunnel();
|
|
97283
97316
|
init_errors();
|
|
97284
97317
|
init_utils11();
|
|
97285
97318
|
logger61 = log.scope("SessionController");
|
|
@@ -97314,7 +97347,14 @@ var init_session_controller = __esm(() => {
|
|
|
97314
97347
|
throw ApiError.badRequest("Missing game ID or slug");
|
|
97315
97348
|
}
|
|
97316
97349
|
logger61.debug("Minting token", { userId: ctx.user.id, gameIdOrSlug, launchId: ctx.launchId });
|
|
97317
|
-
|
|
97350
|
+
const { token, exp } = await ctx.services.session.mintToken(gameIdOrSlug, ctx.user.id);
|
|
97351
|
+
let baseUrl;
|
|
97352
|
+
if (ctx.config.isLocal) {
|
|
97353
|
+
try {
|
|
97354
|
+
baseUrl = await getTunnelUrl();
|
|
97355
|
+
} catch {}
|
|
97356
|
+
}
|
|
97357
|
+
return { token, exp, baseUrl };
|
|
97318
97358
|
});
|
|
97319
97359
|
sessions2 = {
|
|
97320
97360
|
start: start2,
|