@playcademy/sandbox 0.5.1-beta.4 → 0.5.1-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -979,10 +979,10 @@ var init_dist = __esm(() => {
979
979
  // ../utils/src/port.ts
980
980
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
981
981
  import { createServer } from "node:net";
982
- import { homedir } from "node:os";
982
+ import * as os from "node:os";
983
983
  import { join } from "node:path";
984
984
  function getRegistryPath() {
985
- const home = homedir();
985
+ const home = os.homedir();
986
986
  const dir = join(home, ".playcademy");
987
987
  if (!existsSync(dir)) {
988
988
  mkdirSync(dir, { recursive: true });
@@ -1078,7 +1078,7 @@ var package_default;
1078
1078
  var init_package = __esm(() => {
1079
1079
  package_default = {
1080
1080
  name: "@playcademy/sandbox",
1081
- version: "0.5.1-beta.4",
1081
+ version: "0.5.1-beta.6",
1082
1082
  description: "Local development server for Playcademy game development",
1083
1083
  type: "module",
1084
1084
  exports: {
@@ -22505,7 +22505,6 @@ class DeployJobService {
22505
22505
  "app.deploy_job.outcome": "succeeded",
22506
22506
  "app.deploy_job.run_duration_ms": DeployJobService.elapsedMsSince(new Date(runStartedAt))
22507
22507
  });
22508
- await this.deps.cache.refreshGameOrigins().catch(catchAttrs("deploy_job.cache_refresh"));
22509
22508
  await this.deleteCodeBundle(jobId);
22510
22509
  } catch (error) {
22511
22510
  clearInterval(heartbeat);
@@ -26423,7 +26422,7 @@ ${file}:${line3}:${column2}: ERROR: ${pluginText}${e.text}`;
26423
26422
  return result;
26424
26423
  }
26425
26424
  var fs2 = __require("fs");
26426
- var os = __require("os");
26425
+ var os2 = __require("os");
26427
26426
  var path = __require("path");
26428
26427
  var ESBUILD_BINARY_PATH = process.env.ESBUILD_BINARY_PATH || ESBUILD_BINARY_PATH;
26429
26428
  var isValidBinaryPath = (x) => !!x && x !== "/usr/bin/esbuild";
@@ -26465,7 +26464,7 @@ ${file}:${line3}:${column2}: ERROR: ${pluginText}${e.text}`;
26465
26464
  let pkg;
26466
26465
  let subpath;
26467
26466
  let isWASM = false;
26468
- let platformKey = `${process.platform} ${os.arch()} ${os.endianness()}`;
26467
+ let platformKey = `${process.platform} ${os2.arch()} ${os2.endianness()}`;
26469
26468
  if (platformKey in knownWindowsPackages) {
26470
26469
  pkg = knownWindowsPackages[platformKey];
26471
26470
  subpath = "esbuild.exe";
@@ -26608,7 +26607,7 @@ for your current platform.`);
26608
26607
  var crypto3 = __require("crypto");
26609
26608
  var path2 = __require("path");
26610
26609
  var fs22 = __require("fs");
26611
- var os2 = __require("os");
26610
+ var os22 = __require("os");
26612
26611
  var tty = __require("tty");
26613
26612
  var worker_threads;
26614
26613
  if (process.env.ESBUILD_WORKER_THREADS !== "0") {
@@ -26919,7 +26918,7 @@ More information: The file containing the code for esbuild's JavaScript API (${_
26919
26918
  afterClose(null);
26920
26919
  };
26921
26920
  var randomFileName = () => {
26922
- return path2.join(os2.tmpdir(), `esbuild-${crypto3.randomBytes(32).toString("hex")}`);
26921
+ return path2.join(os22.tmpdir(), `esbuild-${crypto3.randomBytes(32).toString("hex")}`);
26923
26922
  };
26924
26923
  var workerThreadService = null;
26925
26924
  var startWorkerThreadService = (worker_threads2) => {
@@ -27181,7 +27180,8 @@ class DeployService {
27181
27180
  expiresIn: null,
27182
27181
  permissions: {
27183
27182
  games: [`read:${slug}`, `write:${slug}`]
27184
- }
27183
+ },
27184
+ rateLimitEnabled: false
27185
27185
  });
27186
27186
  setAttribute("app.deploy.api_key_outcome", "created");
27187
27187
  return apiKey;
@@ -27242,9 +27242,9 @@ class DeployService {
27242
27242
  throw new ValidationError("Uploaded file is empty or not found");
27243
27243
  }
27244
27244
  setAttribute("app.deploy.asset_upload_size", frontendZip.length);
27245
- const os = await import("os");
27245
+ const os2 = await import("os");
27246
27246
  const path = await import("path");
27247
- const tempDir = path.join(os.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
27247
+ const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
27248
27248
  const assetsPath = path.join(tempDir, "dist");
27249
27249
  await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
27250
27250
  uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
@@ -27857,6 +27857,31 @@ var init_game_service = __esm(() => {
27857
27857
  static changedFields(data) {
27858
27858
  return Object.entries(data).filter(([, value]) => value !== undefined).map(([key]) => key).toSorted().join(",");
27859
27859
  }
27860
+ static safeOrigin(game2) {
27861
+ if (!game2 || game2.gameType !== "external" || !game2.externalUrl) {
27862
+ return;
27863
+ }
27864
+ try {
27865
+ return new URL(game2.externalUrl).origin;
27866
+ } catch {
27867
+ return;
27868
+ }
27869
+ }
27870
+ cleanupStaleOrigin(origin, excludeGameId) {
27871
+ if (!this.deps.corsKvs) {
27872
+ return;
27873
+ }
27874
+ const corsKvs = this.deps.corsKvs;
27875
+ const db2 = this.deps.db;
27876
+ (async () => {
27877
+ const sharesOrigin = await db2.select({ id: games.id }).from(games).where(and(ne(games.id, excludeGameId), eq(games.gameType, "external"), or(eq(games.externalUrl, origin), like(games.externalUrl, `${origin}/%`)))).limit(1);
27878
+ if (sharesOrigin.length === 0) {
27879
+ await corsKvs.deleteOrigin(origin);
27880
+ } else {
27881
+ setAttribute("app.cors_kvs.delete_skipped", true);
27882
+ }
27883
+ })().catch(catchAttrs("cors_kvs.delete_origin", {}));
27884
+ }
27860
27885
  async list(caller) {
27861
27886
  const db2 = this.deps.db;
27862
27887
  const isAdmin = caller?.role === "admin";
@@ -28159,6 +28184,18 @@ var init_game_service = __esm(() => {
28159
28184
  "app.game.next_visibility": data.visibility !== undefined ? gameResponse.visibility : undefined
28160
28185
  });
28161
28186
  GameService.recordGameShape(gameResponse);
28187
+ if (this.deps.corsKvs) {
28188
+ const prevOrigin = GameService.safeOrigin(existingGame);
28189
+ const nextOrigin = GameService.safeOrigin(gameResponse);
28190
+ if (nextOrigin) {
28191
+ setAttribute("app.cors_kvs.origin", nextOrigin);
28192
+ this.deps.corsKvs.putOrigin(nextOrigin).catch(catchAttrs("cors_kvs.put_origin", {}));
28193
+ }
28194
+ if (prevOrigin && prevOrigin !== nextOrigin) {
28195
+ setAttribute("app.cors_kvs.prev_origin", prevOrigin);
28196
+ this.cleanupStaleOrigin(prevOrigin, gameResponse.id);
28197
+ }
28198
+ }
28162
28199
  return gameResponse;
28163
28200
  }
28164
28201
  async updateMetadata(gameId, data, user) {
@@ -28277,6 +28314,11 @@ var init_game_service = __esm(() => {
28277
28314
  setAttribute("app.game.api_key_cleanup_error", errorMessage(error));
28278
28315
  }
28279
28316
  }
28317
+ const corsOrigin = GameService.safeOrigin(gameToDelete);
28318
+ if (this.deps.corsKvs && corsOrigin) {
28319
+ setAttribute("app.cors_kvs.origin", corsOrigin);
28320
+ this.cleanupStaleOrigin(corsOrigin, gameId);
28321
+ }
28280
28322
  return {
28281
28323
  slug: gameToDelete.slug,
28282
28324
  displayName: gameToDelete.displayName
@@ -28408,12 +28450,13 @@ var init_logs_service = __esm(() => {
28408
28450
 
28409
28451
  // ../api-core/src/services/factory/game.ts
28410
28452
  function createGameServices(deps) {
28411
- const { db: db2, config: config2, cloudflare: cloudflare2, auth: auth2, storage, cache, alerts } = deps;
28453
+ const { db: db2, config: config2, cloudflare: cloudflare2, corsKvs, auth: auth2, storage, cache, alerts } = deps;
28412
28454
  const game2 = new GameService({
28413
28455
  db: db2,
28414
28456
  alerts,
28415
28457
  cache,
28416
28458
  cloudflare: cloudflare2,
28459
+ corsKvs,
28417
28460
  deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2)
28418
28461
  });
28419
28462
  const gameMember = new GameMemberService({
@@ -28434,7 +28477,6 @@ function createGameServices(deps) {
28434
28477
  db: db2,
28435
28478
  uploadBucket: config2.uploadBucket,
28436
28479
  storage,
28437
- cache,
28438
28480
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
28439
28481
  runDeploy: (slug, request, user, uploadDeps, extractZip) => deploy.deploy(slug, request, user, uploadDeps, extractZip),
28440
28482
  notifyDeploymentFailure: (slug, displayName, error, developerInfo) => deploy.notifyDeploymentFailure(slug, displayName, error, developerInfo)
@@ -29319,6 +29361,9 @@ class DomainService {
29319
29361
  addEvent("domain.cloudflare_hostname_created", {
29320
29362
  "app.domain.cloudflare_id": cfHostname.id
29321
29363
  });
29364
+ const corsOrigin = `https://${hostname}`;
29365
+ setAttribute("app.cors_kvs.origin", corsOrigin);
29366
+ this.deps.corsKvs?.putOrigin(corsOrigin).catch(catchAttrs("cors_kvs.put_origin", {}));
29322
29367
  return customHostname;
29323
29368
  }
29324
29369
  async list(slug, environment, user) {
@@ -29402,6 +29447,9 @@ class DomainService {
29402
29447
  "app.domain.status": dbHostname.status,
29403
29448
  "app.domain.ssl_status": dbHostname.sslStatus
29404
29449
  });
29450
+ const corsOrigin = `https://${hostname}`;
29451
+ setAttribute("app.cors_kvs.origin", corsOrigin);
29452
+ this.deps.corsKvs?.deleteOrigin(corsOrigin).catch(catchAttrs("cors_kvs.delete_origin", {}));
29405
29453
  }
29406
29454
  }
29407
29455
  var init_domain_service = __esm(() => {
@@ -29696,12 +29744,13 @@ var init_secrets_service = __esm(() => {
29696
29744
  });
29697
29745
 
29698
29746
  // ../edge-play/src/constants.ts
29699
- var ROUTES;
29747
+ var ASSET_ROUTE_PREFIX = "/api/assets/", ROUTES;
29700
29748
  var init_constants3 = __esm(() => {
29701
29749
  init_src();
29702
29750
  ROUTES = {
29703
29751
  INDEX: "/api",
29704
29752
  HEALTH: "/api/health",
29753
+ ASSETS: `${ASSET_ROUTE_PREFIX}*`,
29705
29754
  TIMEBACK: {
29706
29755
  END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
29707
29756
  GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
@@ -29858,7 +29907,8 @@ class SeedService {
29858
29907
  }, {
29859
29908
  bindings: { d1: [deploymentId], r2: [], kv: [] },
29860
29909
  keepAssets: false,
29861
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
29910
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
29911
+ observability: false
29862
29912
  });
29863
29913
  if (secrets && Object.keys(secrets).length > 0) {
29864
29914
  await cf.setSecrets(seedDeploymentId, prefixSecrets(secrets));
@@ -34168,13 +34218,14 @@ var init_emoji = __esm(() => {
34168
34218
  });
34169
34219
 
34170
34220
  // ../data/src/domains/game/schemas.ts
34171
- var GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
34221
+ var HttpUrlSchema, GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
34172
34222
  var init_schemas2 = __esm(() => {
34173
34223
  init_drizzle_zod();
34174
34224
  init_esm();
34175
34225
  init_src();
34176
34226
  init_emoji();
34177
34227
  init_table5();
34228
+ HttpUrlSchema = exports_external.string().url().refine((url2) => /^https?:\/\//i.test(url2), { message: "URL must use http or https" });
34178
34229
  GameEmojiSchema = exports_external.string().max(16).refine((value) => value.length === 0 || isSingleEmoji(value), {
34179
34230
  message: "Emoji must be a single emoji."
34180
34231
  });
@@ -34199,7 +34250,7 @@ var init_schemas2 = __esm(() => {
34199
34250
  gameType: exports_external.enum(gameTypeEnum.enumValues).default("hosted"),
34200
34251
  visibility: exports_external.enum(gameVisibilityEnum.enumValues).default("visible"),
34201
34252
  deploymentUrl: exports_external.string().nullable().optional(),
34202
- externalUrl: exports_external.string().url().nullable().optional()
34253
+ externalUrl: HttpUrlSchema.nullable().optional()
34203
34254
  }).omit({
34204
34255
  slug: true,
34205
34256
  version: true
@@ -34222,7 +34273,7 @@ var init_schemas2 = __esm(() => {
34222
34273
  gameType: exports_external.enum(gameTypeEnum.enumValues).optional(),
34223
34274
  visibility: exports_external.enum(gameVisibilityEnum.enumValues).optional(),
34224
34275
  deploymentUrl: exports_external.string().nullable().optional(),
34225
- externalUrl: exports_external.string().url().nullable().optional()
34276
+ externalUrl: HttpUrlSchema.nullable().optional()
34226
34277
  }).omit({
34227
34278
  id: true,
34228
34279
  slug: true,
@@ -34252,7 +34303,7 @@ var init_schemas2 = __esm(() => {
34252
34303
  metadata: GameMetadataRecordSchema.optional().default({}),
34253
34304
  gameType: exports_external.enum(gameTypeEnum.enumValues).optional().default("hosted"),
34254
34305
  visibility: exports_external.enum(gameVisibilityEnum.enumValues).optional(),
34255
- externalUrl: exports_external.string().url().optional()
34306
+ externalUrl: HttpUrlSchema.optional()
34256
34307
  }).refine((data) => {
34257
34308
  if (data.gameType === "external" && !data.externalUrl) {
34258
34309
  return false;
@@ -39163,6 +39214,7 @@ function createPlatformServices(deps) {
39163
39214
  db: db2,
39164
39215
  config: config2,
39165
39216
  cloudflare: cloudflare2,
39217
+ corsKvs,
39166
39218
  storage: storage2,
39167
39219
  r2Storage,
39168
39220
  timebackClient,
@@ -39184,7 +39236,7 @@ function createPlatformServices(deps) {
39184
39236
  });
39185
39237
  const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
39186
39238
  const secrets = new SecretsService({ config: config2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
39187
- const domain = new DomainService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
39239
+ const domain = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
39188
39240
  const database = new DatabaseService({
39189
39241
  db: db2,
39190
39242
  config: config2,
@@ -39862,7 +39914,7 @@ var init_standalone = __esm(() => {
39862
39914
 
39863
39915
  // ../api-core/src/services/factory/index.ts
39864
39916
  function createServices(ctx) {
39865
- const { db: db2, config: config2, providers, cloudflare: cloudflare2, timeback: timeback2, discord } = ctx;
39917
+ const { db: db2, config: config2, providers, cloudflare: cloudflare2, timeback: timeback2, discord, corsKvs } = ctx;
39866
39918
  const { auth: auth2, storage: storage2, r2Storage, cache } = providers;
39867
39919
  const infra2 = createInfraServices({
39868
39920
  db: db2,
@@ -39876,6 +39928,7 @@ function createServices(ctx) {
39876
39928
  db: db2,
39877
39929
  config: config2,
39878
39930
  cloudflare: cloudflare2,
39931
+ corsKvs,
39879
39932
  auth: auth2,
39880
39933
  storage: storage2,
39881
39934
  cache,
@@ -39885,6 +39938,7 @@ function createServices(ctx) {
39885
39938
  db: db2,
39886
39939
  config: config2,
39887
39940
  cloudflare: cloudflare2,
39941
+ corsKvs,
39888
39942
  storage: storage2,
39889
39943
  r2Storage,
39890
39944
  timebackClient: timeback2,
@@ -40073,16 +40127,6 @@ var init_auth_provider = __esm(() => {
40073
40127
  // src/infrastructure/api/providers/cache.provider.ts
40074
40128
  function createSandboxCacheProvider() {
40075
40129
  return {
40076
- async refreshGameOrigins() {
40077
- gameOrigins = ["http://localhost:3000", "http://localhost:5173"];
40078
- lastRefreshTime = Date.now();
40079
- },
40080
- getGameOriginState() {
40081
- return {
40082
- origins: gameOrigins,
40083
- lastRefreshTime
40084
- };
40085
- },
40086
40130
  async get(key) {
40087
40131
  const entry = cache.get(key);
40088
40132
  if (!entry) {
@@ -40107,13 +40151,10 @@ function createSandboxCacheProvider() {
40107
40151
  }
40108
40152
  function clearSandboxCache() {
40109
40153
  cache.clear();
40110
- gameOrigins = [];
40111
- lastRefreshTime = 0;
40112
40154
  }
40113
- var cache, gameOrigins, lastRefreshTime = 0;
40155
+ var cache;
40114
40156
  var init_cache_provider = __esm(() => {
40115
40157
  cache = new Map;
40116
- gameOrigins = [];
40117
40158
  });
40118
40159
 
40119
40160
  // src/infrastructure/api/providers/storage.provider.ts
@@ -49322,7 +49363,7 @@ var require_has_flag = __commonJS((exports, module2) => {
49322
49363
 
49323
49364
  // ../../node_modules/.bun/supports-color@7.2.0/node_modules/supports-color/index.js
49324
49365
  var require_supports_color = __commonJS((exports, module2) => {
49325
- var os = __require("os");
49366
+ var os2 = __require("os");
49326
49367
  var tty = __require("tty");
49327
49368
  var hasFlag = require_has_flag();
49328
49369
  var { env } = process;
@@ -49370,7 +49411,7 @@ var require_supports_color = __commonJS((exports, module2) => {
49370
49411
  return min;
49371
49412
  }
49372
49413
  if (process.platform === "win32") {
49373
- const osRelease = os.release().split(".");
49414
+ const osRelease = os2.release().split(".");
49374
49415
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
49375
49416
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
49376
49417
  }
@@ -54463,7 +54504,7 @@ __export(exports_api, {
54463
54504
  generateDrizzleJson: () => generateDrizzleJson
54464
54505
  });
54465
54506
  import process2 from "process";
54466
- import os from "os";
54507
+ import os2 from "os";
54467
54508
  import tty from "tty";
54468
54509
  import { randomUUID } from "crypto";
54469
54510
  function assembleStyles() {
@@ -54634,7 +54675,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
54634
54675
  return min2;
54635
54676
  }
54636
54677
  if (process2.platform === "win32") {
54637
- const osRelease = os.release().split(".");
54678
+ const osRelease = os2.release().split(".");
54638
54679
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
54639
54680
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
54640
54681
  }
@@ -67474,7 +67515,7 @@ Is ${source_default.bold.blue(this.base.name)} schema created or renamed from an
67474
67515
  });
67475
67516
  require_supports_colors = __commonJS2({
67476
67517
  "../node_modules/.pnpm/colors@1.4.0/node_modules/colors/lib/system/supports-colors.js"(exports, module2) {
67477
- var os2 = __require2("os");
67518
+ var os22 = __require2("os");
67478
67519
  var hasFlag2 = require_has_flag2();
67479
67520
  var env2 = process.env;
67480
67521
  var forceColor = undefined;
@@ -67512,7 +67553,7 @@ Is ${source_default.bold.blue(this.base.name)} schema created or renamed from an
67512
67553
  }
67513
67554
  var min2 = forceColor ? 1 : 0;
67514
67555
  if (process.platform === "win32") {
67515
- var osRelease = os2.release().split(".");
67556
+ var osRelease = os22.release().split(".");
67516
67557
  if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
67517
67558
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
67518
67559
  }
@@ -95934,28 +95975,6 @@ var init_utils11 = __esm(() => {
95934
95975
  init_validation_util();
95935
95976
  });
95936
95977
 
95937
- // ../api-core/src/controllers/admin.controller.ts
95938
- var getAllowedOrigins, admin;
95939
- var init_admin_controller = __esm(() => {
95940
- init_utils11();
95941
- getAllowedOrigins = requireAdmin(async (ctx) => {
95942
- const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
95943
- if (shouldRefresh) {
95944
- await ctx.providers.cache.refreshGameOrigins();
95945
- }
95946
- const { origins, lastRefreshTime: lastRefreshTime2 } = ctx.providers.cache.getGameOriginState();
95947
- return {
95948
- origins,
95949
- count: origins.length,
95950
- lastRefresh: lastRefreshTime2 > 0 ? new Date(lastRefreshTime2).toISOString() : null,
95951
- cacheAge: lastRefreshTime2 > 0 ? Date.now() - lastRefreshTime2 : null
95952
- };
95953
- });
95954
- admin = defineControllerNames("admin", {
95955
- getAllowedOrigins
95956
- });
95957
- });
95958
-
95959
95978
  // ../api-core/src/controllers/bucket.controller.ts
95960
95979
  var listFiles, getFile, putFile, deleteFile, initiateUpload, bucket;
95961
95980
  var init_bucket_controller = __esm(() => {
@@ -97387,7 +97406,6 @@ var init_verify_controller = __esm(() => {
97387
97406
 
97388
97407
  // ../api-core/src/controllers/index.ts
97389
97408
  var init_controllers = __esm(() => {
97390
- init_admin_controller();
97391
97409
  init_bucket_controller();
97392
97410
  init_database_controller();
97393
97411
  init_deploy_controller();
@@ -1,18 +1,3 @@
1
- /**
2
- * Sandbox Cache Provider
3
- *
4
- * In-memory cache for local development. Simple TTL-based expiration.
5
- *
6
- * @module infrastructure/api/providers/cache
7
- */
8
1
  import type { CacheProvider } from '@playcademy/api-core/providers';
9
- /**
10
- * Create a sandbox cache provider.
11
- *
12
- * Uses in-memory storage with TTL support.
13
- */
14
2
  export declare function createSandboxCacheProvider(): CacheProvider;
15
- /**
16
- * Clear all sandbox cache (useful for testing).
17
- */
18
3
  export declare function clearSandboxCache(): void;
package/dist/server.js CHANGED
@@ -978,10 +978,10 @@ var init_dist = __esm(() => {
978
978
  // ../utils/src/port.ts
979
979
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
980
980
  import { createServer } from "node:net";
981
- import { homedir } from "node:os";
981
+ import * as os from "node:os";
982
982
  import { join } from "node:path";
983
983
  function getRegistryPath() {
984
- const home = homedir();
984
+ const home = os.homedir();
985
985
  const dir = join(home, ".playcademy");
986
986
  if (!existsSync(dir)) {
987
987
  mkdirSync(dir, { recursive: true });
@@ -1077,7 +1077,7 @@ var package_default;
1077
1077
  var init_package = __esm(() => {
1078
1078
  package_default = {
1079
1079
  name: "@playcademy/sandbox",
1080
- version: "0.5.1-beta.4",
1080
+ version: "0.5.1-beta.6",
1081
1081
  description: "Local development server for Playcademy game development",
1082
1082
  type: "module",
1083
1083
  exports: {
@@ -22504,7 +22504,6 @@ class DeployJobService {
22504
22504
  "app.deploy_job.outcome": "succeeded",
22505
22505
  "app.deploy_job.run_duration_ms": DeployJobService.elapsedMsSince(new Date(runStartedAt))
22506
22506
  });
22507
- await this.deps.cache.refreshGameOrigins().catch(catchAttrs("deploy_job.cache_refresh"));
22508
22507
  await this.deleteCodeBundle(jobId);
22509
22508
  } catch (error) {
22510
22509
  clearInterval(heartbeat);
@@ -26422,7 +26421,7 @@ ${file}:${line3}:${column2}: ERROR: ${pluginText}${e.text}`;
26422
26421
  return result;
26423
26422
  }
26424
26423
  var fs2 = __require("fs");
26425
- var os = __require("os");
26424
+ var os2 = __require("os");
26426
26425
  var path = __require("path");
26427
26426
  var ESBUILD_BINARY_PATH = process.env.ESBUILD_BINARY_PATH || ESBUILD_BINARY_PATH;
26428
26427
  var isValidBinaryPath = (x) => !!x && x !== "/usr/bin/esbuild";
@@ -26464,7 +26463,7 @@ ${file}:${line3}:${column2}: ERROR: ${pluginText}${e.text}`;
26464
26463
  let pkg;
26465
26464
  let subpath;
26466
26465
  let isWASM = false;
26467
- let platformKey = `${process.platform} ${os.arch()} ${os.endianness()}`;
26466
+ let platformKey = `${process.platform} ${os2.arch()} ${os2.endianness()}`;
26468
26467
  if (platformKey in knownWindowsPackages) {
26469
26468
  pkg = knownWindowsPackages[platformKey];
26470
26469
  subpath = "esbuild.exe";
@@ -26607,7 +26606,7 @@ for your current platform.`);
26607
26606
  var crypto3 = __require("crypto");
26608
26607
  var path2 = __require("path");
26609
26608
  var fs22 = __require("fs");
26610
- var os2 = __require("os");
26609
+ var os22 = __require("os");
26611
26610
  var tty = __require("tty");
26612
26611
  var worker_threads;
26613
26612
  if (process.env.ESBUILD_WORKER_THREADS !== "0") {
@@ -26918,7 +26917,7 @@ More information: The file containing the code for esbuild's JavaScript API (${_
26918
26917
  afterClose(null);
26919
26918
  };
26920
26919
  var randomFileName = () => {
26921
- return path2.join(os2.tmpdir(), `esbuild-${crypto3.randomBytes(32).toString("hex")}`);
26920
+ return path2.join(os22.tmpdir(), `esbuild-${crypto3.randomBytes(32).toString("hex")}`);
26922
26921
  };
26923
26922
  var workerThreadService = null;
26924
26923
  var startWorkerThreadService = (worker_threads2) => {
@@ -27180,7 +27179,8 @@ class DeployService {
27180
27179
  expiresIn: null,
27181
27180
  permissions: {
27182
27181
  games: [`read:${slug}`, `write:${slug}`]
27183
- }
27182
+ },
27183
+ rateLimitEnabled: false
27184
27184
  });
27185
27185
  setAttribute("app.deploy.api_key_outcome", "created");
27186
27186
  return apiKey;
@@ -27241,9 +27241,9 @@ class DeployService {
27241
27241
  throw new ValidationError("Uploaded file is empty or not found");
27242
27242
  }
27243
27243
  setAttribute("app.deploy.asset_upload_size", frontendZip.length);
27244
- const os = await import("os");
27244
+ const os2 = await import("os");
27245
27245
  const path = await import("path");
27246
- const tempDir = path.join(os.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
27246
+ const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
27247
27247
  const assetsPath = path.join(tempDir, "dist");
27248
27248
  await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
27249
27249
  uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
@@ -27856,6 +27856,31 @@ var init_game_service = __esm(() => {
27856
27856
  static changedFields(data) {
27857
27857
  return Object.entries(data).filter(([, value]) => value !== undefined).map(([key]) => key).toSorted().join(",");
27858
27858
  }
27859
+ static safeOrigin(game2) {
27860
+ if (!game2 || game2.gameType !== "external" || !game2.externalUrl) {
27861
+ return;
27862
+ }
27863
+ try {
27864
+ return new URL(game2.externalUrl).origin;
27865
+ } catch {
27866
+ return;
27867
+ }
27868
+ }
27869
+ cleanupStaleOrigin(origin, excludeGameId) {
27870
+ if (!this.deps.corsKvs) {
27871
+ return;
27872
+ }
27873
+ const corsKvs = this.deps.corsKvs;
27874
+ const db2 = this.deps.db;
27875
+ (async () => {
27876
+ const sharesOrigin = await db2.select({ id: games.id }).from(games).where(and(ne(games.id, excludeGameId), eq(games.gameType, "external"), or(eq(games.externalUrl, origin), like(games.externalUrl, `${origin}/%`)))).limit(1);
27877
+ if (sharesOrigin.length === 0) {
27878
+ await corsKvs.deleteOrigin(origin);
27879
+ } else {
27880
+ setAttribute("app.cors_kvs.delete_skipped", true);
27881
+ }
27882
+ })().catch(catchAttrs("cors_kvs.delete_origin", {}));
27883
+ }
27859
27884
  async list(caller) {
27860
27885
  const db2 = this.deps.db;
27861
27886
  const isAdmin = caller?.role === "admin";
@@ -28158,6 +28183,18 @@ var init_game_service = __esm(() => {
28158
28183
  "app.game.next_visibility": data.visibility !== undefined ? gameResponse.visibility : undefined
28159
28184
  });
28160
28185
  GameService.recordGameShape(gameResponse);
28186
+ if (this.deps.corsKvs) {
28187
+ const prevOrigin = GameService.safeOrigin(existingGame);
28188
+ const nextOrigin = GameService.safeOrigin(gameResponse);
28189
+ if (nextOrigin) {
28190
+ setAttribute("app.cors_kvs.origin", nextOrigin);
28191
+ this.deps.corsKvs.putOrigin(nextOrigin).catch(catchAttrs("cors_kvs.put_origin", {}));
28192
+ }
28193
+ if (prevOrigin && prevOrigin !== nextOrigin) {
28194
+ setAttribute("app.cors_kvs.prev_origin", prevOrigin);
28195
+ this.cleanupStaleOrigin(prevOrigin, gameResponse.id);
28196
+ }
28197
+ }
28161
28198
  return gameResponse;
28162
28199
  }
28163
28200
  async updateMetadata(gameId, data, user) {
@@ -28276,6 +28313,11 @@ var init_game_service = __esm(() => {
28276
28313
  setAttribute("app.game.api_key_cleanup_error", errorMessage(error));
28277
28314
  }
28278
28315
  }
28316
+ const corsOrigin = GameService.safeOrigin(gameToDelete);
28317
+ if (this.deps.corsKvs && corsOrigin) {
28318
+ setAttribute("app.cors_kvs.origin", corsOrigin);
28319
+ this.cleanupStaleOrigin(corsOrigin, gameId);
28320
+ }
28279
28321
  return {
28280
28322
  slug: gameToDelete.slug,
28281
28323
  displayName: gameToDelete.displayName
@@ -28407,12 +28449,13 @@ var init_logs_service = __esm(() => {
28407
28449
 
28408
28450
  // ../api-core/src/services/factory/game.ts
28409
28451
  function createGameServices(deps) {
28410
- const { db: db2, config: config2, cloudflare: cloudflare2, auth: auth2, storage, cache, alerts } = deps;
28452
+ const { db: db2, config: config2, cloudflare: cloudflare2, corsKvs, auth: auth2, storage, cache, alerts } = deps;
28411
28453
  const game2 = new GameService({
28412
28454
  db: db2,
28413
28455
  alerts,
28414
28456
  cache,
28415
28457
  cloudflare: cloudflare2,
28458
+ corsKvs,
28416
28459
  deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2)
28417
28460
  });
28418
28461
  const gameMember = new GameMemberService({
@@ -28433,7 +28476,6 @@ function createGameServices(deps) {
28433
28476
  db: db2,
28434
28477
  uploadBucket: config2.uploadBucket,
28435
28478
  storage,
28436
- cache,
28437
28479
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
28438
28480
  runDeploy: (slug, request, user, uploadDeps, extractZip) => deploy.deploy(slug, request, user, uploadDeps, extractZip),
28439
28481
  notifyDeploymentFailure: (slug, displayName, error, developerInfo) => deploy.notifyDeploymentFailure(slug, displayName, error, developerInfo)
@@ -29318,6 +29360,9 @@ class DomainService {
29318
29360
  addEvent("domain.cloudflare_hostname_created", {
29319
29361
  "app.domain.cloudflare_id": cfHostname.id
29320
29362
  });
29363
+ const corsOrigin = `https://${hostname}`;
29364
+ setAttribute("app.cors_kvs.origin", corsOrigin);
29365
+ this.deps.corsKvs?.putOrigin(corsOrigin).catch(catchAttrs("cors_kvs.put_origin", {}));
29321
29366
  return customHostname;
29322
29367
  }
29323
29368
  async list(slug, environment, user) {
@@ -29401,6 +29446,9 @@ class DomainService {
29401
29446
  "app.domain.status": dbHostname.status,
29402
29447
  "app.domain.ssl_status": dbHostname.sslStatus
29403
29448
  });
29449
+ const corsOrigin = `https://${hostname}`;
29450
+ setAttribute("app.cors_kvs.origin", corsOrigin);
29451
+ this.deps.corsKvs?.deleteOrigin(corsOrigin).catch(catchAttrs("cors_kvs.delete_origin", {}));
29404
29452
  }
29405
29453
  }
29406
29454
  var init_domain_service = __esm(() => {
@@ -29695,12 +29743,13 @@ var init_secrets_service = __esm(() => {
29695
29743
  });
29696
29744
 
29697
29745
  // ../edge-play/src/constants.ts
29698
- var ROUTES;
29746
+ var ASSET_ROUTE_PREFIX = "/api/assets/", ROUTES;
29699
29747
  var init_constants3 = __esm(() => {
29700
29748
  init_src();
29701
29749
  ROUTES = {
29702
29750
  INDEX: "/api",
29703
29751
  HEALTH: "/api/health",
29752
+ ASSETS: `${ASSET_ROUTE_PREFIX}*`,
29704
29753
  TIMEBACK: {
29705
29754
  END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
29706
29755
  GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
@@ -29857,7 +29906,8 @@ class SeedService {
29857
29906
  }, {
29858
29907
  bindings: { d1: [deploymentId], r2: [], kv: [] },
29859
29908
  keepAssets: false,
29860
- compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE
29909
+ compatibilityDate: CLOUDFLARE_COMPATIBILITY_DATE,
29910
+ observability: false
29861
29911
  });
29862
29912
  if (secrets && Object.keys(secrets).length > 0) {
29863
29913
  await cf.setSecrets(seedDeploymentId, prefixSecrets(secrets));
@@ -34167,13 +34217,14 @@ var init_emoji = __esm(() => {
34167
34217
  });
34168
34218
 
34169
34219
  // ../data/src/domains/game/schemas.ts
34170
- var GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
34220
+ var HttpUrlSchema, GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
34171
34221
  var init_schemas2 = __esm(() => {
34172
34222
  init_drizzle_zod();
34173
34223
  init_esm();
34174
34224
  init_src();
34175
34225
  init_emoji();
34176
34226
  init_table5();
34227
+ HttpUrlSchema = exports_external.string().url().refine((url2) => /^https?:\/\//i.test(url2), { message: "URL must use http or https" });
34177
34228
  GameEmojiSchema = exports_external.string().max(16).refine((value) => value.length === 0 || isSingleEmoji(value), {
34178
34229
  message: "Emoji must be a single emoji."
34179
34230
  });
@@ -34198,7 +34249,7 @@ var init_schemas2 = __esm(() => {
34198
34249
  gameType: exports_external.enum(gameTypeEnum.enumValues).default("hosted"),
34199
34250
  visibility: exports_external.enum(gameVisibilityEnum.enumValues).default("visible"),
34200
34251
  deploymentUrl: exports_external.string().nullable().optional(),
34201
- externalUrl: exports_external.string().url().nullable().optional()
34252
+ externalUrl: HttpUrlSchema.nullable().optional()
34202
34253
  }).omit({
34203
34254
  slug: true,
34204
34255
  version: true
@@ -34221,7 +34272,7 @@ var init_schemas2 = __esm(() => {
34221
34272
  gameType: exports_external.enum(gameTypeEnum.enumValues).optional(),
34222
34273
  visibility: exports_external.enum(gameVisibilityEnum.enumValues).optional(),
34223
34274
  deploymentUrl: exports_external.string().nullable().optional(),
34224
- externalUrl: exports_external.string().url().nullable().optional()
34275
+ externalUrl: HttpUrlSchema.nullable().optional()
34225
34276
  }).omit({
34226
34277
  id: true,
34227
34278
  slug: true,
@@ -34251,7 +34302,7 @@ var init_schemas2 = __esm(() => {
34251
34302
  metadata: GameMetadataRecordSchema.optional().default({}),
34252
34303
  gameType: exports_external.enum(gameTypeEnum.enumValues).optional().default("hosted"),
34253
34304
  visibility: exports_external.enum(gameVisibilityEnum.enumValues).optional(),
34254
- externalUrl: exports_external.string().url().optional()
34305
+ externalUrl: HttpUrlSchema.optional()
34255
34306
  }).refine((data) => {
34256
34307
  if (data.gameType === "external" && !data.externalUrl) {
34257
34308
  return false;
@@ -39162,6 +39213,7 @@ function createPlatformServices(deps) {
39162
39213
  db: db2,
39163
39214
  config: config2,
39164
39215
  cloudflare: cloudflare2,
39216
+ corsKvs,
39165
39217
  storage: storage2,
39166
39218
  r2Storage,
39167
39219
  timebackClient,
@@ -39183,7 +39235,7 @@ function createPlatformServices(deps) {
39183
39235
  });
39184
39236
  const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
39185
39237
  const secrets = new SecretsService({ config: config2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
39186
- const domain = new DomainService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
39238
+ const domain = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
39187
39239
  const database = new DatabaseService({
39188
39240
  db: db2,
39189
39241
  config: config2,
@@ -39861,7 +39913,7 @@ var init_standalone = __esm(() => {
39861
39913
 
39862
39914
  // ../api-core/src/services/factory/index.ts
39863
39915
  function createServices(ctx) {
39864
- const { db: db2, config: config2, providers, cloudflare: cloudflare2, timeback: timeback2, discord } = ctx;
39916
+ const { db: db2, config: config2, providers, cloudflare: cloudflare2, timeback: timeback2, discord, corsKvs } = ctx;
39865
39917
  const { auth: auth2, storage: storage2, r2Storage, cache } = providers;
39866
39918
  const infra2 = createInfraServices({
39867
39919
  db: db2,
@@ -39875,6 +39927,7 @@ function createServices(ctx) {
39875
39927
  db: db2,
39876
39928
  config: config2,
39877
39929
  cloudflare: cloudflare2,
39930
+ corsKvs,
39878
39931
  auth: auth2,
39879
39932
  storage: storage2,
39880
39933
  cache,
@@ -39884,6 +39937,7 @@ function createServices(ctx) {
39884
39937
  db: db2,
39885
39938
  config: config2,
39886
39939
  cloudflare: cloudflare2,
39940
+ corsKvs,
39887
39941
  storage: storage2,
39888
39942
  r2Storage,
39889
39943
  timebackClient: timeback2,
@@ -40072,16 +40126,6 @@ var init_auth_provider = __esm(() => {
40072
40126
  // src/infrastructure/api/providers/cache.provider.ts
40073
40127
  function createSandboxCacheProvider() {
40074
40128
  return {
40075
- async refreshGameOrigins() {
40076
- gameOrigins = ["http://localhost:3000", "http://localhost:5173"];
40077
- lastRefreshTime = Date.now();
40078
- },
40079
- getGameOriginState() {
40080
- return {
40081
- origins: gameOrigins,
40082
- lastRefreshTime
40083
- };
40084
- },
40085
40129
  async get(key) {
40086
40130
  const entry = cache.get(key);
40087
40131
  if (!entry) {
@@ -40106,13 +40150,10 @@ function createSandboxCacheProvider() {
40106
40150
  }
40107
40151
  function clearSandboxCache() {
40108
40152
  cache.clear();
40109
- gameOrigins = [];
40110
- lastRefreshTime = 0;
40111
40153
  }
40112
- var cache, gameOrigins, lastRefreshTime = 0;
40154
+ var cache;
40113
40155
  var init_cache_provider = __esm(() => {
40114
40156
  cache = new Map;
40115
- gameOrigins = [];
40116
40157
  });
40117
40158
 
40118
40159
  // src/infrastructure/api/providers/storage.provider.ts
@@ -49321,7 +49362,7 @@ var require_has_flag = __commonJS((exports, module2) => {
49321
49362
 
49322
49363
  // ../../node_modules/.bun/supports-color@7.2.0/node_modules/supports-color/index.js
49323
49364
  var require_supports_color = __commonJS((exports, module2) => {
49324
- var os = __require("os");
49365
+ var os2 = __require("os");
49325
49366
  var tty = __require("tty");
49326
49367
  var hasFlag = require_has_flag();
49327
49368
  var { env } = process;
@@ -49369,7 +49410,7 @@ var require_supports_color = __commonJS((exports, module2) => {
49369
49410
  return min;
49370
49411
  }
49371
49412
  if (process.platform === "win32") {
49372
- const osRelease = os.release().split(".");
49413
+ const osRelease = os2.release().split(".");
49373
49414
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
49374
49415
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
49375
49416
  }
@@ -54462,7 +54503,7 @@ __export(exports_api, {
54462
54503
  generateDrizzleJson: () => generateDrizzleJson
54463
54504
  });
54464
54505
  import process2 from "process";
54465
- import os from "os";
54506
+ import os2 from "os";
54466
54507
  import tty from "tty";
54467
54508
  import { randomUUID } from "crypto";
54468
54509
  function assembleStyles() {
@@ -54633,7 +54674,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
54633
54674
  return min2;
54634
54675
  }
54635
54676
  if (process2.platform === "win32") {
54636
- const osRelease = os.release().split(".");
54677
+ const osRelease = os2.release().split(".");
54637
54678
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
54638
54679
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
54639
54680
  }
@@ -67473,7 +67514,7 @@ Is ${source_default.bold.blue(this.base.name)} schema created or renamed from an
67473
67514
  });
67474
67515
  require_supports_colors = __commonJS2({
67475
67516
  "../node_modules/.pnpm/colors@1.4.0/node_modules/colors/lib/system/supports-colors.js"(exports, module2) {
67476
- var os2 = __require2("os");
67517
+ var os22 = __require2("os");
67477
67518
  var hasFlag2 = require_has_flag2();
67478
67519
  var env2 = process.env;
67479
67520
  var forceColor = undefined;
@@ -67511,7 +67552,7 @@ Is ${source_default.bold.blue(this.base.name)} schema created or renamed from an
67511
67552
  }
67512
67553
  var min2 = forceColor ? 1 : 0;
67513
67554
  if (process.platform === "win32") {
67514
- var osRelease = os2.release().split(".");
67555
+ var osRelease = os22.release().split(".");
67515
67556
  if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
67516
67557
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
67517
67558
  }
@@ -95933,28 +95974,6 @@ var init_utils11 = __esm(() => {
95933
95974
  init_validation_util();
95934
95975
  });
95935
95976
 
95936
- // ../api-core/src/controllers/admin.controller.ts
95937
- var getAllowedOrigins, admin;
95938
- var init_admin_controller = __esm(() => {
95939
- init_utils11();
95940
- getAllowedOrigins = requireAdmin(async (ctx) => {
95941
- const shouldRefresh = ctx.url.searchParams.get("refresh") === "true";
95942
- if (shouldRefresh) {
95943
- await ctx.providers.cache.refreshGameOrigins();
95944
- }
95945
- const { origins, lastRefreshTime: lastRefreshTime2 } = ctx.providers.cache.getGameOriginState();
95946
- return {
95947
- origins,
95948
- count: origins.length,
95949
- lastRefresh: lastRefreshTime2 > 0 ? new Date(lastRefreshTime2).toISOString() : null,
95950
- cacheAge: lastRefreshTime2 > 0 ? Date.now() - lastRefreshTime2 : null
95951
- };
95952
- });
95953
- admin = defineControllerNames("admin", {
95954
- getAllowedOrigins
95955
- });
95956
- });
95957
-
95958
95977
  // ../api-core/src/controllers/bucket.controller.ts
95959
95978
  var listFiles, getFile, putFile, deleteFile, initiateUpload, bucket;
95960
95979
  var init_bucket_controller = __esm(() => {
@@ -97386,7 +97405,6 @@ var init_verify_controller = __esm(() => {
97386
97405
 
97387
97406
  // ../api-core/src/controllers/index.ts
97388
97407
  var init_controllers = __esm(() => {
97389
- init_admin_controller();
97390
97408
  init_bucket_controller();
97391
97409
  init_database_controller();
97392
97410
  init_deploy_controller();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/sandbox",
3
- "version": "0.5.1-beta.4",
3
+ "version": "0.5.1-beta.6",
4
4
  "description": "Local development server for Playcademy game development",
5
5
  "type": "module",
6
6
  "exports": {