@playcademy/vite-plugin 1.0.1-beta.5 → 1.1.0-beta.2

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.
@@ -1,30 +1,34 @@
1
1
  /**
2
2
  * Vite transformIndexHtml() hook
3
3
  *
4
- * Injects a PostHog session replay snippet into game HTML at build time.
5
- * This enables cross-origin iframe recording: the game iframe's PostHog
6
- * instance posts its recording data (DOM snapshots, console logs, network
7
- * requests) to the parent platform's PostHog instance, producing a single
8
- * unified session replay.
4
+ * Injects two independent snippets into game HTML `<head>`:
9
5
  *
10
- * The snippet loads before any game JS, so it captures the full boot
11
- * sequence including asset downloads and JS parse/execute timing even
12
- * if the game bundle never finishes loading.
6
+ * 1. **Dev only** the asset manifest (`self.__PLAYCADEMY_ASSET_MANIFEST`),
7
+ * read from the local bucket, so the SDK's `asset()` resolver works in
8
+ * `vite dev` as it does on the platform. In production the edge worker
9
+ * injects this itself, so we don't bake it into the build.
13
10
  *
14
- * Uses a named instance to avoid conflicting with any PostHog instance
15
- * a game developer may have initialized on the default `window.posthog`.
16
- *
17
- * Only injected in production builds.
11
+ * 2. **Build only** a PostHog session-replay snippet enabling cross-origin
12
+ * iframe recording: the game iframe's PostHog instance posts its recording
13
+ * data to the parent platform's instance for a single unified replay. It
14
+ * loads before any game JS (capturing the full boot sequence) and uses a
15
+ * named instance to avoid conflicting with the game's own `window.posthog`.
18
16
  */
19
17
  import type { HtmlTagDescriptor } from 'vite';
20
18
  import type { PluginContext } from '../types';
21
19
  /**
22
- * Transform index.html to inject PostHog cross-origin iframe recording.
20
+ * Transform index.html. Returns HTML tags Vite injects into `<head>`, before
21
+ * any game scripts in `<body>`.
22
+ *
23
+ * Two independent, build-only-vs-dev-only injections:
23
24
  *
24
- * Returns an array of HTML tag descriptors that Vite injects into the
25
- * built HTML. The script is placed in `<head>` so it loads before any
26
- * game scripts in `<body>`.
25
+ * - **Asset manifest (dev only).** Mirrors the SDK's `asset()` resolver against
26
+ * the local bucket so it works in `vite dev` the same as on the platform. We
27
+ * do NOT inject at build time: in production the edge worker injects the
28
+ * manifest into every HTML response itself, sourced from the deployed bucket.
29
+ * Baking a tag at build time would embed the developer's *local* bucket
30
+ * manifest into the deployed HTML — stale and competing with the worker's.
27
31
  *
28
- * Skipped when running the dev server (not a production build).
32
+ * - **PostHog cross-origin iframe recorder (build only).**
29
33
  */
30
- export declare function transformIndexHtmlHook(context: PluginContext): HtmlTagDescriptor[] | undefined;
34
+ export declare function transformIndexHtmlHook(context: PluginContext): Promise<HtmlTagDescriptor[]>;
package/dist/index.js CHANGED
@@ -23746,7 +23746,7 @@ import path from "node:path";
23746
23746
  // package.json
23747
23747
  var package_default = {
23748
23748
  name: "@playcademy/vite-plugin",
23749
- version: "1.0.1-beta.5",
23749
+ version: "1.1.0-beta.2",
23750
23750
  type: "module",
23751
23751
  exports: {
23752
23752
  ".": {
@@ -23974,6 +23974,172 @@ function configResolvedHook(resolvedConfig, context) {
23974
23974
  // src/hooks/configure-server.ts
23975
23975
  import { DEFAULT_PORTS as DEFAULT_PORTS3 } from "playcademy/constants";
23976
23976
 
23977
+ // src/lib/assets/index.ts
23978
+ var import_picocolors4 = __toESM(require_picocolors(), 1);
23979
+ import {
23980
+ ASSET_DEV_ROUTE_PREFIX,
23981
+ hasCustomAssetsRoute,
23982
+ openLocalAssetBucket,
23983
+ readLocalAsset,
23984
+ readLocalManifestScript
23985
+ } from "playcademy/bucket";
23986
+ import { loadPlaycademyConfig } from "playcademy/utils";
23987
+
23988
+ // ../utils/src/vite-logger.ts
23989
+ var import_picocolors2 = __toESM(require_picocolors(), 1);
23990
+ var { bold, cyan, dim } = import_picocolors2.default;
23991
+ function formatTimestamp() {
23992
+ const now = new Date;
23993
+ const hours = now.getHours();
23994
+ const minutes = now.getMinutes().toString().padStart(2, "0");
23995
+ const seconds = now.getSeconds().toString().padStart(2, "0");
23996
+ const ampm = hours >= 12 ? "PM" : "AM";
23997
+ const displayHours = hours % 12 || 12;
23998
+ return dim(`${displayHours}:${minutes}:${seconds} ${ampm}`);
23999
+ }
24000
+ function createLogPrefix(entity, domain) {
24001
+ const timestamp = formatTimestamp();
24002
+ const label = bold(cyan(`[${entity}]`));
24003
+ if (domain) {
24004
+ return `${timestamp} ${label} ${dim(`(${domain})`)}`;
24005
+ }
24006
+ return `${timestamp} ${label}`;
24007
+ }
24008
+
24009
+ // src/lib/logging/adapter.ts
24010
+ function createLoggerAdapter(domain) {
24011
+ return {
24012
+ info: (msg) => console.log(`${createLogPrefix("playcademy", domain)} ${msg}`),
24013
+ warn: (msg) => console.warn(`${createLogPrefix("playcademy", domain)} ${msg}`),
24014
+ error: (msg) => console.error(`${createLogPrefix("playcademy", domain)} ${msg}`)
24015
+ };
24016
+ }
24017
+ // src/lib/logging/utils.ts
24018
+ var import_picocolors3 = __toESM(require_picocolors(), 1);
24019
+
24020
+ // ../utils/src/string.ts
24021
+ function pluralize(count, singular, pluralForm) {
24022
+ return count === 1 ? singular : pluralForm || `${singular}s`;
24023
+ }
24024
+
24025
+ // src/lib/logging/utils.ts
24026
+ function createBackendBannerOptions(backendPort, vitePort) {
24027
+ if (!backendPort) {
24028
+ return;
24029
+ }
24030
+ return { port: backendPort, vitePort };
24031
+ }
24032
+ function createTimebackBannerOptions(timebackMode, courseCount, enrolledCount) {
24033
+ if (!timebackMode || !courseCount) {
24034
+ return;
24035
+ }
24036
+ return {
24037
+ courseCount,
24038
+ enrolledCount: enrolledCount ?? courseCount,
24039
+ mode: timebackMode
24040
+ };
24041
+ }
24042
+ function printBanner(viteConfig, options) {
24043
+ const INDENT = " ".repeat(2);
24044
+ const { version, gameName, sandbox, backend, timeback } = options;
24045
+ viteConfig.logger.info("");
24046
+ viteConfig.logger.info(`${INDENT}${import_picocolors3.green(import_picocolors3.bold("PLAYCADEMY"))} ${import_picocolors3.green(`v${version}`)}`);
24047
+ viteConfig.logger.info("");
24048
+ if (gameName) {
24049
+ viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Project:")} ${import_picocolors3.cyan(gameName)}`);
24050
+ }
24051
+ if (sandbox?.enabled) {
24052
+ viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Sandbox:")} ${import_picocolors3.cyan(`http://localhost:${import_picocolors3.bold(sandbox.port.toString())}/api`)}`);
24053
+ } else if (sandbox) {
24054
+ viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Sandbox:")} ${import_picocolors3.cyan("Disabled")}`);
24055
+ }
24056
+ if (backend) {
24057
+ const backendUrl = backend.vitePort ? `http://localhost:${import_picocolors3.bold(backend.vitePort.toString())}/api ${import_picocolors3.dim(`(via ${backend.port})`)}` : `http://localhost:${import_picocolors3.bold(backend.port.toString())}/api`;
24058
+ viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Backend:")} ${import_picocolors3.cyan(backendUrl)}`);
24059
+ }
24060
+ if (timeback && timeback.courseCount > 0) {
24061
+ const enrollmentInfo = timeback.enrolledCount !== timeback.courseCount ? ` / ${timeback.enrolledCount} enrolled` : "";
24062
+ const label = `${timeback.courseCount} ${pluralize(timeback.courseCount, "course")}${enrollmentInfo} ${import_picocolors3.dim(`(${timeback.mode})`)}`;
24063
+ viteConfig.logger.info(`${INDENT}${import_picocolors3.green("➜")} ${import_picocolors3.bold("Timeback:")} ${import_picocolors3.cyan(label)}`);
24064
+ }
24065
+ viteConfig.logger.info("");
24066
+ }
24067
+ // src/lib/assets/index.ts
24068
+ var logger = createLoggerAdapter("assets");
24069
+ async function loadConfigSafely(configPath) {
24070
+ try {
24071
+ return await loadPlaycademyConfig(configPath);
24072
+ } catch {
24073
+ return null;
24074
+ }
24075
+ }
24076
+ var session = null;
24077
+ var hasWarnedNoManifest = false;
24078
+ async function getBucket() {
24079
+ if (!session) {
24080
+ session = await openLocalAssetBucket();
24081
+ }
24082
+ return session.bucket;
24083
+ }
24084
+ async function disposeAssetBucket() {
24085
+ if (session) {
24086
+ await session.dispose();
24087
+ session = null;
24088
+ }
24089
+ hasWarnedNoManifest = false;
24090
+ }
24091
+ async function getDevManifestScript(configPath) {
24092
+ const config = await loadConfigSafely(configPath);
24093
+ if (!config?.integrations?.bucket) {
24094
+ return null;
24095
+ }
24096
+ const script = await readLocalManifestScript(await getBucket());
24097
+ if (!script) {
24098
+ if (!hasWarnedNoManifest) {
24099
+ hasWarnedNoManifest = true;
24100
+ logger.warn(`No local asset manifest found (run ${import_picocolors4.greenBright("playcademy bucket sync")} to serve bucket assets in local dev)`);
24101
+ }
24102
+ return null;
24103
+ }
24104
+ return script;
24105
+ }
24106
+ async function registerAssetMiddleware(server, projectRoot, configPath) {
24107
+ const config = await loadConfigSafely(configPath);
24108
+ if (!config?.integrations?.bucket) {
24109
+ return;
24110
+ }
24111
+ if (hasCustomAssetsRoute(projectRoot, config)) {
24112
+ return;
24113
+ }
24114
+ server.middlewares.use(ASSET_DEV_ROUTE_PREFIX, (req, res, next) => {
24115
+ serveAsset(req, res, next);
24116
+ });
24117
+ }
24118
+ async function serveAsset(req, res, next) {
24119
+ const raw = (req.url ?? "").split("?")[0].replace(/^\/+/, "");
24120
+ let servingKey;
24121
+ try {
24122
+ servingKey = decodeURIComponent(raw);
24123
+ } catch {
24124
+ res.statusCode = 400;
24125
+ res.end("Malformed asset path");
24126
+ return;
24127
+ }
24128
+ if (!servingKey) {
24129
+ next();
24130
+ return;
24131
+ }
24132
+ const asset = await readLocalAsset(await getBucket(), servingKey);
24133
+ if (!asset) {
24134
+ next();
24135
+ return;
24136
+ }
24137
+ res.statusCode = 200;
24138
+ res.setHeader("Content-Type", asset.contentType);
24139
+ res.setHeader("Cache-Control", "no-store");
24140
+ res.end(Buffer.from(asset.body));
24141
+ }
24142
+
23977
24143
  // src/server/state.ts
23978
24144
  var serverState = {
23979
24145
  sandbox: null,
@@ -24016,6 +24182,9 @@ function setPlatformRoleOverride(role) {
24016
24182
 
24017
24183
  // src/server/cleanup.ts
24018
24184
  async function cleanupServers() {
24185
+ try {
24186
+ await disposeAssetBucket();
24187
+ } catch {}
24019
24188
  if (serverState.backend) {
24020
24189
  try {
24021
24190
  await serverState.backend.server.dispose();
@@ -24053,31 +24222,10 @@ function getNextMode(mode) {
24053
24222
  }
24054
24223
 
24055
24224
  // src/server/recreate-sandbox.ts
24056
- var import_picocolors7 = __toESM(require_picocolors(), 1);
24057
-
24058
- // ../utils/src/vite-logger.ts
24059
- var import_picocolors2 = __toESM(require_picocolors(), 1);
24060
- var { bold, cyan, dim } = import_picocolors2.default;
24061
- function formatTimestamp() {
24062
- const now = new Date;
24063
- const hours = now.getHours();
24064
- const minutes = now.getMinutes().toString().padStart(2, "0");
24065
- const seconds = now.getSeconds().toString().padStart(2, "0");
24066
- const ampm = hours >= 12 ? "PM" : "AM";
24067
- const displayHours = hours % 12 || 12;
24068
- return dim(`${displayHours}:${minutes}:${seconds} ${ampm}`);
24069
- }
24070
- function createLogPrefix(entity, domain) {
24071
- const timestamp = formatTimestamp();
24072
- const label = bold(cyan(`[${entity}]`));
24073
- if (domain) {
24074
- return `${timestamp} ${label} ${dim(`(${domain})`)}`;
24075
- }
24076
- return `${timestamp} ${label}`;
24077
- }
24225
+ var import_picocolors8 = __toESM(require_picocolors(), 1);
24078
24226
 
24079
24227
  // src/lib/sandbox/server.ts
24080
- var import_picocolors6 = __toESM(require_picocolors(), 1);
24228
+ var import_picocolors7 = __toESM(require_picocolors(), 1);
24081
24229
  import { DEFAULT_PORTS as DEFAULT_PORTS2 } from "playcademy/constants";
24082
24230
 
24083
24231
  // ../sandbox/dist/server.js
@@ -24089,7 +24237,7 @@ import { Readable } from "stream";
24089
24237
  import crypto2 from "crypto";
24090
24238
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
24091
24239
  import { createServer } from "node:net";
24092
- import { homedir as homedir2 } from "node:os";
24240
+ import * as os from "node:os";
24093
24241
  import { join } from "node:path";
24094
24242
  import { mkdir, writeFile } from "fs/promises";
24095
24243
  import { join as join2 } from "path";
@@ -24103,7 +24251,7 @@ import * as o3 from "path";
24103
24251
  import fs22 from "node:fs";
24104
24252
  import { dirname, isAbsolute, join as join4 } from "node:path";
24105
24253
  import process2 from "process";
24106
- import os from "os";
24254
+ import os2 from "os";
24107
24255
  import tty from "tty";
24108
24256
  import { randomUUID } from "crypto";
24109
24257
  import fs3 from "node:fs";
@@ -25100,7 +25248,7 @@ var init_dist = __esm(() => {
25100
25248
  outgoingEnded = Symbol("outgoingEnded");
25101
25249
  });
25102
25250
  function getRegistryPath() {
25103
- const home = homedir2();
25251
+ const home = os.homedir();
25104
25252
  const dir = join(home, ".playcademy");
25105
25253
  if (!existsSync(dir)) {
25106
25254
  mkdirSync(dir, { recursive: true });
@@ -25194,7 +25342,7 @@ var package_default2;
25194
25342
  var init_package = __esm(() => {
25195
25343
  package_default2 = {
25196
25344
  name: "@playcademy/sandbox",
25197
- version: "0.5.0",
25345
+ version: "0.5.1-beta.6",
25198
25346
  description: "Local development server for Playcademy game development",
25199
25347
  type: "module",
25200
25348
  exports: {
@@ -25646,7 +25794,7 @@ function createLogger(scopeName) {
25646
25794
  var colors;
25647
25795
  var levelPriority;
25648
25796
  var customHandler;
25649
- var log;
25797
+ var log2;
25650
25798
  var init_src2 = __esm(() => {
25651
25799
  colors = {
25652
25800
  reset: "\x1B[0m",
@@ -25664,7 +25812,7 @@ var init_src2 = __esm(() => {
25664
25812
  warn: 2,
25665
25813
  error: 3
25666
25814
  };
25667
- log = createLogger();
25815
+ log2 = createLogger();
25668
25816
  });
25669
25817
  function formatSSE(eventType, data) {
25670
25818
  const payload = `event: ${eventType}
@@ -25701,7 +25849,7 @@ function toApiError(err2) {
25701
25849
  return ApiError.fromDomain(err2);
25702
25850
  }
25703
25851
  const message = err2 instanceof Error ? err2.message : "Unknown error";
25704
- log.error("[api-hono] Unexpected error", { error: err2 });
25852
+ log2.error("[api-hono] Unexpected error", { error: err2 });
25705
25853
  return ApiError.internal(message);
25706
25854
  }
25707
25855
  function errorResponse(c, err2) {
@@ -25742,7 +25890,7 @@ function createStreamHandle(buildContext) {
25742
25890
  } catch (error) {
25743
25891
  const apiError = toApiError(error);
25744
25892
  const logLevel = apiError.status >= 500 ? "error" : "warn";
25745
- log[logLevel]("[api-hono] Stream error", {
25893
+ log2[logLevel]("[api-hono] Stream error", {
25746
25894
  status: apiError.status,
25747
25895
  code: apiError.code,
25748
25896
  message: apiError.message,
@@ -33225,9 +33373,9 @@ var init_delete = __esm(() => {
33225
33373
  init_tracing();
33226
33374
  init_utils();
33227
33375
  PgDeleteBase = class PgDeleteBase2 extends QueryPromise {
33228
- constructor(table2, session, dialect, withList) {
33376
+ constructor(table2, session2, dialect, withList) {
33229
33377
  super();
33230
- this.session = session;
33378
+ this.session = session2;
33231
33379
  this.dialect = dialect;
33232
33380
  this.config = { table: table2, withList };
33233
33381
  }
@@ -33365,7 +33513,7 @@ var init_dialect = __esm(() => {
33365
33513
  constructor(config2) {
33366
33514
  this.casing = new CasingCache(config2?.casing);
33367
33515
  }
33368
- async migrate(migrations, session, config2) {
33516
+ async migrate(migrations, session2, config2) {
33369
33517
  const migrationsTable = typeof config2 === "string" ? "__drizzle_migrations" : config2.migrationsTable ?? "__drizzle_migrations";
33370
33518
  const migrationsSchema = typeof config2 === "string" ? "drizzle" : config2.migrationsSchema ?? "drizzle";
33371
33519
  const migrationTableCreate = sql`
@@ -33375,11 +33523,11 @@ var init_dialect = __esm(() => {
33375
33523
  created_at bigint
33376
33524
  )
33377
33525
  `;
33378
- await session.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.identifier(migrationsSchema)}`);
33379
- await session.execute(migrationTableCreate);
33380
- const dbMigrations = await session.all(sql`select id, hash, created_at from ${sql.identifier(migrationsSchema)}.${sql.identifier(migrationsTable)} order by created_at desc limit 1`);
33526
+ await session2.execute(sql`CREATE SCHEMA IF NOT EXISTS ${sql.identifier(migrationsSchema)}`);
33527
+ await session2.execute(migrationTableCreate);
33528
+ const dbMigrations = await session2.all(sql`select id, hash, created_at from ${sql.identifier(migrationsSchema)}.${sql.identifier(migrationsTable)} order by created_at desc limit 1`);
33381
33529
  const lastDbMigration = dbMigrations[0];
33382
- await session.transaction(async (tx) => {
33530
+ await session2.transaction(async (tx) => {
33383
33531
  for await (const migration of migrations) {
33384
33532
  if (!lastDbMigration || Number(lastDbMigration.created_at) < migration.folderMillis) {
33385
33533
  for (const stmt of migration.sql) {
@@ -34026,7 +34174,7 @@ var init_select2 = __esm(() => {
34026
34174
  isPartialSelect;
34027
34175
  session;
34028
34176
  dialect;
34029
- constructor({ table: table2, fields, isPartialSelect, session, dialect, withList, distinct }) {
34177
+ constructor({ table: table2, fields, isPartialSelect, session: session2, dialect, withList, distinct }) {
34030
34178
  super();
34031
34179
  this.config = {
34032
34180
  withList,
@@ -34036,7 +34184,7 @@ var init_select2 = __esm(() => {
34036
34184
  setOperators: []
34037
34185
  };
34038
34186
  this.isPartialSelect = isPartialSelect;
34039
- this.session = session;
34187
+ this.session = session2;
34040
34188
  this.dialect = dialect;
34041
34189
  this._ = {
34042
34190
  selectedFields: fields
@@ -34200,13 +34348,13 @@ var init_select2 = __esm(() => {
34200
34348
  PgSelectBase = class PgSelectBase2 extends PgSelectQueryBuilderBase {
34201
34349
  static [entityKind] = "PgSelect";
34202
34350
  _prepare(name2) {
34203
- const { session, config: config2, dialect, joinsNotNullableMap, authToken } = this;
34204
- if (!session) {
34351
+ const { session: session2, config: config2, dialect, joinsNotNullableMap, authToken } = this;
34352
+ if (!session2) {
34205
34353
  throw new Error("Cannot execute a query on a query builder. Please use a database instance instead.");
34206
34354
  }
34207
34355
  return tracer.startActiveSpan("drizzle.prepareQuery", () => {
34208
34356
  const fieldsList = orderSelectedFields(config2.fields);
34209
- const query = session.prepareQuery(dialect.sqlToQuery(this.getSQL()), fieldsList, name2, true);
34357
+ const query = session2.prepareQuery(dialect.sqlToQuery(this.getSQL()), fieldsList, name2, true);
34210
34358
  query.joinsNotNullableMap = joinsNotNullableMap;
34211
34359
  return query.setToken(authToken);
34212
34360
  });
@@ -34329,9 +34477,9 @@ var init_insert = __esm(() => {
34329
34477
  init_utils();
34330
34478
  init_query_builder2();
34331
34479
  PgInsertBuilder = class PgInsertBuilder2 {
34332
- constructor(table2, session, dialect, withList, overridingSystemValue_) {
34480
+ constructor(table2, session2, dialect, withList, overridingSystemValue_) {
34333
34481
  this.table = table2;
34334
- this.session = session;
34482
+ this.session = session2;
34335
34483
  this.dialect = dialect;
34336
34484
  this.withList = withList;
34337
34485
  this.overridingSystemValue_ = overridingSystemValue_;
@@ -34371,9 +34519,9 @@ var init_insert = __esm(() => {
34371
34519
  }
34372
34520
  };
34373
34521
  PgInsertBase = class PgInsertBase2 extends QueryPromise {
34374
- constructor(table2, values, session, dialect, withList, select2, overridingSystemValue_) {
34522
+ constructor(table2, values, session2, dialect, withList, select2, overridingSystemValue_) {
34375
34523
  super();
34376
- this.session = session;
34524
+ this.session = session2;
34377
34525
  this.dialect = dialect;
34378
34526
  this.config = { table: table2, values, withList, select: select2, overridingSystemValue_ };
34379
34527
  }
@@ -34451,9 +34599,9 @@ var init_refresh_materialized_view = __esm(() => {
34451
34599
  init_query_promise();
34452
34600
  init_tracing();
34453
34601
  PgRefreshMaterializedView = class PgRefreshMaterializedView2 extends QueryPromise {
34454
- constructor(view, session, dialect) {
34602
+ constructor(view, session2, dialect) {
34455
34603
  super();
34456
- this.session = session;
34604
+ this.session = session2;
34457
34605
  this.dialect = dialect;
34458
34606
  this.config = { view };
34459
34607
  }
@@ -34513,9 +34661,9 @@ var init_update = __esm(() => {
34513
34661
  init_utils();
34514
34662
  init_view_common();
34515
34663
  PgUpdateBuilder = class PgUpdateBuilder2 {
34516
- constructor(table2, session, dialect, withList) {
34664
+ constructor(table2, session2, dialect, withList) {
34517
34665
  this.table = table2;
34518
- this.session = session;
34666
+ this.session = session2;
34519
34667
  this.dialect = dialect;
34520
34668
  this.withList = withList;
34521
34669
  }
@@ -34530,9 +34678,9 @@ var init_update = __esm(() => {
34530
34678
  }
34531
34679
  };
34532
34680
  PgUpdateBase = class PgUpdateBase2 extends QueryPromise {
34533
- constructor(table2, set, session, dialect, withList) {
34681
+ constructor(table2, set, session2, dialect, withList) {
34534
34682
  super();
34535
- this.session = session;
34683
+ this.session = session2;
34536
34684
  this.dialect = dialect;
34537
34685
  this.config = { set, table: table2, withList, joins: [] };
34538
34686
  this.tableName = getTableLikeName(table2);
@@ -34720,14 +34868,14 @@ var init_query = __esm(() => {
34720
34868
  init_relations();
34721
34869
  init_tracing();
34722
34870
  RelationalQueryBuilder = class RelationalQueryBuilder2 {
34723
- constructor(fullSchema, schema, tableNamesMap, table2, tableConfig, dialect, session) {
34871
+ constructor(fullSchema, schema, tableNamesMap, table2, tableConfig, dialect, session2) {
34724
34872
  this.fullSchema = fullSchema;
34725
34873
  this.schema = schema;
34726
34874
  this.tableNamesMap = tableNamesMap;
34727
34875
  this.table = table2;
34728
34876
  this.tableConfig = tableConfig;
34729
34877
  this.dialect = dialect;
34730
- this.session = session;
34878
+ this.session = session2;
34731
34879
  }
34732
34880
  static [entityKind] = "PgRelationalQueryBuilder";
34733
34881
  findMany(config2) {
@@ -34738,7 +34886,7 @@ var init_query = __esm(() => {
34738
34886
  }
34739
34887
  };
34740
34888
  PgRelationalQuery = class PgRelationalQuery2 extends QueryPromise {
34741
- constructor(fullSchema, schema, tableNamesMap, table2, tableConfig, dialect, session, config2, mode) {
34889
+ constructor(fullSchema, schema, tableNamesMap, table2, tableConfig, dialect, session2, config2, mode) {
34742
34890
  super();
34743
34891
  this.fullSchema = fullSchema;
34744
34892
  this.schema = schema;
@@ -34746,7 +34894,7 @@ var init_query = __esm(() => {
34746
34894
  this.table = table2;
34747
34895
  this.tableConfig = tableConfig;
34748
34896
  this.dialect = dialect;
34749
- this.session = session;
34897
+ this.session = session2;
34750
34898
  this.config = config2;
34751
34899
  this.mode = mode;
34752
34900
  }
@@ -34842,24 +34990,24 @@ var init_db = __esm(() => {
34842
34990
  init_raw();
34843
34991
  init_refresh_materialized_view();
34844
34992
  PgDatabase = class PgDatabase2 {
34845
- constructor(dialect, session, schema) {
34993
+ constructor(dialect, session2, schema) {
34846
34994
  this.dialect = dialect;
34847
- this.session = session;
34995
+ this.session = session2;
34848
34996
  this._ = schema ? {
34849
34997
  schema: schema.schema,
34850
34998
  fullSchema: schema.fullSchema,
34851
34999
  tableNamesMap: schema.tableNamesMap,
34852
- session
35000
+ session: session2
34853
35001
  } : {
34854
35002
  schema: undefined,
34855
35003
  fullSchema: {},
34856
35004
  tableNamesMap: {},
34857
- session
35005
+ session: session2
34858
35006
  };
34859
35007
  this.query = {};
34860
35008
  if (this._.schema) {
34861
35009
  for (const [tableName, columns] of Object.entries(this._.schema)) {
34862
- this.query[tableName] = new RelationalQueryBuilder(schema.fullSchema, this._.schema, this._.tableNamesMap, schema.fullSchema[tableName], columns, dialect, session);
35010
+ this.query[tableName] = new RelationalQueryBuilder(schema.fullSchema, this._.schema, this._.tableNamesMap, schema.fullSchema[tableName], columns, dialect, session2);
34863
35011
  }
34864
35012
  }
34865
35013
  }
@@ -35114,8 +35262,8 @@ var init_session = __esm(() => {
35114
35262
  }
35115
35263
  };
35116
35264
  PgTransaction = class PgTransaction2 extends PgDatabase {
35117
- constructor(dialect, session, schema, nestedIndex = 0) {
35118
- super(dialect, session, schema);
35265
+ constructor(dialect, session2, schema, nestedIndex = 0) {
35266
+ super(dialect, session2, schema);
35119
35267
  this.schema = schema;
35120
35268
  this.nestedIndex = nestedIndex;
35121
35269
  }
@@ -45982,13 +46130,13 @@ async function extractZipToDirectory(zipBuffer, targetDir) {
45982
46130
  }
45983
46131
  });
45984
46132
  await Promise.all(extractPromises);
45985
- log.debug("[utils/zip] Extracted ZIP to directory", {
46133
+ log2.debug("[utils/zip] Extracted ZIP to directory", {
45986
46134
  targetDir,
45987
46135
  fileCount: Object.keys(zip.files).length
45988
46136
  });
45989
46137
  } catch (error) {
45990
46138
  const errorMessage2 = error instanceof Error ? error.message : String(error);
45991
- log.error("[utils/zip] Failed to extract ZIP", { targetDir, error });
46139
+ log2.error("[utils/zip] Failed to extract ZIP", { targetDir, error });
45992
46140
  throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage2}`, { cause: error });
45993
46141
  }
45994
46142
  }
@@ -50275,7 +50423,7 @@ ${file}:${line3}:${column2}: ERROR: ${pluginText}${e.text}`;
50275
50423
  return result;
50276
50424
  }
50277
50425
  var fs23 = __require2("fs");
50278
- var os2 = __require2("os");
50426
+ var os22 = __require2("os");
50279
50427
  var path2 = __require2("path");
50280
50428
  var ESBUILD_BINARY_PATH = process.env.ESBUILD_BINARY_PATH || ESBUILD_BINARY_PATH;
50281
50429
  var isValidBinaryPath = (x) => !!x && x !== "/usr/bin/esbuild";
@@ -50317,7 +50465,7 @@ ${file}:${line3}:${column2}: ERROR: ${pluginText}${e.text}`;
50317
50465
  let pkg;
50318
50466
  let subpath;
50319
50467
  let isWASM = false;
50320
- let platformKey = `${process.platform} ${os2.arch()} ${os2.endianness()}`;
50468
+ let platformKey = `${process.platform} ${os22.arch()} ${os22.endianness()}`;
50321
50469
  if (platformKey in knownWindowsPackages) {
50322
50470
  pkg = knownWindowsPackages[platformKey];
50323
50471
  subpath = "esbuild.exe";
@@ -50462,7 +50610,7 @@ for your current platform.`);
50462
50610
  var crypto32 = __require2("crypto");
50463
50611
  var path22 = __require2("path");
50464
50612
  var fs222 = __require2("fs");
50465
- var os22 = __require2("os");
50613
+ var os222 = __require2("os");
50466
50614
  var tty2 = __require2("tty");
50467
50615
  var worker_threads;
50468
50616
  if (process.env.ESBUILD_WORKER_THREADS !== "0") {
@@ -50773,7 +50921,7 @@ More information: The file containing the code for esbuild's JavaScript API (${_
50773
50921
  afterClose(null);
50774
50922
  };
50775
50923
  var randomFileName = () => {
50776
- return path22.join(os22.tmpdir(), `esbuild-${crypto32.randomBytes(32).toString("hex")}`);
50924
+ return path22.join(os222.tmpdir(), `esbuild-${crypto32.randomBytes(32).toString("hex")}`);
50777
50925
  };
50778
50926
  var workerThreadService = null;
50779
50927
  var startWorkerThreadService = (worker_threads2) => {
@@ -51014,7 +51162,8 @@ class DeployService {
51014
51162
  expiresIn: null,
51015
51163
  permissions: {
51016
51164
  games: [`read:${slug}`, `write:${slug}`]
51017
- }
51165
+ },
51166
+ rateLimitEnabled: false
51018
51167
  });
51019
51168
  setAttribute("app.deploy.api_key_outcome", "created");
51020
51169
  return apiKey;
@@ -51075,9 +51224,9 @@ class DeployService {
51075
51224
  throw new ValidationError("Uploaded file is empty or not found");
51076
51225
  }
51077
51226
  setAttribute("app.deploy.asset_upload_size", frontendZip.length);
51078
- const os2 = await import("os");
51227
+ const os22 = await import("os");
51079
51228
  const path2 = await import("path");
51080
- const tempDir = path2.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
51229
+ const tempDir = path2.join(os22.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
51081
51230
  const assetsPath = path2.join(tempDir, "dist");
51082
51231
  await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
51083
51232
  uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
@@ -53551,12 +53700,14 @@ var init_secrets_service = __esm(() => {
53551
53700
  init_deployment_util();
53552
53701
  INTERNAL_SECRET_KEYS = ["PLAYCADEMY_API_KEY", "GAME_ID", "PLAYCADEMY_BASE_URL"];
53553
53702
  });
53703
+ var ASSET_ROUTE_PREFIX = "/api/assets/";
53554
53704
  var ROUTES;
53555
53705
  var init_constants3 = __esm(() => {
53556
53706
  init_src();
53557
53707
  ROUTES = {
53558
53708
  INDEX: "/api",
53559
53709
  HEALTH: "/api/health",
53710
+ ASSETS: `${ASSET_ROUTE_PREFIX}*`,
53560
53711
  TIMEBACK: {
53561
53712
  END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
53562
53713
  GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
@@ -63923,14 +64074,14 @@ function buildTimebackClient() {
63923
64074
  }
63924
64075
  const { mode, onerosterApiUrl, caliperApiUrl, clientId, clientSecret, authUrl } = config.timeback;
63925
64076
  if (mode === "local") {
63926
- log.debug("[Sandbox] Initializing TimeBack client (local mode)", { onerosterApiUrl });
64077
+ log2.debug("[Sandbox] Initializing TimeBack client (local mode)", { onerosterApiUrl });
63927
64078
  return new TimebackClient({
63928
64079
  baseUrl: onerosterApiUrl,
63929
64080
  caliperUrl: caliperApiUrl
63930
64081
  });
63931
64082
  }
63932
64083
  if (mode === "remote" && clientId && clientSecret && authUrl) {
63933
- log.debug("[Sandbox] Initializing TimeBack client (remote mode)", { onerosterApiUrl });
64084
+ log2.debug("[Sandbox] Initializing TimeBack client (remote mode)", { onerosterApiUrl });
63934
64085
  return new TimebackClient({
63935
64086
  baseUrl: onerosterApiUrl,
63936
64087
  caliperUrl: caliperApiUrl,
@@ -63965,7 +64116,7 @@ function createSandboxAuthProvider() {
63965
64116
  expiresAt: params.expiresIn ? new Date(Date.now() + params.expiresIn * 1000) : null
63966
64117
  };
63967
64118
  sandboxApiKeys.set(id, apiKey);
63968
- log.debug("[SandboxAuthProvider] Created API key", { id, name: params.name });
64119
+ log2.debug("[SandboxAuthProvider] Created API key", { id, name: params.name });
63969
64120
  return { id, key };
63970
64121
  },
63971
64122
  async listApiKeys(_headers) {
@@ -63973,13 +64124,13 @@ function createSandboxAuthProvider() {
63973
64124
  },
63974
64125
  async deleteApiKey(keyId) {
63975
64126
  sandboxApiKeys.delete(keyId);
63976
- log.debug("[SandboxAuthProvider] Deleted API key", { keyId });
64127
+ log2.debug("[SandboxAuthProvider] Deleted API key", { keyId });
63977
64128
  },
63978
64129
  async deleteApiKeyByName(name3, userId) {
63979
64130
  for (const [id, key] of sandboxApiKeys.entries()) {
63980
64131
  if (key.name === name3 && key.userId === userId) {
63981
64132
  sandboxApiKeys.delete(id);
63982
- log.debug("[SandboxAuthProvider] Deleted API key by name", { id, name: name3 });
64133
+ log2.debug("[SandboxAuthProvider] Deleted API key by name", { id, name: name3 });
63983
64134
  return id;
63984
64135
  }
63985
64136
  }
@@ -64002,7 +64153,7 @@ function createSandboxAuthProvider() {
64002
64153
  const payload2 = JSON.parse(atob(parts2[1]));
64003
64154
  if (payload2.sub && payload2.uid) {
64004
64155
  if (payload2.exp && payload2.exp < Date.now()) {
64005
- log.debug("[SandboxAuthProvider] Token expired");
64156
+ log2.debug("[SandboxAuthProvider] Token expired");
64006
64157
  return null;
64007
64158
  }
64008
64159
  return payload2;
@@ -64053,7 +64204,7 @@ function createSandboxAuthProvider() {
64053
64204
  const payload = JSON.parse(atob(parts2[1]));
64054
64205
  if (payload.jti && payload.sub && payload.game) {
64055
64206
  if (payload.exp && payload.exp < Date.now()) {
64056
- log.debug("[SandboxAuthProvider] Log stream token expired");
64207
+ log2.debug("[SandboxAuthProvider] Log stream token expired");
64057
64208
  return null;
64058
64209
  }
64059
64210
  return { jti: payload.jti, sub: payload.sub, game: payload.game };
@@ -64103,7 +64254,7 @@ var cache;
64103
64254
  var init_cache_provider = __esm(() => {
64104
64255
  cache = new Map;
64105
64256
  });
64106
- function getBucket(bucketName) {
64257
+ function getBucket2(bucketName) {
64107
64258
  let bucket = storage2.get(bucketName);
64108
64259
  if (!bucket) {
64109
64260
  bucket = new Map;
@@ -64114,7 +64265,7 @@ function getBucket(bucketName) {
64114
64265
  function createSandboxStorageProvider() {
64115
64266
  return {
64116
64267
  async listObjects(bucketName, prefix) {
64117
- const bucket = getBucket(bucketName);
64268
+ const bucket = getBucket2(bucketName);
64118
64269
  const files = [];
64119
64270
  for (const [key, data] of bucket.entries()) {
64120
64271
  if (!prefix || key.startsWith(prefix)) {
@@ -64129,7 +64280,7 @@ function createSandboxStorageProvider() {
64129
64280
  return files;
64130
64281
  },
64131
64282
  async getObject(bucketName, key) {
64132
- const bucket = getBucket(bucketName);
64283
+ const bucket = getBucket2(bucketName);
64133
64284
  const data = bucket.get(key);
64134
64285
  if (!data) {
64135
64286
  return null;
@@ -64141,18 +64292,18 @@ function createSandboxStorageProvider() {
64141
64292
  };
64142
64293
  },
64143
64294
  async putObject(bucketName, key, body2, options) {
64144
- const bucket = getBucket(bucketName);
64295
+ const bucket = getBucket2(bucketName);
64145
64296
  bucket.set(key, { body: body2, contentType: options?.contentType });
64146
- log.debug("[SandboxStorageProvider] Stored object", {
64297
+ log2.debug("[SandboxStorageProvider] Stored object", {
64147
64298
  bucket: bucketName,
64148
64299
  key,
64149
64300
  size: body2.length
64150
64301
  });
64151
64302
  },
64152
64303
  async deleteObject(bucketName, key) {
64153
- const bucket = getBucket(bucketName);
64304
+ const bucket = getBucket2(bucketName);
64154
64305
  bucket.delete(key);
64155
- log.debug("[SandboxStorageProvider] Deleted object", { bucket: bucketName, key });
64306
+ log2.debug("[SandboxStorageProvider] Deleted object", { bucket: bucketName, key });
64156
64307
  },
64157
64308
  async generatePresignedPutUrl(bucketName, key, _contentType, _expiresIn) {
64158
64309
  const baseUrl = "http://localhost:3000";
@@ -64218,7 +64369,7 @@ function createSandboxContext(options) {
64218
64369
  };
64219
64370
  Object.assign(services, createServices(ctx));
64220
64371
  cachedServiceContext = ctx;
64221
- log.debug("[Sandbox] ServiceContext initialized", {
64372
+ log2.debug("[Sandbox] ServiceContext initialized", {
64222
64373
  sstStage: config2.sstStage,
64223
64374
  baseUrl: config2.baseUrl,
64224
64375
  hasTimeback: Boolean(timeback2)
@@ -73538,7 +73689,7 @@ var require_has_flag = __commonJS2((exports, module2) => {
73538
73689
  };
73539
73690
  });
73540
73691
  var require_supports_color = __commonJS2((exports, module2) => {
73541
- var os2 = __require2("os");
73692
+ var os22 = __require2("os");
73542
73693
  var tty2 = __require2("tty");
73543
73694
  var hasFlag = require_has_flag();
73544
73695
  var { env } = process;
@@ -73586,7 +73737,7 @@ var require_supports_color = __commonJS2((exports, module2) => {
73586
73737
  return min;
73587
73738
  }
73588
73739
  if (process.platform === "win32") {
73589
- const osRelease = os2.release().split(".");
73740
+ const osRelease = os22.release().split(".");
73590
73741
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
73591
73742
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
73592
73743
  }
@@ -78700,8 +78851,8 @@ function assembleStyles() {
78700
78851
  styles2.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET);
78701
78852
  Object.defineProperties(styles2, {
78702
78853
  rgbToAnsi256: {
78703
- value(red, green, blue) {
78704
- if (red === green && green === blue) {
78854
+ value(red, green2, blue2) {
78855
+ if (red === green2 && green2 === blue2) {
78705
78856
  if (red < 8) {
78706
78857
  return 16;
78707
78858
  }
@@ -78710,7 +78861,7 @@ function assembleStyles() {
78710
78861
  }
78711
78862
  return Math.round((red - 8) / 247 * 24) + 232;
78712
78863
  }
78713
- return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5);
78864
+ return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green2 / 255 * 5) + Math.round(blue2 / 255 * 5);
78714
78865
  },
78715
78866
  enumerable: false
78716
78867
  },
@@ -78746,24 +78897,24 @@ function assembleStyles() {
78746
78897
  return 90 + (code - 8);
78747
78898
  }
78748
78899
  let red;
78749
- let green;
78750
- let blue;
78900
+ let green2;
78901
+ let blue2;
78751
78902
  if (code >= 232) {
78752
78903
  red = ((code - 232) * 10 + 8) / 255;
78753
- green = red;
78754
- blue = red;
78904
+ green2 = red;
78905
+ blue2 = red;
78755
78906
  } else {
78756
78907
  code -= 16;
78757
78908
  const remainder = code % 36;
78758
78909
  red = Math.floor(code / 36) / 5;
78759
- green = Math.floor(remainder / 6) / 5;
78760
- blue = remainder % 6 / 5;
78910
+ green2 = Math.floor(remainder / 6) / 5;
78911
+ blue2 = remainder % 6 / 5;
78761
78912
  }
78762
- const value = Math.max(red, green, blue) * 2;
78913
+ const value = Math.max(red, green2, blue2) * 2;
78763
78914
  if (value === 0) {
78764
78915
  return 30;
78765
78916
  }
78766
- let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red));
78917
+ let result = 30 + (Math.round(blue2) << 2 | Math.round(green2) << 1 | Math.round(red));
78767
78918
  if (value === 2) {
78768
78919
  result += 60;
78769
78920
  }
@@ -78772,7 +78923,7 @@ function assembleStyles() {
78772
78923
  enumerable: false
78773
78924
  },
78774
78925
  rgbToAnsi: {
78775
- value: (red, green, blue) => styles2.ansi256ToAnsi(styles2.rgbToAnsi256(red, green, blue)),
78926
+ value: (red, green2, blue2) => styles2.ansi256ToAnsi(styles2.rgbToAnsi256(red, green2, blue2)),
78776
78927
  enumerable: false
78777
78928
  },
78778
78929
  hexToAnsi: {
@@ -78838,7 +78989,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
78838
78989
  return min2;
78839
78990
  }
78840
78991
  if (process2.platform === "win32") {
78841
- const osRelease = os.release().split(".");
78992
+ const osRelease = os2.release().split(".");
78842
78993
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
78843
78994
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
78844
78995
  }
@@ -84239,7 +84390,7 @@ var init_api2 = __esm(() => {
84239
84390
  ANSI_BACKGROUND_OFFSET = 10;
84240
84391
  wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`;
84241
84392
  wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`;
84242
- wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`;
84393
+ wrapAnsi16m = (offset = 0) => (red, green2, blue2) => `\x1B[${38 + offset};2;${red};${green2};${blue2}m`;
84243
84394
  styles2 = {
84244
84395
  modifier: {
84245
84396
  reset: [0, 0],
@@ -94105,7 +94256,7 @@ Is ${source_default.bold.blue(this.base.name)} schema created or renamed from an
94105
94256
  });
94106
94257
  require_supports_colors = __commonJS22({
94107
94258
  "../node_modules/.pnpm/colors@1.4.0/node_modules/colors/lib/system/supports-colors.js"(exports, module2) {
94108
- var os2 = __require22("os");
94259
+ var os22 = __require22("os");
94109
94260
  var hasFlag2 = require_has_flag2();
94110
94261
  var env2 = process.env;
94111
94262
  var forceColor = undefined;
@@ -94143,7 +94294,7 @@ Is ${source_default.bold.blue(this.base.name)} schema created or renamed from an
94143
94294
  }
94144
94295
  var min2 = forceColor ? 1 : 0;
94145
94296
  if (process.platform === "win32") {
94146
- var osRelease = os2.release().split(".");
94297
+ var osRelease = os22.release().split(".");
94147
94298
  if (Number(process.versions.node.split(".")[0]) >= 8 && Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
94148
94299
  return Number(osRelease[2]) >= 14931 ? 3 : 2;
94149
94300
  }
@@ -121921,8 +122072,8 @@ var require_colorette = __commonJS2((exports) => {
121921
122072
  var createColors = ({ useColor = isColorSupported } = {}) => useColor ? colors3 : Object.keys(colors3).reduce((colors4, key) => ({ ...colors4, [key]: String }), {});
121922
122073
  var {
121923
122074
  reset,
121924
- bold: bold2,
121925
- dim: dim2,
122075
+ bold: bold22,
122076
+ dim: dim22,
121926
122077
  italic,
121927
122078
  underline,
121928
122079
  inverse,
@@ -121930,11 +122081,11 @@ var require_colorette = __commonJS2((exports) => {
121930
122081
  strikethrough,
121931
122082
  black,
121932
122083
  red,
121933
- green,
122084
+ green: green2,
121934
122085
  yellow,
121935
- blue,
122086
+ blue: blue2,
121936
122087
  magenta,
121937
- cyan: cyan2,
122088
+ cyan: cyan3,
121938
122089
  white,
121939
122090
  gray,
121940
122091
  bgBlack,
@@ -121947,7 +122098,7 @@ var require_colorette = __commonJS2((exports) => {
121947
122098
  bgWhite,
121948
122099
  blackBright,
121949
122100
  redBright,
121950
- greenBright,
122101
+ greenBright: greenBright2,
121951
122102
  yellowBright,
121952
122103
  blueBright,
121953
122104
  magentaBright,
@@ -121980,16 +122131,16 @@ var require_colorette = __commonJS2((exports) => {
121980
122131
  exports.bgYellowBright = bgYellowBright;
121981
122132
  exports.black = black;
121982
122133
  exports.blackBright = blackBright;
121983
- exports.blue = blue;
122134
+ exports.blue = blue2;
121984
122135
  exports.blueBright = blueBright;
121985
- exports.bold = bold2;
122136
+ exports.bold = bold22;
121986
122137
  exports.createColors = createColors;
121987
- exports.cyan = cyan2;
122138
+ exports.cyan = cyan3;
121988
122139
  exports.cyanBright = cyanBright;
121989
- exports.dim = dim2;
122140
+ exports.dim = dim22;
121990
122141
  exports.gray = gray;
121991
- exports.green = green;
121992
- exports.greenBright = greenBright;
122142
+ exports.green = green2;
122143
+ exports.greenBright = greenBright2;
121993
122144
  exports.hidden = hidden;
121994
122145
  exports.inverse = inverse;
121995
122146
  exports.isColorSupported = isColorSupported;
@@ -122168,7 +122319,7 @@ function processServerOptions(_port, options) {
122168
122319
  if (customLogger2) {
122169
122320
  setLogger(customLogger2);
122170
122321
  setCustomLogHandler((level, message, context2, scope) => {
122171
- const scopePrefix = scope ? `${import_picocolors3.default.bold(`[${scope}]`)} ` : "";
122322
+ const scopePrefix = scope ? `${import_picocolors5.default.bold(`[${scope}]`)} ` : "";
122172
122323
  let formattedMessage = `${scopePrefix}${message}`;
122173
122324
  if (context2) {
122174
122325
  const colorized = import_json_colorizer.colorize(context2);
@@ -122192,13 +122343,13 @@ ${stripped}`;
122192
122343
  };
122193
122344
  }
122194
122345
  var import_json_colorizer;
122195
- var import_picocolors3;
122346
+ var import_picocolors5;
122196
122347
  var init_options = __esm(() => {
122197
122348
  init_src2();
122198
122349
  init_config();
122199
122350
  init_logging();
122200
122351
  import_json_colorizer = __toESM2(require_dist2(), 1);
122201
- import_picocolors3 = __toESM2(require_picocolors2(), 1);
122352
+ import_picocolors5 = __toESM2(require_picocolors2(), 1);
122202
122353
  });
122203
122354
  var healthRouter;
122204
122355
  var init_health = __esm(() => {
@@ -124104,7 +124255,7 @@ async function getMockTimebackData(db2, timebackId, gameId) {
124104
124255
  const { role, organizations } = getMockStudentProfile();
124105
124256
  const allEnrollments = await getMockEnrollments(db2);
124106
124257
  const enrollments = gameId ? filterEnrollmentsByGame(allEnrollments, gameId) : allEnrollments;
124107
- log.debug("[Timeback] Sandbox is using mock data", {
124258
+ log2.debug("[Timeback] Sandbox is using mock data", {
124108
124259
  timebackId,
124109
124260
  role,
124110
124261
  enrollments: enrollments.map((e) => `${e.subject}:${e.grade}`),
@@ -124309,7 +124460,7 @@ var init_deploy = __esm(() => {
124309
124460
  init_src2();
124310
124461
  init_api();
124311
124462
  init_uploads();
124312
- logger4 = log.scope("SandboxDeploy");
124463
+ logger4 = log2.scope("SandboxDeploy");
124313
124464
  gameDeployRouter = new Hono2;
124314
124465
  gameDeployRouter.post("/:slug/deploy", async (c2) => {
124315
124466
  const user = c2.get("user");
@@ -125007,7 +125158,7 @@ var init_lti = __esm(() => {
125007
125158
  init_src2();
125008
125159
  init_constants();
125009
125160
  init_api();
125010
- logger5 = log.scope("SandboxLti");
125161
+ logger5 = log2.scope("SandboxLti");
125011
125162
  ltiRouter = new Hono2;
125012
125163
  ltiRouter.post("/launch", async (c2) => {
125013
125164
  const db2 = c2.get("db");
@@ -125194,68 +125345,10 @@ var init_server = __esm(() => {
125194
125345
  });
125195
125346
  init_server();
125196
125347
 
125197
- // src/lib/logging/adapter.ts
125198
- function createLoggerAdapter(domain) {
125199
- return {
125200
- info: (msg) => console.log(`${createLogPrefix("playcademy", domain)} ${msg}`),
125201
- warn: (msg) => console.warn(`${createLogPrefix("playcademy", domain)} ${msg}`),
125202
- error: (msg) => console.error(`${createLogPrefix("playcademy", domain)} ${msg}`)
125203
- };
125204
- }
125205
- // src/lib/logging/utils.ts
125206
- var import_picocolors4 = __toESM(require_picocolors(), 1);
125207
-
125208
- // ../utils/src/string.ts
125209
- function pluralize(count2, singular, plural) {
125210
- return count2 === 1 ? singular : plural || `${singular}s`;
125211
- }
125212
-
125213
- // src/lib/logging/utils.ts
125214
- function createBackendBannerOptions(backendPort, vitePort) {
125215
- if (!backendPort) {
125216
- return;
125217
- }
125218
- return { port: backendPort, vitePort };
125219
- }
125220
- function createTimebackBannerOptions(timebackMode, courseCount, enrolledCount) {
125221
- if (!timebackMode || !courseCount) {
125222
- return;
125223
- }
125224
- return {
125225
- courseCount,
125226
- enrolledCount: enrolledCount ?? courseCount,
125227
- mode: timebackMode
125228
- };
125229
- }
125230
- function printBanner(viteConfig, options) {
125231
- const INDENT = " ".repeat(2);
125232
- const { version: version4, gameName, sandbox, backend, timeback } = options;
125233
- viteConfig.logger.info("");
125234
- viteConfig.logger.info(`${INDENT}${import_picocolors4.green(import_picocolors4.bold("PLAYCADEMY"))} ${import_picocolors4.green(`v${version4}`)}`);
125235
- viteConfig.logger.info("");
125236
- if (gameName) {
125237
- viteConfig.logger.info(`${INDENT}${import_picocolors4.green("➜")} ${import_picocolors4.bold("Project:")} ${import_picocolors4.cyan(gameName)}`);
125238
- }
125239
- if (sandbox?.enabled) {
125240
- viteConfig.logger.info(`${INDENT}${import_picocolors4.green("➜")} ${import_picocolors4.bold("Sandbox:")} ${import_picocolors4.cyan(`http://localhost:${import_picocolors4.bold(sandbox.port.toString())}/api`)}`);
125241
- } else if (sandbox) {
125242
- viteConfig.logger.info(`${INDENT}${import_picocolors4.green("➜")} ${import_picocolors4.bold("Sandbox:")} ${import_picocolors4.cyan("Disabled")}`);
125243
- }
125244
- if (backend) {
125245
- const backendUrl = backend.vitePort ? `http://localhost:${import_picocolors4.bold(backend.vitePort.toString())}/api ${import_picocolors4.dim(`(via ${backend.port})`)}` : `http://localhost:${import_picocolors4.bold(backend.port.toString())}/api`;
125246
- viteConfig.logger.info(`${INDENT}${import_picocolors4.green("➜")} ${import_picocolors4.bold("Backend:")} ${import_picocolors4.cyan(backendUrl)}`);
125247
- }
125248
- if (timeback && timeback.courseCount > 0) {
125249
- const enrollmentInfo = timeback.enrolledCount !== timeback.courseCount ? ` / ${timeback.enrolledCount} enrolled` : "";
125250
- const label = `${timeback.courseCount} ${pluralize(timeback.courseCount, "course")}${enrollmentInfo} ${import_picocolors4.dim(`(${timeback.mode})`)}`;
125251
- viteConfig.logger.info(`${INDENT}${import_picocolors4.green("➜")} ${import_picocolors4.bold("Timeback:")} ${import_picocolors4.cyan(label)}`);
125252
- }
125253
- viteConfig.logger.info("");
125254
- }
125255
125348
  // src/lib/sandbox/project-info.ts
125256
125349
  import fs5 from "node:fs";
125257
125350
  import path3 from "node:path";
125258
- import { loadPlaycademyConfig } from "playcademy/utils";
125351
+ import { loadPlaycademyConfig as loadPlaycademyConfig2 } from "playcademy/utils";
125259
125352
 
125260
125353
  // ../utils/src/uuid.ts
125261
125354
  var UUID_REGEX2 = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
@@ -125476,7 +125569,7 @@ async function extractProjectInfo(viteConfig, timebackOptions) {
125476
125569
  const directoryName = path3.basename(projectRoot);
125477
125570
  let config2 = null;
125478
125571
  try {
125479
- config2 = await loadPlaycademyConfig();
125572
+ config2 = await loadPlaycademyConfig2();
125480
125573
  } catch {}
125481
125574
  let packageJson = {};
125482
125575
  try {
@@ -125508,7 +125601,7 @@ async function extractProjectInfo(viteConfig, timebackOptions) {
125508
125601
  }
125509
125602
 
125510
125603
  // src/lib/sandbox/timeback.ts
125511
- var import_picocolors5 = __toESM(require_picocolors(), 1);
125604
+ var import_picocolors6 = __toESM(require_picocolors(), 1);
125512
125605
  // ../constants/src/domains.ts
125513
125606
  var POSTHOG_CONFIG = {
125514
125607
  apiKey: "phc_z7aVQnB4P9FHm91g0BSfJTlIU6lISCBVhIJEFdfnYfX",
@@ -125573,9 +125666,9 @@ function validateTimebackConfig(viteConfig, timeback3) {
125573
125666
  if (realCourses.length > 0 && !hasTimebackCredentials2()) {
125574
125667
  const courseList = realCourses.map(([key]) => key).join(", ");
125575
125668
  viteConfig.logger.warn("");
125576
- viteConfig.logger.warn(import_picocolors5.default.yellow(`⚠️ TimeBack: Real course IDs for ${import_picocolors5.default.bold(courseList)} but credentials missing.`));
125577
- viteConfig.logger.warn(import_picocolors5.default.dim(` Required: TIMEBACK_API_CLIENT_ID, TIMEBACK_API_CLIENT_SECRET, TIMEBACK_API_AUTH_URL, TIMEBACK_ONEROSTER_API_URL`));
125578
- viteConfig.logger.warn(import_picocolors5.default.dim(` Or use 'mock' for local testing.`));
125669
+ viteConfig.logger.warn(import_picocolors6.default.yellow(`⚠️ TimeBack: Real course IDs for ${import_picocolors6.default.bold(courseList)} but credentials missing.`));
125670
+ viteConfig.logger.warn(import_picocolors6.default.dim(` Required: TIMEBACK_API_CLIENT_ID, TIMEBACK_API_CLIENT_SECRET, TIMEBACK_API_AUTH_URL, TIMEBACK_ONEROSTER_API_URL`));
125671
+ viteConfig.logger.warn(import_picocolors6.default.dim(` Or use 'mock' for local testing.`));
125579
125672
  viteConfig.logger.warn("");
125580
125673
  }
125581
125674
  }
@@ -125694,7 +125787,7 @@ async function startSandbox(viteConfig, autoStart = true, options = {}) {
125694
125787
  }
125695
125788
  };
125696
125789
  } catch (error2) {
125697
- viteConfig.logger.error(import_picocolors6.default.red(`[Playcademy] Failed to start sandbox: ${error2}`));
125790
+ viteConfig.logger.error(import_picocolors7.default.red(`[Playcademy] Failed to start sandbox: ${error2}`));
125698
125791
  return {
125699
125792
  baseUrl: `http://localhost:${DEFAULT_PORTS2.SANDBOX}`,
125700
125793
  port: DEFAULT_PORTS2.SANDBOX,
@@ -126167,11 +126260,11 @@ async function recreateSandbox(options) {
126167
126260
  const viteServer = getViteServerRef();
126168
126261
  const prefix2 = createLogPrefix("playcademy", "sandbox");
126169
126262
  if (!viteServer) {
126170
- viteConfig.logger.error(`${prefix2} ${import_picocolors7.red("Cannot recreate sandbox database: no Vite server reference")}`);
126263
+ viteConfig.logger.error(`${prefix2} ${import_picocolors8.red("Cannot recreate sandbox database: no Vite server reference")}`);
126171
126264
  return { success: false, error: "No Vite server reference" };
126172
126265
  }
126173
126266
  if (!isShellMode(currentMode)) {
126174
- viteConfig.logger.warn(`${prefix2} ${import_picocolors7.yellow("can only recreate sandbox database in platform or demo mode")}`);
126267
+ viteConfig.logger.warn(`${prefix2} ${import_picocolors8.yellow("can only recreate sandbox database in platform or demo mode")}`);
126175
126268
  return { success: false, error: "Not in a shell-backed mode" };
126176
126269
  }
126177
126270
  viteConfig.logger.info(`${prefix2} recreating database...`);
@@ -126204,7 +126297,7 @@ async function recreateSandbox(options) {
126204
126297
  }
126205
126298
  });
126206
126299
  }
126207
- viteConfig.logger.info(`${prefix2} ${import_picocolors7.green("database recreated")}`);
126300
+ viteConfig.logger.info(`${prefix2} ${import_picocolors8.green("database recreated")}`);
126208
126301
  return { success: true };
126209
126302
  }
126210
126303
 
@@ -126251,38 +126344,38 @@ function setupConfigWatcher(server, viteConfig, platformModeOptions) {
126251
126344
  }
126252
126345
 
126253
126346
  // src/server/hotkeys/cycle-platform-role.ts
126254
- var import_picocolors8 = __toESM(require_picocolors(), 1);
126347
+ var import_picocolors9 = __toESM(require_picocolors(), 1);
126255
126348
 
126256
126349
  // src/types/internal.ts
126257
126350
  var TIMEBACK_ROLES = ["student", "parent", "teacher", "administrator"];
126258
126351
  var PLATFORM_ROLES = ["player", "developer", "admin"];
126259
126352
  // src/server/hotkeys/cycle-platform-role.ts
126260
- function cyclePlatformRole(logger) {
126353
+ function cyclePlatformRole(logger6) {
126261
126354
  const currentRole = getPlatformRoleOverride() ?? "player";
126262
126355
  const currentIndex = PLATFORM_ROLES.indexOf(currentRole);
126263
126356
  const nextIndex = (currentIndex + 1) % PLATFORM_ROLES.length;
126264
126357
  const nextRole = PLATFORM_ROLES[nextIndex];
126265
126358
  const prefix2 = createLogPrefix("playcademy", "user");
126266
126359
  setPlatformRoleOverride(nextRole);
126267
- logger.info(`${prefix2} ${import_picocolors8.red(currentRole)} → ${import_picocolors8.green(nextRole)}`);
126360
+ logger6.info(`${prefix2} ${import_picocolors9.red(currentRole)} → ${import_picocolors9.green(nextRole)}`);
126268
126361
  if (getViteServerRef()) {
126269
126362
  getViteServerRef()?.ws.send({ type: "full-reload", path: "*" });
126270
126363
  } else {
126271
- logger.warn(`${prefix2} ${import_picocolors8.yellow("Cannot cycle platform role: no Vite server reference")}`);
126364
+ logger6.warn(`${prefix2} ${import_picocolors9.yellow("Cannot cycle platform role: no Vite server reference")}`);
126272
126365
  }
126273
126366
  }
126274
126367
  function cyclePlatformRoleHotkey(options) {
126275
126368
  return {
126276
126369
  key: "p",
126277
- description: `${import_picocolors8.cyan(import_picocolors8.bold("[playcademy]"))} cycle platform role`,
126370
+ description: `${import_picocolors9.cyan(import_picocolors9.bold("[playcademy]"))} cycle platform role`,
126278
126371
  action: () => cyclePlatformRole(options.viteConfig.logger)
126279
126372
  };
126280
126373
  }
126281
126374
 
126282
126375
  // src/server/hotkeys/cycle-timeback-role.ts
126283
- var import_picocolors9 = __toESM(require_picocolors(), 1);
126284
- var { bold: bold5, cyan: cyan4, green: green4, red: red3, yellow: yellow3 } = import_picocolors9.default;
126285
- function cycleTimebackRole(logger) {
126376
+ var import_picocolors10 = __toESM(require_picocolors(), 1);
126377
+ var { bold: bold5, cyan: cyan4, green: green4, red: red3, yellow: yellow3 } = import_picocolors10.default;
126378
+ function cycleTimebackRole(logger6) {
126286
126379
  const currentRole = getTimebackRoleOverride() ?? "student";
126287
126380
  const currentIndex = TIMEBACK_ROLES.indexOf(currentRole);
126288
126381
  const nextIndex = (currentIndex + 1) % TIMEBACK_ROLES.length;
@@ -126290,11 +126383,11 @@ function cycleTimebackRole(logger) {
126290
126383
  setTimebackRoleOverride(nextRole);
126291
126384
  getSandboxRef()?.setRole?.(nextRole);
126292
126385
  const prefix2 = createLogPrefix("playcademy", "timeback");
126293
- logger.info(`${prefix2} ${red3(currentRole)} → ${green4(nextRole)}`);
126386
+ logger6.info(`${prefix2} ${red3(currentRole)} → ${green4(nextRole)}`);
126294
126387
  if (getViteServerRef()) {
126295
126388
  getViteServerRef()?.ws.send({ type: "full-reload", path: "*" });
126296
126389
  } else {
126297
- logger.warn(`${prefix2} ${yellow3("Cannot cycle TimeBack role: no Vite server reference")}`);
126390
+ logger6.warn(`${prefix2} ${yellow3("Cannot cycle TimeBack role: no Vite server reference")}`);
126298
126391
  }
126299
126392
  }
126300
126393
  function cycleTimebackRoleHotkey(options) {
@@ -126306,8 +126399,8 @@ function cycleTimebackRoleHotkey(options) {
126306
126399
  }
126307
126400
 
126308
126401
  // src/server/hotkeys/recreate-database.ts
126309
- var import_picocolors10 = __toESM(require_picocolors(), 1);
126310
- var { bold: bold6, cyan: cyan5 } = import_picocolors10.default;
126402
+ var import_picocolors11 = __toESM(require_picocolors(), 1);
126403
+ var { bold: bold6, cyan: cyan5 } = import_picocolors11.default;
126311
126404
  async function recreateSandboxDatabase(options) {
126312
126405
  await recreateSandbox({
126313
126406
  viteConfig: options.viteConfig,
@@ -126323,7 +126416,7 @@ function recreateDatabaseHotkey(options) {
126323
126416
  }
126324
126417
 
126325
126418
  // src/server/hotkeys/toggle-mode.ts
126326
- var import_picocolors12 = __toESM(require_picocolors(), 1);
126419
+ var import_picocolors13 = __toESM(require_picocolors(), 1);
126327
126420
 
126328
126421
  // src/lib/backend/server.ts
126329
126422
  import {
@@ -126333,7 +126426,7 @@ import {
126333
126426
  } from "playcademy/utils";
126334
126427
 
126335
126428
  // src/lib/backend/hot-reload.ts
126336
- var import_picocolors11 = __toESM(require_picocolors(), 1);
126429
+ var import_picocolors12 = __toESM(require_picocolors(), 1);
126337
126430
  import path5 from "node:path";
126338
126431
  function createHotReloadCallbacks(viteConfig) {
126339
126432
  function formatPath(changedPath) {
@@ -126350,7 +126443,7 @@ function createHotReloadCallbacks(viteConfig) {
126350
126443
  onSuccess: (changedPath) => {
126351
126444
  const relativePath = formatPath(changedPath);
126352
126445
  if (relativePath) {
126353
- viteConfig.logger.info(`${prefix2} ${import_picocolors11.green("hmr update")} ${import_picocolors11.dim(relativePath)}`);
126446
+ viteConfig.logger.info(`${prefix2} ${import_picocolors12.green("hmr update")} ${import_picocolors12.dim(relativePath)}`);
126354
126447
  } else {
126355
126448
  viteConfig.logger.info(`${prefix2} reloaded`);
126356
126449
  }
@@ -126532,7 +126625,7 @@ async function toggleMode(options) {
126532
126625
  const viteServer = getViteServerRef();
126533
126626
  const prefix2 = createLogPrefix("playcademy");
126534
126627
  if (!viteServer) {
126535
- options.viteConfig.logger.error(`${prefix2} ${import_picocolors12.red("Cannot toggle mode: no Vite server reference")}`);
126628
+ options.viteConfig.logger.error(`${prefix2} ${import_picocolors13.red("Cannot toggle mode: no Vite server reference")}`);
126536
126629
  return;
126537
126630
  }
126538
126631
  await cleanupServers();
@@ -126546,12 +126639,12 @@ async function toggleMode(options) {
126546
126639
  } else {
126547
126640
  await configurePlatformMode(viteServer, options.viteConfig, options.platformModeOptions);
126548
126641
  }
126549
- options.viteConfig.logger.info(`${prefix2} ${import_picocolors12.green("switched to")} ${import_picocolors12.green(import_picocolors12.bold(newMode))} ${import_picocolors12.green("mode")}`);
126642
+ options.viteConfig.logger.info(`${prefix2} ${import_picocolors13.green("switched to")} ${import_picocolors13.green(import_picocolors13.bold(newMode))} ${import_picocolors13.green("mode")}`);
126550
126643
  }
126551
126644
  function toggleModeHotkey(options) {
126552
126645
  return {
126553
126646
  key: "m",
126554
- description: `${import_picocolors12.cyan(import_picocolors12.bold("[playcademy]"))} cycle platform/demo/standalone mode`,
126647
+ description: `${import_picocolors13.cyan(import_picocolors13.bold("[playcademy]"))} cycle platform/demo/standalone mode`,
126555
126648
  action: () => toggleMode(options)
126556
126649
  };
126557
126650
  }
@@ -126594,6 +126687,7 @@ async function configureServerHook(server, context) {
126594
126687
  setViteServerRef(server);
126595
126688
  setCurrentMode(context.options.mode);
126596
126689
  setupProcessShutdownHandlers();
126690
+ await registerAssetMiddleware(server, context.viteConfig.root, context.options.configPath);
126597
126691
  if (hasActiveServers()) {
126598
126692
  await cleanupServers();
126599
126693
  }
@@ -126672,17 +126766,24 @@ function buildInitCall() {
126672
126766
  ].join(",");
126673
126767
  return `posthog.init('${POSTHOG_CONFIG.apiKey}',{${config2}},'${POSTHOG_INSTANCE_NAME}');`;
126674
126768
  }
126675
- function transformIndexHtmlHook(context) {
126676
- if (context.viteConfig?.command !== "build") {
126677
- return;
126769
+ async function transformIndexHtmlHook(context) {
126770
+ const isBuild = context.viteConfig?.command === "build";
126771
+ const tags = [];
126772
+ if (!isBuild) {
126773
+ const manifestScript = await getDevManifestScript(context.options.configPath);
126774
+ if (manifestScript) {
126775
+ const body2 = manifestScript.replace(/^<script>/, "").replace(/<\/script>$/, "");
126776
+ tags.push({ tag: "script", children: body2, injectTo: "head" });
126777
+ }
126678
126778
  }
126679
- return [
126680
- {
126779
+ if (isBuild) {
126780
+ tags.push({
126681
126781
  tag: "script",
126682
126782
  children: SERVER_TIMING_PATCH + POSTHOG_LOADER + buildInitCall(),
126683
126783
  injectTo: "head"
126684
- }
126685
- ];
126784
+ });
126785
+ }
126786
+ return tags;
126686
126787
  }
126687
126788
 
126688
126789
  // src/hooks/write-bundle.ts
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Local asset dev support.
3
+ *
4
+ * In production the edge worker injects the asset manifest into the page and
5
+ * serves `/api/assets/*` from R2. There is no edge worker in `vite dev`, so the
6
+ * plugin reproduces both against the local Miniflare bucket that
7
+ * `playcademy bucket sync` writes to — making the SDK's `asset()` resolver work
8
+ * locally exactly as it does on the platform.
9
+ *
10
+ * A single Miniflare instance is opened for the dev server's lifetime (not one
11
+ * per request) and reused for the manifest read and every asset read. It still
12
+ * observes `bucket sync` writes to the same persist dir, so it stays fresh
13
+ * across syncs without reopening.
14
+ *
15
+ * The local-bucket reads go through `playcademy/bucket` (Node-only, build-time
16
+ * tooling). This never reaches a game's browser bundle — frontends use the
17
+ * dependency-free `@playcademy/sdk/assets` `asset()` resolver instead.
18
+ */
19
+ import type { ViteDevServer } from 'vite';
20
+ /** Dispose the persistent local-bucket session (call on dev-server shutdown). */
21
+ export declare function disposeAssetBucket(): Promise<void>;
22
+ /**
23
+ * Build the `<script>` injecting `self.__PLAYCADEMY_ASSET_MANIFEST` for dev,
24
+ * read from the local bucket. Returns `null` when bucket storage isn't enabled
25
+ * or no `bucket sync` has populated the local bucket yet — in the latter case,
26
+ * warns once so the dev knows why `asset()` paths 404 (re-syncing then reloading
27
+ * picks up the manifest without restarting the dev server).
28
+ */
29
+ export declare function getDevManifestScript(configPath?: string): Promise<string | null>;
30
+ /**
31
+ * Register the dev middleware that serves `/api/assets/*` from the local bucket,
32
+ * mirroring the edge asset route (the request path after the route prefix maps
33
+ * to the stored object).
34
+ *
35
+ * No-op when bucket storage isn't enabled, or when the game defines its own
36
+ * `server/api/assets` route — in that case `/api/assets/*` proxies to the
37
+ * backend so the custom route runs, matching production where a custom route
38
+ * supersedes the built-in one.
39
+ */
40
+ export declare function registerAssetMiddleware(server: ViteDevServer, projectRoot: string, configPath?: string): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playcademy/vite-plugin",
3
- "version": "1.0.1-beta.5",
3
+ "version": "1.1.0-beta.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -19,14 +19,14 @@
19
19
  "dependencies": {
20
20
  "archiver": "^7.0.1",
21
21
  "picocolors": "^1.1.1",
22
- "playcademy": "0.24.0"
22
+ "playcademy": "0.25.0-beta.2"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@electric-sql/pglite": "^0.3.16",
26
26
  "@inquirer/prompts": "^7.8.6",
27
27
  "@playcademy/constants": "0.0.1",
28
- "@playcademy/sandbox": "0.5.0",
29
- "@playcademy/sdk": "0.12.0",
28
+ "@playcademy/sandbox": "0.5.1-beta.6",
29
+ "@playcademy/sdk": "0.13.0-beta.2",
30
30
  "@playcademy/types": "0.0.1",
31
31
  "@playcademy/utils": "0.0.1",
32
32
  "@types/archiver": "^6.0.3",