@playcademy/sandbox 0.6.1-beta.4 → 0.6.1-beta.5

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
@@ -204,6 +204,20 @@ var init_auth = __esm(() => {
204
204
  };
205
205
  });
206
206
 
207
+ // ../constants/src/dashboard.ts
208
+ var DASHBOARD_USER_ROLES, DASHBOARD_WORKER_ROUTES, DASHBOARD_WORKER_SUFFIX = "-dash";
209
+ var init_dashboard = __esm(() => {
210
+ DASHBOARD_USER_ROLES = {
211
+ ADMIN: "admin",
212
+ VIEWER: "viewer"
213
+ };
214
+ DASHBOARD_WORKER_ROUTES = {
215
+ verifySession: "/sessions",
216
+ acceptInvite: "/invites/accept",
217
+ acceptReset: "/resets/accept"
218
+ };
219
+ });
220
+
207
221
  // ../constants/src/typescript.ts
208
222
  var init_typescript = () => {};
209
223
 
@@ -245,17 +259,8 @@ var init_platform = __esm(() => {
245
259
  var PLATFORM_TIMEZONE = "America/New_York";
246
260
 
247
261
  // ../constants/src/timeback.ts
248
- var TIMEBACK_ROUTES, TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY", TIMEBACK_ORG_NAME = "Playcademy Studios", TIMEBACK_ORG_TYPE = "department", TIMEBACK_COURSE_DEFAULTS, TIMEBACK_RESOURCE_DEFAULTS, TIMEBACK_COMPONENT_DEFAULTS, TIMEBACK_COMPONENT_RESOURCE_DEFAULTS, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
262
+ var TIMEBACK_ORG_SOURCED_ID = "PLAYCADEMY", TIMEBACK_ORG_NAME = "Playcademy Studios", TIMEBACK_ORG_TYPE = "department", TIMEBACK_COURSE_DEFAULTS, TIMEBACK_RESOURCE_DEFAULTS, TIMEBACK_COMPONENT_DEFAULTS, TIMEBACK_COMPONENT_RESOURCE_DEFAULTS, TIMEBACK_GAME_METRIC_DECIMAL_PLACES, TIMEBACK_GAME_METRIC_COMPARISON_TOLERANCE;
249
263
  var init_timeback2 = __esm(() => {
250
- TIMEBACK_ROUTES = {
251
- END_ACTIVITY: "/integrations/timeback/end-activity",
252
- GET_XP: "/integrations/timeback/xp",
253
- GET_MASTERY: "/integrations/timeback/mastery",
254
- GET_HIGHEST_GRADE_MASTERED: "/integrations/timeback/highest-grade-mastered",
255
- HEARTBEAT: "/integrations/timeback/heartbeat",
256
- ADVANCE_COURSE: "/integrations/timeback/advance-course",
257
- UNENROLL_COURSE: "/integrations/timeback/unenroll-course"
258
- };
259
264
  TIMEBACK_COURSE_DEFAULTS = {
260
265
  gradingScheme: "STANDARD",
261
266
  level: {
@@ -306,7 +311,7 @@ var init_timeback2 = __esm(() => {
306
311
  });
307
312
 
308
313
  // ../constants/src/cloudflare.ts
309
- var WORKER_NAMING, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
314
+ var WORKER_NAMING, MAX_WORKER_NAME_LENGTH = 63, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
310
315
  var init_cloudflare = __esm(() => {
311
316
  WORKER_NAMING = {
312
317
  STAGING_PREFIX: "staging-",
@@ -318,6 +323,7 @@ var init_cloudflare = __esm(() => {
318
323
  // ../constants/src/index.ts
319
324
  var init_src = __esm(() => {
320
325
  init_auth();
326
+ init_dashboard();
321
327
  init_typescript();
322
328
  init_domains();
323
329
  init_env_vars();
@@ -1057,8 +1063,7 @@ async function waitForPort(port, timeoutMs = 5000) {
1057
1063
  const start2 = Date.now();
1058
1064
  while (await isPortInUse(port)) {
1059
1065
  if (Date.now() - start2 > timeoutMs) {
1060
- throw new Error(`Port ${port} is already in use.
1061
- ` + `Stop the other server or specify a different port with --port <number>.`);
1066
+ throw new Error(`Port ${port} is already in use`);
1062
1067
  }
1063
1068
  await new Promise((resolve) => setTimeout(resolve, 100));
1064
1069
  }
@@ -1067,8 +1072,7 @@ async function requirePortAvailable(port, timeoutMs = 100) {
1067
1072
  const start2 = Date.now();
1068
1073
  while (await isPortInUse(port)) {
1069
1074
  if (Date.now() - start2 > timeoutMs) {
1070
- throw new Error(`Port ${port} is already in use.
1071
- ` + `Stop the other server or specify a different port with --port <number>.`);
1075
+ throw new Error(`Port ${port} is already in use`);
1072
1076
  }
1073
1077
  await new Promise((resolve) => setTimeout(resolve, 50));
1074
1078
  }
@@ -1080,7 +1084,7 @@ var package_default;
1080
1084
  var init_package = __esm(() => {
1081
1085
  package_default = {
1082
1086
  name: "@playcademy/sandbox",
1083
- version: "0.6.1-beta.4",
1087
+ version: "0.6.1-beta.5",
1084
1088
  description: "Local development server for Playcademy game development",
1085
1089
  type: "module",
1086
1090
  exports: {
@@ -1149,7 +1153,7 @@ var init_package = __esm(() => {
1149
1153
  });
1150
1154
 
1151
1155
  // ../api-core/src/errors/domain.error.ts
1152
- var DomainError, BadRequestError, AccessDeniedError, NotFoundError, ConflictError, AlreadyExistsError, ValidationError, InternalError, ServiceUnavailableError, TimeoutError;
1156
+ var DomainError, BadRequestError, UnauthorizedError, AccessDeniedError, NotFoundError, ConflictError, AlreadyExistsError, ValidationError, RateLimitError, InternalError, ServiceUnavailableError, TimeoutError;
1153
1157
  var init_domain_error = __esm(() => {
1154
1158
  DomainError = class DomainError extends Error {
1155
1159
  code;
@@ -1167,6 +1171,12 @@ var init_domain_error = __esm(() => {
1167
1171
  this.name = "BadRequestError";
1168
1172
  }
1169
1173
  };
1174
+ UnauthorizedError = class UnauthorizedError extends DomainError {
1175
+ constructor(message = "Unauthorized", details) {
1176
+ super("UNAUTHORIZED", message, details);
1177
+ this.name = "UnauthorizedError";
1178
+ }
1179
+ };
1170
1180
  AccessDeniedError = class AccessDeniedError extends DomainError {
1171
1181
  constructor(message = "Access denied", details) {
1172
1182
  super("ACCESS_DENIED", message, details);
@@ -1199,6 +1209,12 @@ var init_domain_error = __esm(() => {
1199
1209
  this.name = "ValidationError";
1200
1210
  }
1201
1211
  };
1212
+ RateLimitError = class RateLimitError extends DomainError {
1213
+ constructor(message = "Rate limit exceeded", details) {
1214
+ super("RATE_LIMITED", message, details);
1215
+ this.name = "RateLimitError";
1216
+ }
1217
+ };
1202
1218
  InternalError = class InternalError extends DomainError {
1203
1219
  constructor(message = "Internal error", details) {
1204
1220
  super("INTERNAL", message, details);
@@ -1557,12 +1573,24 @@ data: ${JSON.stringify(data)}
1557
1573
  return sseEncoder.encode(payload);
1558
1574
  }
1559
1575
  function createContextBuilder(options) {
1560
- const { db, config: config2, providers, services, cloudflare: cloudflare2, timeback: timeback2, resolveUser, resolveGameId } = options;
1576
+ const {
1577
+ db,
1578
+ config: config2,
1579
+ providers,
1580
+ services,
1581
+ cloudflare: cloudflare2,
1582
+ timeback: timeback2,
1583
+ resolveUser,
1584
+ resolveGameId,
1585
+ resolveApiKey
1586
+ } = options;
1561
1587
  return async (c) => {
1562
1588
  const user = resolveUser ? await resolveUser(c) : null;
1563
1589
  const gameId = resolveGameId ? await resolveGameId(c) : undefined;
1590
+ const apiKey = resolveApiKey ? await resolveApiKey(c) : undefined;
1564
1591
  return {
1565
1592
  user,
1593
+ apiKey,
1566
1594
  gameId,
1567
1595
  params: c.req.param(),
1568
1596
  url: new URL(c.req.url),
@@ -11181,7 +11209,7 @@ var init_table4 = __esm(() => {
11181
11209
  });
11182
11210
 
11183
11211
  // ../data/src/domains/game/table.ts
11184
- var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
11212
+ var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, gameDashboardUserRoleEnum, gameDashboardUsers, gameDashboardUsersRelations, deploymentProviderEnum, deploymentTargetEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
11185
11213
  var init_table5 = __esm(() => {
11186
11214
  init_drizzle_orm();
11187
11215
  init_pg_core();
@@ -11225,7 +11253,37 @@ var init_table5 = __esm(() => {
11225
11253
  references: [users.id]
11226
11254
  })
11227
11255
  }));
11256
+ gameDashboardUserRoleEnum = pgEnum("game_dashboard_user_role", [
11257
+ DASHBOARD_USER_ROLES.ADMIN,
11258
+ DASHBOARD_USER_ROLES.VIEWER
11259
+ ]);
11260
+ gameDashboardUsers = pgTable("game_dashboard_users", {
11261
+ id: uuid("id").primaryKey().defaultRandom(),
11262
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
11263
+ email: text("email").notNull(),
11264
+ displayName: text("display_name"),
11265
+ role: gameDashboardUserRoleEnum("role").notNull().default(DASHBOARD_USER_ROLES.VIEWER),
11266
+ passwordHash: text("password_hash"),
11267
+ inviteTokenHash: text("invite_token_hash"),
11268
+ inviteExpiresAt: timestamp("invite_expires_at", { withTimezone: true }),
11269
+ resetTokenHash: text("reset_token_hash"),
11270
+ resetExpiresAt: timestamp("reset_expires_at", { withTimezone: true }),
11271
+ failedLoginAttempts: integer("failed_login_attempts").notNull().default(0),
11272
+ lastFailedLoginAt: timestamp("last_failed_login_at", { withTimezone: true }),
11273
+ platformUserId: text("platform_user_id").references(() => users.id, {
11274
+ onDelete: "set null"
11275
+ }),
11276
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
11277
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
11278
+ }, (table3) => [uniqueIndex("game_dashboard_users_game_email_idx").on(table3.gameId, table3.email)]);
11279
+ gameDashboardUsersRelations = relations(gameDashboardUsers, ({ one }) => ({
11280
+ game: one(games, {
11281
+ fields: [gameDashboardUsers.gameId],
11282
+ references: [games.id]
11283
+ })
11284
+ }));
11228
11285
  deploymentProviderEnum = pgEnum("deployment_provider", ["cloudflare", "aws"]);
11286
+ deploymentTargetEnum = pgEnum("deployment_target", ["game", "dashboard"]);
11229
11287
  deployJobStatusEnum = pgEnum("deploy_job_status", [
11230
11288
  "pending",
11231
11289
  "running",
@@ -11237,6 +11295,7 @@ var init_table5 = __esm(() => {
11237
11295
  gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
11238
11296
  deploymentId: text("deployment_id").notNull(),
11239
11297
  provider: deploymentProviderEnum("provider").notNull(),
11298
+ target: deploymentTargetEnum("target").notNull().default("game"),
11240
11299
  url: text("url").notNull(),
11241
11300
  codeHash: text("code_hash"),
11242
11301
  isActive: boolean("is_active").notNull().default(false),
@@ -11424,8 +11483,12 @@ __export(exports_tables_index, {
11424
11483
  gameMemberRoleEnum: () => gameMemberRoleEnum,
11425
11484
  gameDeployments: () => gameDeployments,
11426
11485
  gameDeployJobs: () => gameDeployJobs,
11486
+ gameDashboardUsersRelations: () => gameDashboardUsersRelations,
11487
+ gameDashboardUsers: () => gameDashboardUsers,
11488
+ gameDashboardUserRoleEnum: () => gameDashboardUserRoleEnum,
11427
11489
  gameCustomHostnames: () => gameCustomHostnames,
11428
11490
  developerStatusEnum: () => developerStatusEnum,
11491
+ deploymentTargetEnum: () => deploymentTargetEnum,
11429
11492
  deploymentProviderEnum: () => deploymentProviderEnum,
11430
11493
  deployJobStatusEnum: () => deployJobStatusEnum,
11431
11494
  customHostnameStatusEnum: () => customHostnameStatusEnum,
@@ -12929,6 +12992,10 @@ function errorMessage(err2) {
12929
12992
  function errorType(err2) {
12930
12993
  return err2 instanceof Error ? err2.constructor.name : typeof err2;
12931
12994
  }
12995
+ function isUniqueViolation(err2) {
12996
+ return typeof err2 === "object" && err2 !== null && err2.code === PG_UNIQUE_VIOLATION;
12997
+ }
12998
+ var PG_UNIQUE_VIOLATION = "23505";
12932
12999
 
12933
13000
  // ../otel/src/spans.ts
12934
13001
  function truncate(value) {
@@ -13007,6 +13074,310 @@ var init_spans = __esm(() => {
13007
13074
  ROOT_SPAN_KEY = import_api2.createContextKey("playcademy.root_span");
13008
13075
  });
13009
13076
 
13077
+ // ../utils/src/random.ts
13078
+ async function generateSecureRandomString(length) {
13079
+ const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
13080
+ const randomValues = new Uint8Array(length);
13081
+ crypto.getRandomValues(randomValues);
13082
+ return [...randomValues].map((byte) => charset[byte % charset.length]).join("");
13083
+ }
13084
+
13085
+ // ../api-core/src/utils/hash.util.ts
13086
+ async function sha256Hex(input) {
13087
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
13088
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
13089
+ }
13090
+
13091
+ // ../api-core/src/utils/dashboard.util.ts
13092
+ function dashboardCredentialScope(slug) {
13093
+ return `${CREDENTIAL_SCOPE_PREFIX}${slug}`;
13094
+ }
13095
+ function dashboardCredentialSlugs(permissions) {
13096
+ return (permissions?.[DASHBOARD_PERMISSION_NAMESPACE] ?? []).filter((scope) => scope.startsWith(CREDENTIAL_SCOPE_PREFIX)).map((scope) => scope.slice(CREDENTIAL_SCOPE_PREFIX.length)).filter((slug) => slug.length > 0);
13097
+ }
13098
+ function isAllowedDashboardWorkerRequest(method, pathname, permissions) {
13099
+ const path = pathname.replace(/\/+$/, "");
13100
+ return dashboardCredentialSlugs(permissions).some((slug) => {
13101
+ if (method === "GET") {
13102
+ return path === `/api/games/${slug}`;
13103
+ }
13104
+ if (method === "POST") {
13105
+ return Object.values(DASHBOARD_WORKER_ROUTES).some((subpath) => path === `/api/games/${slug}/dashboard${subpath}`);
13106
+ }
13107
+ return false;
13108
+ });
13109
+ }
13110
+ function hashDashboardToken(token) {
13111
+ return sha256Hex(token);
13112
+ }
13113
+ function loginBackoffMs(failedAttempts) {
13114
+ if (failedAttempts < LOGIN_BACKOFF_THRESHOLD) {
13115
+ return 0;
13116
+ }
13117
+ return Math.min(LOGIN_BACKOFF_CAP_MS, LOGIN_BACKOFF_BASE_MS * 2 ** (failedAttempts - LOGIN_BACKOFF_THRESHOLD));
13118
+ }
13119
+ var DASHBOARD_PERMISSION_NAMESPACE = "dashboard", DASHBOARD_WORKER_KEY_RESTRICTED = "The dashboard worker key is restricted to dashboard runtime endpoints", CREDENTIAL_SCOPE_PREFIX = "credentials:", LOGIN_BACKOFF_THRESHOLD = 5, LOGIN_BACKOFF_BASE_MS = 1000, LOGIN_BACKOFF_CAP_MS;
13120
+ var init_dashboard_util = __esm(() => {
13121
+ init_src();
13122
+ LOGIN_BACKOFF_CAP_MS = 5 * 60 * 1000;
13123
+ });
13124
+
13125
+ // ../api-core/src/services/dashboard.service.ts
13126
+ function alreadyExists(gameId) {
13127
+ return new AlreadyExistsError("A dashboard user with this email already exists", { gameId });
13128
+ }
13129
+ var DashboardService;
13130
+ var init_dashboard_service = __esm(() => {
13131
+ init_drizzle_orm();
13132
+ init_tables_index();
13133
+ init_spans();
13134
+ init_errors();
13135
+ init_dashboard_util();
13136
+ DashboardService = class DashboardService {
13137
+ deps;
13138
+ static INVITE_TOKEN_LENGTH = 48;
13139
+ static INVITE_TTL_MS = 72 * 60 * 60 * 1000;
13140
+ static RESET_TTL_MS = 24 * 60 * 60 * 1000;
13141
+ constructor(deps) {
13142
+ this.deps = deps;
13143
+ }
13144
+ async listUsers(slug, user) {
13145
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13146
+ const rows = await this.deps.db.select({
13147
+ id: gameDashboardUsers.id,
13148
+ gameId: gameDashboardUsers.gameId,
13149
+ email: gameDashboardUsers.email,
13150
+ displayName: gameDashboardUsers.displayName,
13151
+ role: gameDashboardUsers.role,
13152
+ platformUserId: gameDashboardUsers.platformUserId,
13153
+ createdAt: gameDashboardUsers.createdAt,
13154
+ updatedAt: gameDashboardUsers.updatedAt,
13155
+ pending: sql`(${gameDashboardUsers.passwordHash} IS NULL)`
13156
+ }).from(gameDashboardUsers).where(eq(gameDashboardUsers.gameId, game2.id)).orderBy(asc(gameDashboardUsers.createdAt));
13157
+ setAttributes({
13158
+ "app.dashboard.operation": "list_users",
13159
+ "app.dashboard.result_count": rows.length
13160
+ });
13161
+ return rows;
13162
+ }
13163
+ async addUser(slug, user, input) {
13164
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13165
+ const email = input.email.toLowerCase();
13166
+ setAttributes({
13167
+ "app.dashboard.operation": "add_user",
13168
+ "app.dashboard.user_role": input.role
13169
+ });
13170
+ const existing = await this.findByEmail(game2.id, email);
13171
+ if (existing) {
13172
+ throw alreadyExists(game2.id);
13173
+ }
13174
+ const inviteToken = await generateSecureRandomString(DashboardService.INVITE_TOKEN_LENGTH);
13175
+ let created;
13176
+ try {
13177
+ [created] = await this.deps.db.insert(gameDashboardUsers).values({
13178
+ gameId: game2.id,
13179
+ email,
13180
+ displayName: input.displayName,
13181
+ role: input.role,
13182
+ inviteTokenHash: await hashDashboardToken(inviteToken),
13183
+ inviteExpiresAt: new Date(Date.now() + DashboardService.INVITE_TTL_MS),
13184
+ platformUserId: email === user.email.toLowerCase() ? user.id : undefined
13185
+ }).returning();
13186
+ } catch (error) {
13187
+ if (isUniqueViolation(error)) {
13188
+ throw alreadyExists(game2.id);
13189
+ }
13190
+ throw error;
13191
+ }
13192
+ if (!created) {
13193
+ throw new ValidationError("Failed to create dashboard user");
13194
+ }
13195
+ return { user: DashboardService.toSummary(created), token: inviteToken, kind: "invite" };
13196
+ }
13197
+ async removeUser(slug, user, dashboardUserId) {
13198
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13199
+ setAttributes({
13200
+ "app.dashboard.operation": "remove_user",
13201
+ "app.dashboard.user_id": dashboardUserId
13202
+ });
13203
+ const [deleted] = await this.deps.db.delete(gameDashboardUsers).where(and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.id, dashboardUserId))).returning({ id: gameDashboardUsers.id });
13204
+ if (!deleted) {
13205
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13206
+ }
13207
+ }
13208
+ async verifyLogin(slug, input) {
13209
+ const game2 = await this.getGameForRuntime(slug);
13210
+ setAttributes({ "app.dashboard.operation": "verify_login" });
13211
+ const dashboardUser = await this.findByEmail(game2.id, input.email.toLowerCase());
13212
+ if (!dashboardUser?.passwordHash) {
13213
+ throw new UnauthorizedError("Invalid credentials");
13214
+ }
13215
+ if (dashboardUser.lastFailedLoginAt) {
13216
+ const retryAt = dashboardUser.lastFailedLoginAt.getTime() + loginBackoffMs(dashboardUser.failedLoginAttempts);
13217
+ if (retryAt > Date.now()) {
13218
+ addEvent("dashboard.login_throttled", {
13219
+ "app.dashboard.failed_attempts": dashboardUser.failedLoginAttempts
13220
+ });
13221
+ throw new RateLimitError("Too many failed attempts — try again shortly", {
13222
+ retryAfterSeconds: Math.ceil((retryAt - Date.now()) / 1000)
13223
+ });
13224
+ }
13225
+ }
13226
+ const valid = await this.deps.verifyPassword({
13227
+ hash: dashboardUser.passwordHash,
13228
+ password: input.password
13229
+ });
13230
+ if (!valid) {
13231
+ await this.deps.db.update(gameDashboardUsers).set({
13232
+ failedLoginAttempts: sql`${gameDashboardUsers.failedLoginAttempts} + 1`,
13233
+ lastFailedLoginAt: new Date
13234
+ }).where(eq(gameDashboardUsers.id, dashboardUser.id));
13235
+ throw new UnauthorizedError("Invalid credentials");
13236
+ }
13237
+ if (dashboardUser.failedLoginAttempts > 0) {
13238
+ await this.deps.db.update(gameDashboardUsers).set({ failedLoginAttempts: 0, lastFailedLoginAt: null }).where(eq(gameDashboardUsers.id, dashboardUser.id));
13239
+ }
13240
+ return DashboardService.toSummary(dashboardUser);
13241
+ }
13242
+ async acceptInvite(slug, input) {
13243
+ const game2 = await this.getGameForRuntime(slug);
13244
+ setAttributes({ "app.dashboard.operation": "accept_invite" });
13245
+ const tokenHash = await hashDashboardToken(input.token);
13246
+ const invited = await this.deps.db.query.gameDashboardUsers.findFirst({
13247
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.inviteTokenHash, tokenHash))
13248
+ });
13249
+ if (!invited) {
13250
+ throw new UnauthorizedError("Invalid or already-used invite token");
13251
+ }
13252
+ if (invited.inviteExpiresAt && invited.inviteExpiresAt.getTime() < Date.now()) {
13253
+ throw new UnauthorizedError("This invite has expired — ask for a new one");
13254
+ }
13255
+ const passwordHash = await this.deps.hashPassword(input.password);
13256
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({
13257
+ passwordHash,
13258
+ inviteTokenHash: null,
13259
+ inviteExpiresAt: null,
13260
+ updatedAt: new Date
13261
+ }).where(and(eq(gameDashboardUsers.id, invited.id), eq(gameDashboardUsers.inviteTokenHash, tokenHash), isNull(gameDashboardUsers.passwordHash))).returning();
13262
+ if (!updated) {
13263
+ throw new UnauthorizedError("Invalid or already-used invite token");
13264
+ }
13265
+ return DashboardService.toSummary(updated);
13266
+ }
13267
+ async acceptReset(slug, input) {
13268
+ const game2 = await this.getGameForRuntime(slug);
13269
+ setAttributes({ "app.dashboard.operation": "accept_reset" });
13270
+ const tokenHash = await hashDashboardToken(input.token);
13271
+ const found = await this.deps.db.query.gameDashboardUsers.findFirst({
13272
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.resetTokenHash, tokenHash))
13273
+ });
13274
+ if (!found) {
13275
+ throw new UnauthorizedError("Invalid or already-used reset token");
13276
+ }
13277
+ if (found.resetExpiresAt && found.resetExpiresAt.getTime() < Date.now()) {
13278
+ throw new UnauthorizedError("This reset link has expired — ask for a new one");
13279
+ }
13280
+ const passwordHash = await this.deps.hashPassword(input.password);
13281
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({
13282
+ passwordHash,
13283
+ resetTokenHash: null,
13284
+ resetExpiresAt: null,
13285
+ inviteTokenHash: null,
13286
+ inviteExpiresAt: null,
13287
+ failedLoginAttempts: 0,
13288
+ lastFailedLoginAt: null,
13289
+ updatedAt: new Date
13290
+ }).where(and(eq(gameDashboardUsers.id, found.id), eq(gameDashboardUsers.resetTokenHash, tokenHash))).returning();
13291
+ if (!updated) {
13292
+ throw new UnauthorizedError("Invalid or already-used reset token");
13293
+ }
13294
+ return DashboardService.toSummary(updated);
13295
+ }
13296
+ async issueRecoveryLink(slug, user, dashboardUserId) {
13297
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13298
+ const existing = await this.deps.db.query.gameDashboardUsers.findFirst({
13299
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.id, dashboardUserId))
13300
+ });
13301
+ if (!existing) {
13302
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13303
+ }
13304
+ const kind = existing.passwordHash ? "reset" : "invite";
13305
+ setAttributes({
13306
+ "app.dashboard.operation": "issue_recovery_link",
13307
+ "app.dashboard.recovery_kind": kind
13308
+ });
13309
+ const token = await generateSecureRandomString(DashboardService.INVITE_TOKEN_LENGTH);
13310
+ const tokenHash = await hashDashboardToken(token);
13311
+ const fields = kind === "invite" ? {
13312
+ inviteTokenHash: tokenHash,
13313
+ inviteExpiresAt: new Date(Date.now() + DashboardService.INVITE_TTL_MS)
13314
+ } : {
13315
+ resetTokenHash: tokenHash,
13316
+ resetExpiresAt: new Date(Date.now() + DashboardService.RESET_TTL_MS)
13317
+ };
13318
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({ ...fields, updatedAt: new Date }).where(eq(gameDashboardUsers.id, existing.id)).returning();
13319
+ if (!updated) {
13320
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13321
+ }
13322
+ return { user: DashboardService.toSummary(updated), token, kind };
13323
+ }
13324
+ async getGameForRuntime(slug) {
13325
+ const game2 = await this.deps.db.query.games.findFirst({
13326
+ where: eq(games.slug, slug),
13327
+ columns: { id: true }
13328
+ });
13329
+ if (!game2) {
13330
+ throw new NotFoundError("Game", slug);
13331
+ }
13332
+ return game2;
13333
+ }
13334
+ async findByEmail(gameId, email) {
13335
+ const row = await this.deps.db.query.gameDashboardUsers.findFirst({
13336
+ where: and(eq(gameDashboardUsers.gameId, gameId), eq(gameDashboardUsers.email, email))
13337
+ });
13338
+ return row ?? null;
13339
+ }
13340
+ static toSummary(row) {
13341
+ return {
13342
+ id: row.id,
13343
+ gameId: row.gameId,
13344
+ email: row.email,
13345
+ displayName: row.displayName,
13346
+ role: row.role,
13347
+ platformUserId: row.platformUserId,
13348
+ createdAt: row.createdAt,
13349
+ updatedAt: row.updatedAt,
13350
+ pending: !row.passwordHash
13351
+ };
13352
+ }
13353
+ };
13354
+ });
13355
+
13356
+ // ../data/src/domains/game/helpers.ts
13357
+ function activeDeploymentWhere(gameId, target = "game") {
13358
+ return and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.target, target), eq(gameDeployments.isActive, true));
13359
+ }
13360
+ var init_helpers = __esm(() => {
13361
+ init_drizzle_orm();
13362
+ init_table5();
13363
+ });
13364
+
13365
+ // ../data/src/domains/timeback/helpers.ts
13366
+ function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
13367
+ return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
13368
+ }
13369
+ var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active", DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
13370
+ var init_helpers2 = __esm(() => {
13371
+ init_drizzle_orm();
13372
+ init_table7();
13373
+ });
13374
+
13375
+ // ../data/src/helpers.index.ts
13376
+ var init_helpers_index = __esm(() => {
13377
+ init_helpers();
13378
+ init_helpers2();
13379
+ });
13380
+
13010
13381
  // ../../node_modules/.bun/process-nextick-args@2.0.1/node_modules/process-nextick-args/index.js
13011
13382
  var require_process_nextick_args = __commonJS((exports, module2) => {
13012
13383
  if (typeof process === "undefined" || !process.version || process.version.indexOf("v0.") === 0 || process.version.indexOf("v1.") === 0 && process.version.indexOf("v1.8.") !== 0) {
@@ -22087,8 +22458,8 @@ var require_lib3 = __commonJS((exports, module2) => {
22087
22458
  });
22088
22459
 
22089
22460
  // ../utils/src/zip.ts
22090
- import { mkdir, writeFile } from "fs/promises";
22091
- import { join as join2 } from "path";
22461
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
22462
+ import { join as join2, relative, sep } from "path";
22092
22463
  async function extractZipToDirectory(zipBuffer, targetDir) {
22093
22464
  try {
22094
22465
  const zip = await import_jszip.default.loadAsync(zipBuffer);
@@ -22114,9 +22485,10 @@ async function extractZipToDirectory(zipBuffer, targetDir) {
22114
22485
  fileCount: Object.keys(zip.files).length
22115
22486
  });
22116
22487
  } catch (error) {
22117
- const errorMessage2 = error instanceof Error ? error.message : String(error);
22118
22488
  log.error("[utils/zip] Failed to extract ZIP", { targetDir, error });
22119
- throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage2}`, { cause: error });
22489
+ throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage(error)}`, {
22490
+ cause: error
22491
+ });
22120
22492
  }
22121
22493
  }
22122
22494
  var import_jszip;
@@ -22386,6 +22758,7 @@ class DeployJobService {
22386
22758
  jobId,
22387
22759
  leaseId,
22388
22760
  leaseLost,
22761
+ target,
22389
22762
  game: game2,
22390
22763
  user,
22391
22764
  error
@@ -22415,9 +22788,12 @@ class DeployJobService {
22415
22788
  }
22416
22789
  }
22417
22790
  if (!effectiveLeaseLost) {
22418
- await this.deps.notifyDeploymentFailure(game2.slug, game2.displayName, message, {
22419
- id: user.id,
22420
- email: user.email
22791
+ await this.deps.notifyDeploymentFailure({
22792
+ slug: game2.slug,
22793
+ displayName: game2.displayName,
22794
+ error: message,
22795
+ target,
22796
+ developer: { id: user.id, email: user.email }
22421
22797
  });
22422
22798
  }
22423
22799
  await this.deleteCodeBundle(jobId);
@@ -22519,7 +22895,7 @@ class DeployJobService {
22519
22895
  }
22520
22896
  assertLease();
22521
22897
  const activeDeployment = await this.deps.db.query.gameDeployments.findFirst({
22522
- where: and(eq(gameDeployments.gameId, job.gameId), eq(gameDeployments.isActive, true)),
22898
+ where: activeDeploymentWhere(job.gameId, request.target ?? "game"),
22523
22899
  columns: {
22524
22900
  deploymentId: true,
22525
22901
  url: true
@@ -22542,7 +22918,15 @@ class DeployJobService {
22542
22918
  } catch (error) {
22543
22919
  clearInterval(heartbeat);
22544
22920
  setAttribute("app.deploy_job.run_duration_ms", DeployJobService.elapsedMsSince(new Date(runStartedAt)));
22545
- await this.handleRunError({ jobId, leaseId, leaseLost, game: game2, user, error });
22921
+ await this.handleRunError({
22922
+ jobId,
22923
+ leaseId,
22924
+ leaseLost,
22925
+ target: request.target ?? "game",
22926
+ game: game2,
22927
+ user,
22928
+ error
22929
+ });
22546
22930
  throw error;
22547
22931
  }
22548
22932
  }
@@ -22561,6 +22945,7 @@ async function forceMarkDeployJobFailed(db2, jobId, error) {
22561
22945
  var STATUS_MAP2, DEPLOY_JOB_LEASE_MS, DEPLOY_JOB_HEARTBEAT_MS, DEPLOY_JOB_CODE_BUNDLE_PREFIX = "deploy-job-code";
22562
22946
  var init_deploy_job_service = __esm(() => {
22563
22947
  init_drizzle_orm();
22948
+ init_helpers_index();
22564
22949
  init_tables_index();
22565
22950
  init_spans();
22566
22951
  init_zip();
@@ -24704,7 +25089,7 @@ var init_bindings = __esm(() => {
24704
25089
  });
24705
25090
 
24706
25091
  // ../cloudflare/src/playcademy/provider/helpers.ts
24707
- var init_helpers = __esm(() => {
25092
+ var init_helpers3 = __esm(() => {
24708
25093
  init_mime();
24709
25094
  });
24710
25095
 
@@ -24716,7 +25101,7 @@ var init_provider = __esm(() => {
24716
25101
  init_constants2();
24717
25102
  init_assets_config();
24718
25103
  init_bindings();
24719
- init_helpers();
25104
+ init_helpers3();
24720
25105
  });
24721
25106
  // ../cloudflare/src/playcademy/provider/index.ts
24722
25107
  var init_provider2 = __esm(() => {
@@ -27167,7 +27552,7 @@ var init_stages = __esm(() => {
27167
27552
  });
27168
27553
 
27169
27554
  // ../api-core/src/utils/deployment.util.ts
27170
- function getDeploymentId(gameSlug, sstStage) {
27555
+ function getGameDeploymentId(gameSlug, sstStage) {
27171
27556
  if (sstStage === "production") {
27172
27557
  return gameSlug;
27173
27558
  }
@@ -27176,45 +27561,57 @@ function getDeploymentId(gameSlug, sstStage) {
27176
27561
  }
27177
27562
  return `${WORKER_NAMING.LOCAL_PREFIX}${sstStage}-${gameSlug}`;
27178
27563
  }
27564
+ function getDashboardDeploymentId(gameSlug, sstStage) {
27565
+ return `${getGameDeploymentId(gameSlug, sstStage)}${DASHBOARD_WORKER_SUFFIX}`;
27566
+ }
27179
27567
  function getGameWorkerApiKeyName(slug) {
27180
- return `game-worker-${slug}`.substring(0, 32);
27568
+ return `${GAME_WORKER_KEY_PREFIX}${slug}`.substring(0, 32);
27569
+ }
27570
+ function getDashboardWorkerApiKeyName(slug) {
27571
+ return `${DASHBOARD_WORKER_KEY_PREFIX}${slug}`;
27181
27572
  }
27182
27573
  function toBindingName(queueKey) {
27183
27574
  return `${queueKey.replace(/-/g, "_").toUpperCase()}_QUEUE`;
27184
27575
  }
27185
- async function generateDeploymentHash(code) {
27186
- const encoder = new TextEncoder;
27187
- const data = encoder.encode(code);
27188
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
27189
- const hashArray = [...new Uint8Array(hashBuffer)];
27190
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
27576
+ function generateDeploymentHash(code) {
27577
+ return sha256Hex(code);
27191
27578
  }
27579
+ var GAME_WORKER_KEY_PREFIX = "game-worker-", DASHBOARD_WORKER_KEY_PREFIX = "dash-worker-";
27192
27580
  var init_deployment_util = __esm(() => {
27193
27581
  init_src();
27194
27582
  init_stages();
27195
27583
  });
27196
27584
 
27585
+ // ../api-core/src/utils/worker-keys.util.ts
27586
+ async function sweepDashboardWorkerKeys(deleteApiKeysByName, slug) {
27587
+ try {
27588
+ await deleteApiKeysByName(getDashboardWorkerApiKeyName(slug));
27589
+ } catch (error) {
27590
+ addEvent("dashboard.worker_key_sweep_failed", {
27591
+ "exception.type": errorType(error),
27592
+ "app.error.message": errorMessage(error)
27593
+ });
27594
+ }
27595
+ }
27596
+ var init_worker_keys_util = __esm(() => {
27597
+ init_spans();
27598
+ init_deployment_util();
27599
+ });
27600
+
27197
27601
  // ../api-core/src/services/deploy.service.ts
27602
+ function readDashboardTheme(config2) {
27603
+ const theme = config2?.dashboard?.theme;
27604
+ return {
27605
+ ...typeof theme?.primary === "string" && { primary: theme.primary },
27606
+ ...typeof theme?.secondary === "string" && { secondary: theme.secondary }
27607
+ };
27608
+ }
27609
+
27198
27610
  class DeployService {
27199
27611
  deps;
27200
27612
  constructor(deps) {
27201
27613
  this.deps = deps;
27202
27614
  }
27203
- static classifyDeployType(flags2) {
27204
- if (flags2.hasBackend && flags2.hasFrontend) {
27205
- return "full_stack";
27206
- }
27207
- if (flags2.hasFrontend) {
27208
- return "frontend_only";
27209
- }
27210
- if (flags2.hasBackend) {
27211
- return "backend_only";
27212
- }
27213
- return "metadata_only";
27214
- }
27215
- static countDeadLetterQueues(bindings) {
27216
- return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
27217
- }
27218
27615
  getCloudflare() {
27219
27616
  const cf = this.deps.cloudflare;
27220
27617
  if (!cf) {
@@ -27222,86 +27619,307 @@ class DeployService {
27222
27619
  }
27223
27620
  return cf;
27224
27621
  }
27225
- async createApiKey(user, slug, keyName) {
27226
- const { key: apiKey } = await this.deps.createAuthApiKey({
27227
- userId: user.id,
27228
- name: keyName,
27229
- expiresIn: null,
27230
- permissions: {
27231
- games: [`read:${slug}`, `write:${slug}`]
27232
- },
27233
- rateLimitEnabled: false
27234
- });
27235
- setAttribute("app.deploy.api_key_outcome", "created");
27236
- return apiKey;
27237
- }
27238
- async regenerateApiKey(user, slug, existingKeyId, keyName) {
27239
- if (existingKeyId) {
27240
- setAttribute("app.deploy.api_key_outcome", "regenerated");
27241
- try {
27242
- await this.deps.deleteAuthApiKey(existingKeyId);
27243
- } catch (error) {
27244
- addEvent("deploy.api_key_revoke_failed", {
27245
- "exception.type": errorType(error),
27246
- "app.error.message": errorMessage(error),
27247
- "app.deploy.old_key_id": existingKeyId
27248
- });
27622
+ async* deploy(slug, request, user, uploadDeps, extractZip) {
27623
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
27624
+ const context2 = { slug, request, user, game: game2, uploadDeps, extractZip };
27625
+ switch (request.target ?? "game") {
27626
+ case "dashboard": {
27627
+ yield* this.deployDashboard(context2);
27628
+ break;
27629
+ }
27630
+ case "game": {
27631
+ yield* this.deployGame(context2);
27632
+ break;
27633
+ }
27634
+ default: {
27635
+ throw new ValidationError("Invalid deployment target");
27249
27636
  }
27250
27637
  }
27251
- return this.createApiKey(user, slug, keyName);
27252
27638
  }
27253
- async ensureApiKeyOnWorker(user, slug, deploymentId) {
27639
+ async* deployGame(context2) {
27640
+ const { slug, request, user, game: game2, uploadDeps, extractZip } = context2;
27254
27641
  const cf = this.getCloudflare();
27255
- const keyName = getGameWorkerApiKeyName(slug);
27256
- const existingKey = await this.deps.db.query.apikey.findFirst({
27257
- where: and(eq(apikey.userId, user.id), eq(apikey.name, keyName)),
27642
+ const db2 = this.deps.db;
27643
+ const flags2 = this.validateGameRequest(request);
27644
+ const { hasBackend, hasFrontend } = flags2;
27645
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
27646
+ let frontendAssetsPath;
27647
+ let tempDir;
27648
+ if (hasFrontend) {
27649
+ if (!uploadDeps || !extractZip) {
27650
+ throw new ValidationError("Upload dependencies not configured for frontend deployment");
27651
+ }
27652
+ yield { type: "status", data: { message: "Fetching temporary files" } };
27653
+ const extracted = await this.fetchAndExtractFrontendAssets(slug, game2.id, request.uploadToken, uploadDeps, extractZip);
27654
+ tempDir = extracted.tempDir;
27655
+ frontendAssetsPath = extracted.assetsPath;
27656
+ yield { type: "status", data: { message: "Extracting assets" } };
27657
+ }
27658
+ const platformBaseUrl = await this.resolvePlatformBaseUrl();
27659
+ const env = {
27660
+ GAME_ID: game2.id,
27661
+ PLAYCADEMY_BASE_URL: platformBaseUrl,
27662
+ PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
27663
+ };
27664
+ yield {
27665
+ type: "status",
27666
+ data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
27667
+ };
27668
+ const keepAssets = hasBackend && !hasFrontend;
27669
+ const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings, request.schema);
27670
+ const bindings = deploymentOptions?.bindings;
27671
+ setAttributes({
27672
+ "app.deploy.has_d1": Boolean(bindings?.d1?.length),
27673
+ "app.deploy.has_kv": Boolean(bindings?.kv?.length),
27674
+ "app.deploy.has_r2": Boolean(bindings?.r2?.length),
27675
+ "app.deploy.queue_count": bindings?.queues?.length ?? 0,
27676
+ "app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
27677
+ });
27678
+ const activeDeployment = await db2.query.gameDeployments.findFirst({
27679
+ where: activeDeploymentWhere(game2.id, "game"),
27680
+ columns: { resources: true }
27681
+ });
27682
+ if (deploymentOptions?.bindings?.d1?.length) {
27683
+ await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
27684
+ }
27685
+ const result = await this.deployToCloudflare({
27686
+ deploymentId,
27687
+ code: request.code,
27688
+ env,
27689
+ tempDir,
27690
+ options: {
27691
+ ...deploymentOptions,
27692
+ compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27693
+ compatibilityFlags: request.compatibilityFlags,
27694
+ existingResources: activeDeployment?.resources ?? undefined,
27695
+ assetsPath: frontendAssetsPath,
27696
+ keepAssets
27697
+ }
27698
+ });
27699
+ yield { type: "status", data: { message: "Finalizing deployment" } };
27700
+ const updatedGame = await this.finalizeGameDeployment({
27701
+ game: game2,
27702
+ slug,
27703
+ deploymentId,
27704
+ result,
27705
+ request,
27706
+ user,
27707
+ flags: flags2
27708
+ });
27709
+ yield { type: "complete", data: updatedGame };
27710
+ }
27711
+ async* deployDashboard(context2) {
27712
+ const { slug, request, user, game: game2 } = context2;
27713
+ const db2 = this.deps.db;
27714
+ const contract = this.validateDashboardRequest(request, context2.uploadDeps, context2.extractZip);
27715
+ const deploymentId = getDashboardDeploymentId(slug, this.deps.config.sstStage);
27716
+ if (deploymentId.length > MAX_WORKER_NAME_LENGTH) {
27717
+ throw new ValidationError(`Dashboard worker name '${deploymentId}' exceeds Cloudflare's ${MAX_WORKER_NAME_LENGTH}-character limit — shorten the game slug`);
27718
+ }
27719
+ const collidingGame = await db2.query.games.findFirst({
27720
+ where: eq(games.slug, `${slug}${DASHBOARD_WORKER_SUFFIX}`),
27258
27721
  columns: { id: true }
27259
27722
  });
27260
- let apiKey;
27261
- if (!existingKey) {
27262
- apiKey = await this.createApiKey(user, slug, keyName);
27263
- } else {
27264
- try {
27265
- const workerSecrets = await cf.listSecrets(deploymentId);
27266
- if (workerSecrets.includes("PLAYCADEMY_API_KEY")) {
27267
- setAttribute("app.deploy.api_key_outcome", "already_set");
27268
- return;
27269
- }
27270
- } catch (error) {
27271
- addEvent("deploy.worker_secrets_check_failed", {
27272
- "exception.type": errorType(error),
27273
- "app.error.message": errorMessage(error)
27274
- });
27723
+ if (collidingGame) {
27724
+ throw new ValidationError(`Cannot deploy this dashboard: an existing game's slug is '${slug}${DASHBOARD_WORKER_SUFFIX}', which owns the '${deploymentId}' worker name`);
27725
+ }
27726
+ const gameDeployment = await db2.query.gameDeployments.findFirst({
27727
+ where: activeDeploymentWhere(game2.id, "game"),
27728
+ columns: { resources: true }
27729
+ });
27730
+ if (!gameDeployment) {
27731
+ throw new ValidationError("Deploy the game before its dashboard — the dashboard attaches to the game deployment");
27732
+ }
27733
+ setAttributes({
27734
+ "app.deploy.type": "dashboard",
27735
+ "app.deploy.deployment_id": deploymentId,
27736
+ "app.deploy.code_size": contract.code.length,
27737
+ "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27738
+ "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0
27739
+ });
27740
+ yield { type: "status", data: { message: "Fetching temporary files" } };
27741
+ const [{ tempDir, assetsPath }, platformBaseUrl, dashboardActive] = await Promise.all([
27742
+ this.fetchAndExtractFrontendAssets(slug, game2.id, contract.uploadToken, contract.uploadDeps, contract.extractZip),
27743
+ this.resolvePlatformBaseUrl(),
27744
+ db2.query.gameDeployments.findFirst({
27745
+ where: activeDeploymentWhere(game2.id, "dashboard"),
27746
+ columns: { resources: true }
27747
+ })
27748
+ ]);
27749
+ yield { type: "status", data: { message: "Extracting assets" } };
27750
+ const theme = readDashboardTheme(request.config);
27751
+ const env = {
27752
+ GAME_ID: game2.id,
27753
+ GAME_SLUG: slug,
27754
+ GAME_NAME: game2.displayName,
27755
+ ...game2.metadata?.emoji && { GAME_EMOJI: game2.metadata.emoji },
27756
+ ...theme.primary && { DASHBOARD_THEME_PRIMARY: theme.primary },
27757
+ ...theme.secondary && { DASHBOARD_THEME_SECONDARY: theme.secondary },
27758
+ PLAYCADEMY_BASE_URL: platformBaseUrl,
27759
+ PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
27760
+ };
27761
+ const gameResources = gameDeployment.resources;
27762
+ const bindings = {
27763
+ d1: gameResources?.d1?.map((database) => database.name) ?? [],
27764
+ kv: gameResources?.kv?.map((namespace) => namespace.name) ?? [],
27765
+ r2: gameResources?.r2?.filter((bucket) => bucket.kind !== "assets").map((bucket) => bucket.name) ?? []
27766
+ };
27767
+ const existingResources = {
27768
+ ...dashboardActive?.resources,
27769
+ d1: gameResources?.d1,
27770
+ kv: gameResources?.kv,
27771
+ r2: gameResources?.r2
27772
+ };
27773
+ yield { type: "status", data: { message: "Deploying dashboard" } };
27774
+ const result = await this.deployToCloudflare({
27775
+ deploymentId,
27776
+ code: contract.code,
27777
+ env,
27778
+ tempDir,
27779
+ options: {
27780
+ bindings,
27781
+ compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27782
+ compatibilityFlags: request.compatibilityFlags,
27783
+ existingResources,
27784
+ assetsPath,
27785
+ keepAssets: false,
27786
+ assetsConfig: { run_worker_first: true }
27275
27787
  }
27276
- apiKey = await this.regenerateApiKey(user, slug, existingKey.id, keyName);
27788
+ });
27789
+ yield { type: "status", data: { message: "Finalizing deployment" } };
27790
+ await withSpan("deploy.configure_worker_secrets", async () => {
27791
+ const existingSecrets = await this.listWorkerSecretNames(result.deploymentId);
27792
+ await this.ensureWorkerApiKeyOnWorker(user, DeployService.dashboardWorkerKeySpec(slug), result.deploymentId, existingSecrets);
27793
+ await this.ensureDashboardSessionSecret(result.deploymentId, existingSecrets, Boolean(dashboardActive));
27794
+ });
27795
+ const codeHash = await generateDeploymentHash(contract.code);
27796
+ await this.saveDeployment({
27797
+ gameId: game2.id,
27798
+ deploymentId: result.deploymentId,
27799
+ url: result.url,
27800
+ codeHash,
27801
+ resources: result.resources,
27802
+ target: "dashboard"
27803
+ });
27804
+ setAttribute("app.deploy.code_hash", codeHash);
27805
+ try {
27806
+ await withSpan("deploy.send_alert", () => this.deps.alerts.notifyDashboardDeployment({
27807
+ slug,
27808
+ displayName: game2.displayName,
27809
+ url: result.url,
27810
+ developer: { id: user.id, email: user.email }
27811
+ }));
27812
+ } catch (error) {
27813
+ setAttribute("app.alerts.delivery", "failed");
27814
+ setAttribute("app.alerts.delivery_error", errorMessage(error));
27815
+ setAttribute("app.alerts.type", "dashboard_deployment");
27277
27816
  }
27278
- await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
27817
+ yield { type: "complete", data: game2 };
27279
27818
  }
27280
- async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
27281
- const secret = this.deps.config.queueIngressSecret;
27282
- if (!secret) {
27283
- return;
27819
+ validateGameRequest(request) {
27820
+ const hasBackend = Boolean(request.code);
27821
+ const hasFrontend = Boolean(request.uploadToken);
27822
+ const hasMetadata = Boolean(request.metadata);
27823
+ setAttributes({
27824
+ "app.deploy.has_backend": hasBackend,
27825
+ "app.deploy.has_frontend": hasFrontend,
27826
+ "app.deploy.has_metadata": hasMetadata,
27827
+ "app.deploy.type": DeployService.classifyGameDeployType({
27828
+ hasBackend,
27829
+ hasFrontend,
27830
+ hasMetadata
27831
+ }),
27832
+ "app.deploy.code_size": request.code?.length ?? 0,
27833
+ "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27834
+ "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
27835
+ "app.deploy.has_schema": Boolean(request.schema)
27836
+ });
27837
+ if (!hasBackend && !hasFrontend && !hasMetadata) {
27838
+ throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
27284
27839
  }
27285
- const cf = this.getCloudflare();
27286
- await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
27840
+ if (!this.deps.config.baseUrl) {
27841
+ throw new ValidationError("baseUrl is not configured");
27842
+ }
27843
+ return { hasBackend, hasFrontend, hasMetadata };
27287
27844
  }
27288
- async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
27289
- const frontendZip = await withSpan("deploy.fetch_temporary_files", () => uploadDeps.getObjectAsByteArray(uploadToken));
27290
- if (!frontendZip || frontendZip.length === 0) {
27291
- throw new ValidationError("Uploaded file is empty or not found");
27845
+ validateDashboardRequest(request, uploadDeps, extractZip) {
27846
+ if (!request.code || !request.uploadToken) {
27847
+ throw new ValidationError("Dashboard deployments require both worker code and an asset upload");
27292
27848
  }
27293
- setAttribute("app.deploy.asset_upload_size", frontendZip.length);
27294
- const os2 = await import("os");
27295
- const path = await import("path");
27296
- const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
27297
- const assetsPath = path.join(tempDir, "dist");
27298
- await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
27299
- uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
27300
- return { frontendZip, tempDir, assetsPath };
27849
+ if (request.metadata) {
27850
+ throw new ValidationError("Dashboard deployments cannot update game metadata");
27851
+ }
27852
+ if (!uploadDeps || !extractZip) {
27853
+ throw new ValidationError("Upload dependencies not configured for frontend deployment");
27854
+ }
27855
+ if (!this.deps.config.baseUrl) {
27856
+ throw new ValidationError("baseUrl is not configured");
27857
+ }
27858
+ return { code: request.code, uploadToken: request.uploadToken, uploadDeps, extractZip };
27859
+ }
27860
+ static classifyGameDeployType(flags2) {
27861
+ if (flags2.hasBackend && flags2.hasFrontend) {
27862
+ return "full_stack";
27863
+ }
27864
+ if (flags2.hasFrontend) {
27865
+ return "frontend_only";
27866
+ }
27867
+ if (flags2.hasBackend) {
27868
+ return "backend_only";
27869
+ }
27870
+ return "metadata_only";
27871
+ }
27872
+ mapGameBindingsToOptions(deploymentId, bindings, schema2) {
27873
+ if (!bindings && !schema2) {
27874
+ return;
27875
+ }
27876
+ const workerBindings = {};
27877
+ if (bindings?.database) {
27878
+ workerBindings.d1 = [deploymentId];
27879
+ }
27880
+ if (bindings?.keyValue) {
27881
+ workerBindings.kv = [deploymentId];
27882
+ }
27883
+ if (bindings?.bucket) {
27884
+ workerBindings.r2 = [deploymentId];
27885
+ }
27886
+ if (bindings?.queues) {
27887
+ let toQueueName = function(queueKey) {
27888
+ return `${QUEUE_NAME_PREFIX}-${deploymentId}--${queueKey}`;
27889
+ };
27890
+ const queueNameByKey = new Map;
27891
+ for (const queueKey of Object.keys(bindings.queues)) {
27892
+ queueNameByKey.set(queueKey, toQueueName(queueKey));
27893
+ }
27894
+ workerBindings.queues = Object.entries(bindings.queues).map(([queueKey, queueConfig]) => {
27895
+ const config2 = queueConfig === true ? undefined : queueConfig;
27896
+ const deadLetterQueue = config2?.deadLetterQueue && queueNameByKey.get(config2.deadLetterQueue) ? queueNameByKey.get(config2.deadLetterQueue) : undefined;
27897
+ return {
27898
+ bindingName: toBindingName(queueKey),
27899
+ queueName: toQueueName(queueKey),
27900
+ settings: config2 ? {
27901
+ maxBatchSize: config2.maxBatchSize,
27902
+ maxRetries: config2.maxRetries,
27903
+ maxBatchTimeout: config2.maxBatchTimeout,
27904
+ maxConcurrency: config2.maxConcurrency,
27905
+ retryDelay: config2.retryDelay
27906
+ } : undefined,
27907
+ deadLetterQueue
27908
+ };
27909
+ });
27910
+ }
27911
+ const hasBindings = workerBindings.d1?.length || workerBindings.kv?.length || workerBindings.r2?.length || workerBindings.queues?.length;
27912
+ return {
27913
+ ...hasBindings && { bindings: workerBindings },
27914
+ ...schema2 && { schema: schema2 }
27915
+ };
27916
+ }
27917
+ static countDeadLetterQueues(bindings) {
27918
+ return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
27301
27919
  }
27302
27920
  async cleanupOrphanD1Databases(cf, d1Names, slug, gameId) {
27303
27921
  const hasAnyPriorDeployment = await this.deps.db.query.gameDeployments.findFirst({
27304
- where: eq(gameDeployments.gameId, gameId),
27922
+ where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.target, "game")),
27305
27923
  columns: { id: true }
27306
27924
  });
27307
27925
  if (hasAnyPriorDeployment) {
@@ -27336,33 +27954,7 @@ class DeployService {
27336
27954
  }
27337
27955
  await this.deps.db.update(games).set(updates).where(eq(games.id, gameId));
27338
27956
  }
27339
- validateDeployRequest(request) {
27340
- const hasBackend = Boolean(request.code);
27341
- const hasFrontend = Boolean(request.uploadToken);
27342
- const hasMetadata = Boolean(request.metadata);
27343
- setAttributes({
27344
- "app.deploy.has_backend": hasBackend,
27345
- "app.deploy.has_frontend": hasFrontend,
27346
- "app.deploy.has_metadata": hasMetadata,
27347
- "app.deploy.type": DeployService.classifyDeployType({
27348
- hasBackend,
27349
- hasFrontend,
27350
- hasMetadata
27351
- }),
27352
- "app.deploy.code_size": request.code?.length ?? 0,
27353
- "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27354
- "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
27355
- "app.deploy.has_schema": Boolean(request.schema)
27356
- });
27357
- if (!hasBackend && !hasFrontend && !hasMetadata) {
27358
- throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
27359
- }
27360
- if (!this.deps.config.baseUrl) {
27361
- throw new ValidationError("baseUrl is not configured");
27362
- }
27363
- return { hasBackend, hasFrontend, hasMetadata };
27364
- }
27365
- async finalizeDeployment({
27957
+ async finalizeGameDeployment({
27366
27958
  game: game2,
27367
27959
  slug,
27368
27960
  deploymentId,
@@ -27374,10 +27966,17 @@ class DeployService {
27374
27966
  const { hasBackend, hasFrontend, hasMetadata } = flags2;
27375
27967
  const db2 = this.deps.db;
27376
27968
  const codeHash = hasBackend ? await generateDeploymentHash(request.code) : null;
27377
- await this.saveDeployment(game2.id, result.deploymentId, result.url, codeHash, result.resources);
27969
+ await this.saveDeployment({
27970
+ gameId: game2.id,
27971
+ deploymentId: result.deploymentId,
27972
+ url: result.url,
27973
+ codeHash,
27974
+ resources: result.resources,
27975
+ target: "game"
27976
+ });
27378
27977
  if (hasBackend) {
27379
27978
  await withSpan("deploy.configure_worker_secrets", async () => {
27380
- await this.ensureApiKeyOnWorker(user, slug, result.deploymentId);
27979
+ await this.ensureWorkerApiKeyOnWorker(user, DeployService.gameWorkerKeySpec(slug), result.deploymentId);
27381
27980
  await this.ensureQueueIngressSecretOnWorker(slug, result.deploymentId);
27382
27981
  });
27383
27982
  }
@@ -27410,169 +28009,232 @@ class DeployService {
27410
28009
  }
27411
28010
  return updatedGame;
27412
28011
  }
27413
- async* deploy(slug, request, user, uploadDeps, extractZip) {
27414
- const cf = this.getCloudflare();
27415
- const db2 = this.deps.db;
27416
- const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
27417
- const flags2 = this.validateDeployRequest(request);
27418
- const { hasBackend, hasFrontend } = flags2;
27419
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
27420
- let frontendAssetsPath;
27421
- let tempDir;
27422
- if (hasFrontend) {
27423
- if (!uploadDeps || !extractZip) {
27424
- throw new ValidationError("Upload dependencies not configured for frontend deployment");
27425
- }
27426
- yield { type: "status", data: { message: "Fetching temporary files" } };
27427
- const extracted = await this.fetchAndExtractFrontendAssets(slug, game2.id, request.uploadToken, uploadDeps, extractZip);
27428
- tempDir = extracted.tempDir;
27429
- frontendAssetsPath = extracted.assetsPath;
27430
- yield { type: "status", data: { message: "Extracting assets" } };
27431
- }
27432
- let platformBaseUrl = this.deps.config.baseUrl;
27433
- if (this.deps.config.isLocal) {
27434
- try {
27435
- platformBaseUrl = await getTunnelUrl();
27436
- } catch {
27437
- setAttributes({
27438
- "app.deploy.tunnel_available": false,
27439
- "app.deploy.failure_kind": "local_tunnel_unavailable"
27440
- });
27441
- throw new ValidationError("Local tunnel is not running. Ensure cloudflared is installed (`brew install cloudflared`) and the tunnel DevCommand started successfully.");
27442
- }
27443
- }
27444
- const env = {
27445
- GAME_ID: game2.id,
27446
- PLAYCADEMY_BASE_URL: platformBaseUrl,
27447
- PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
28012
+ static gameWorkerKeySpec(slug) {
28013
+ return {
28014
+ keyName: getGameWorkerApiKeyName(slug),
28015
+ permissions: { games: [`read:${slug}`, `write:${slug}`] },
28016
+ lookup: "owner-and-name"
27448
28017
  };
27449
- yield {
27450
- type: "status",
27451
- data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
28018
+ }
28019
+ static dashboardWorkerKeySpec(slug) {
28020
+ return {
28021
+ keyName: getDashboardWorkerApiKeyName(slug),
28022
+ permissions: {
28023
+ games: [`read:${slug}`],
28024
+ [DASHBOARD_PERMISSION_NAMESPACE]: [dashboardCredentialScope(slug)]
28025
+ },
28026
+ rateLimit: { maxRequests: 300, timeWindowMs: 60000 },
28027
+ lookup: "name",
28028
+ isCurrent: (permissionsJson) => DeployService.hasDashboardCredentialScope(permissionsJson, slug)
27452
28029
  };
27453
- const keepAssets = hasBackend && !hasFrontend;
27454
- const deploymentOptions = this.mapBindingsToOptions(deploymentId, request.bindings, request.schema);
27455
- const bindings = deploymentOptions?.bindings;
27456
- setAttributes({
27457
- "app.deploy.has_d1": Boolean(bindings?.d1?.length),
27458
- "app.deploy.has_kv": Boolean(bindings?.kv?.length),
27459
- "app.deploy.has_r2": Boolean(bindings?.r2?.length),
27460
- "app.deploy.queue_count": bindings?.queues?.length ?? 0,
27461
- "app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
27462
- });
27463
- const activeDeployment = await db2.query.gameDeployments.findFirst({
27464
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
27465
- columns: { resources: true }
27466
- });
27467
- if (deploymentOptions?.bindings?.d1?.length) {
27468
- await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
28030
+ }
28031
+ static hasDashboardCredentialScope(permissionsJson, slug) {
28032
+ if (!permissionsJson) {
28033
+ return false;
27469
28034
  }
27470
- let result;
27471
28035
  try {
27472
- result = await withSpan("deploy.cloudflare", () => cf.deploy(deploymentId, request.code, env, {
27473
- ...deploymentOptions,
27474
- compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27475
- compatibilityFlags: request.compatibilityFlags,
27476
- existingResources: activeDeployment?.resources ?? undefined,
27477
- assetsPath: frontendAssetsPath,
27478
- keepAssets
27479
- }));
27480
- } finally {
27481
- if (tempDir) {
27482
- const fs2 = await import("fs/promises");
27483
- fs2.rm(tempDir, { recursive: true, force: true }).catch(catchAttrs("deploy.temp_cleanup"));
28036
+ const parsed = JSON.parse(permissionsJson);
28037
+ return dashboardCredentialSlugs(parsed).includes(slug);
28038
+ } catch {
28039
+ return false;
28040
+ }
28041
+ }
28042
+ async ensureWorkerApiKeyOnWorker(user, spec, deploymentId, existingSecrets) {
28043
+ const cf = this.getCloudflare();
28044
+ const existingKey = await this.deps.db.query.apikey.findFirst({
28045
+ where: spec.lookup === "owner-and-name" ? and(eq(apikey.userId, user.id), eq(apikey.name, spec.keyName)) : eq(apikey.name, spec.keyName),
28046
+ columns: { id: true, permissions: true }
28047
+ });
28048
+ let apiKey;
28049
+ if (!existingKey) {
28050
+ apiKey = await this.createApiKey(user, spec);
28051
+ } else {
28052
+ const current = spec.isCurrent?.(existingKey.permissions) ?? true;
28053
+ const workerHasKey = existingSecrets ? existingSecrets.includes("PLAYCADEMY_API_KEY") : await this.workerHasSecret(deploymentId, "PLAYCADEMY_API_KEY");
28054
+ if (current && workerHasKey) {
28055
+ setAttribute("app.deploy.api_key_outcome", "already_set");
28056
+ return;
27484
28057
  }
28058
+ apiKey = await this.regenerateApiKey(user, existingKey.id, spec);
27485
28059
  }
27486
- yield { type: "status", data: { message: "Finalizing deployment" } };
27487
- const updatedGame = await this.finalizeDeployment({
27488
- game: game2,
27489
- slug,
27490
- deploymentId,
27491
- result,
27492
- request,
27493
- user,
27494
- flags: flags2
28060
+ await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
28061
+ }
28062
+ async createApiKey(user, spec) {
28063
+ const { key: apiKey } = await this.deps.createAuthApiKey({
28064
+ userId: user.id,
28065
+ name: spec.keyName,
28066
+ expiresIn: null,
28067
+ permissions: spec.permissions,
28068
+ ...spec.rateLimit ? {
28069
+ rateLimitEnabled: true,
28070
+ rateLimitMax: spec.rateLimit.maxRequests,
28071
+ rateLimitTimeWindow: spec.rateLimit.timeWindowMs
28072
+ } : { rateLimitEnabled: false }
27495
28073
  });
27496
- yield { type: "complete", data: updatedGame };
28074
+ setAttribute("app.deploy.api_key_outcome", "created");
28075
+ return apiKey;
27497
28076
  }
27498
- mapBindingsToOptions(deploymentId, bindings, schema2) {
27499
- if (!bindings && !schema2) {
28077
+ async regenerateApiKey(user, existingKeyId, spec) {
28078
+ setAttribute("app.deploy.api_key_outcome", "regenerated");
28079
+ try {
28080
+ await this.deps.deleteAuthApiKey(existingKeyId);
28081
+ } catch (error) {
28082
+ addEvent("deploy.api_key_revoke_failed", {
28083
+ "exception.type": errorType(error),
28084
+ "app.error.message": errorMessage(error),
28085
+ "app.deploy.old_key_id": existingKeyId
28086
+ });
28087
+ }
28088
+ return this.createApiKey(user, spec);
28089
+ }
28090
+ async listWorkerSecretNames(deploymentId) {
28091
+ try {
28092
+ return await this.getCloudflare().listSecrets(deploymentId);
28093
+ } catch (error) {
28094
+ addEvent("deploy.worker_secrets_check_failed", {
28095
+ "exception.type": errorType(error),
28096
+ "app.error.message": errorMessage(error)
28097
+ });
28098
+ return null;
28099
+ }
28100
+ }
28101
+ async workerHasSecret(deploymentId, secretName) {
28102
+ const workerSecrets = await this.listWorkerSecretNames(deploymentId);
28103
+ return Boolean(workerSecrets?.includes(secretName));
28104
+ }
28105
+ async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
28106
+ const secret = this.deps.config.queueIngressSecret;
28107
+ if (!secret) {
27500
28108
  return;
27501
28109
  }
27502
- const workerBindings = {};
27503
- if (bindings?.database) {
27504
- workerBindings.d1 = [deploymentId];
28110
+ const cf = this.getCloudflare();
28111
+ await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
28112
+ }
28113
+ async ensureDashboardSessionSecret(deploymentId, existingSecrets, hasPriorDeployment) {
28114
+ if (existingSecrets === null && hasPriorDeployment) {
28115
+ setAttribute("app.deploy.session_secret_outcome", "check_failed_kept");
28116
+ return;
27505
28117
  }
27506
- if (bindings?.keyValue) {
27507
- workerBindings.kv = [deploymentId];
28118
+ if (existingSecrets?.includes("DASHBOARD_SESSION_SECRET")) {
28119
+ setAttribute("app.deploy.session_secret_outcome", "already_set");
28120
+ return;
27508
28121
  }
27509
- if (bindings?.bucket) {
27510
- workerBindings.r2 = [deploymentId];
28122
+ const secret = await generateSecureRandomString(64);
28123
+ await this.getCloudflare().setSecrets(deploymentId, { DASHBOARD_SESSION_SECRET: secret });
28124
+ setAttribute("app.deploy.session_secret_outcome", "created");
28125
+ }
28126
+ async rotateDashboardSessionSecret(slug, user) {
28127
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
28128
+ const deployment = await this.deps.db.query.gameDeployments.findFirst({
28129
+ where: activeDeploymentWhere(game2.id, "dashboard"),
28130
+ columns: { deploymentId: true }
28131
+ });
28132
+ if (!deployment) {
28133
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28134
+ throw new NotFoundError("Dashboard deployment", slug);
27511
28135
  }
27512
- if (bindings?.queues) {
27513
- let toQueueName = function(queueKey) {
27514
- return `${QUEUE_NAME_PREFIX}-${deploymentId}--${queueKey}`;
27515
- };
27516
- const queueNameByKey = new Map;
27517
- for (const queueKey of Object.keys(bindings.queues)) {
27518
- queueNameByKey.set(queueKey, toQueueName(queueKey));
27519
- }
27520
- workerBindings.queues = Object.entries(bindings.queues).map(([queueKey, queueConfig]) => {
27521
- const config2 = queueConfig === true ? undefined : queueConfig;
27522
- const deadLetterQueue = config2?.deadLetterQueue && queueNameByKey.get(config2.deadLetterQueue) ? queueNameByKey.get(config2.deadLetterQueue) : undefined;
27523
- return {
27524
- bindingName: toBindingName(queueKey),
27525
- queueName: toQueueName(queueKey),
27526
- settings: config2 ? {
27527
- maxBatchSize: config2.maxBatchSize,
27528
- maxRetries: config2.maxRetries,
27529
- maxBatchTimeout: config2.maxBatchTimeout,
27530
- maxConcurrency: config2.maxConcurrency,
27531
- retryDelay: config2.retryDelay
27532
- } : undefined,
27533
- deadLetterQueue
27534
- };
28136
+ const secret = await generateSecureRandomString(64);
28137
+ await this.getCloudflare().setSecrets(deployment.deploymentId, {
28138
+ DASHBOARD_SESSION_SECRET: secret
28139
+ });
28140
+ setAttribute("app.deploy.session_secret_outcome", "rotated");
28141
+ }
28142
+ async removeDashboard(slug, user) {
28143
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
28144
+ const db2 = this.deps.db;
28145
+ const deployment = await db2.query.gameDeployments.findFirst({
28146
+ where: activeDeploymentWhere(game2.id, "dashboard"),
28147
+ columns: { deploymentId: true, provider: true, resources: true }
28148
+ });
28149
+ if (!deployment) {
28150
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28151
+ throw new NotFoundError("Dashboard deployment", slug);
28152
+ }
28153
+ const assetBuckets = deployment.resources?.r2?.filter((bucket) => bucket.kind === "assets") ?? [];
28154
+ await this.getCloudflare().delete(deployment.deploymentId, {
28155
+ deleteBindings: assetBuckets.length > 0,
28156
+ resources: assetBuckets.length > 0 ? { r2: assetBuckets } : undefined
28157
+ });
28158
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28159
+ await db2.delete(gameDeployments).where(and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "dashboard")));
28160
+ setAttribute("app.deploy.dashboard_removed", true);
28161
+ await withSpan("deploy.send_alert", () => this.deps.alerts.notifyDashboardRemoval({
28162
+ slug,
28163
+ displayName: game2.displayName,
28164
+ developer: { id: user.id, email: user.email }
28165
+ })).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "dashboard_removal" }));
28166
+ }
28167
+ async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
28168
+ const frontendZip = await withSpan("deploy.fetch_temporary_files", () => uploadDeps.getObjectAsByteArray(uploadToken));
28169
+ if (!frontendZip || frontendZip.length === 0) {
28170
+ throw new ValidationError("Uploaded file is empty or not found");
28171
+ }
28172
+ setAttribute("app.deploy.asset_upload_size", frontendZip.length);
28173
+ const os2 = await import("os");
28174
+ const path = await import("path");
28175
+ const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
28176
+ const assetsPath = path.join(tempDir, "dist");
28177
+ await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
28178
+ uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
28179
+ return { frontendZip, tempDir, assetsPath };
28180
+ }
28181
+ async resolvePlatformBaseUrl() {
28182
+ if (!this.deps.config.isLocal) {
28183
+ return this.deps.config.baseUrl;
28184
+ }
28185
+ try {
28186
+ return await getTunnelUrl();
28187
+ } catch {
28188
+ setAttributes({
28189
+ "app.deploy.tunnel_available": false,
28190
+ "app.deploy.failure_kind": "local_tunnel_unavailable"
27535
28191
  });
28192
+ throw new ValidationError("Local tunnel is not running. Ensure cloudflared is installed (`brew install cloudflared`) and the tunnel DevCommand started successfully.");
27536
28193
  }
27537
- const hasBindings = workerBindings.d1?.length || workerBindings.kv?.length || workerBindings.r2?.length || workerBindings.queues?.length;
27538
- return {
27539
- ...hasBindings && { bindings: workerBindings },
27540
- ...schema2 && { schema: schema2 }
27541
- };
27542
28194
  }
27543
- async notifyDeploymentFailure(slug, displayName, error, developer) {
27544
- await this.deps.alerts.notifyDeploymentFailure({
27545
- slug,
27546
- displayName,
27547
- error,
27548
- developer
27549
- }).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
28195
+ async deployToCloudflare(args2) {
28196
+ const cf = this.getCloudflare();
28197
+ try {
28198
+ return await withSpan("deploy.cloudflare", () => cf.deploy(args2.deploymentId, args2.code, args2.env, args2.options));
28199
+ } finally {
28200
+ if (args2.tempDir) {
28201
+ const fs2 = await import("fs/promises");
28202
+ fs2.rm(args2.tempDir, { recursive: true, force: true }).catch(catchAttrs("deploy.temp_cleanup"));
28203
+ }
28204
+ }
27550
28205
  }
27551
- async saveDeployment(gameId, deploymentId, url, codeHash, resources) {
28206
+ async saveDeployment(record) {
27552
28207
  const db2 = this.deps.db;
27553
28208
  await db2.transaction(async (tx) => {
27554
- await tx.update(gameDeployments).set({ isActive: false }).where(eq(gameDeployments.gameId, gameId));
28209
+ await tx.update(gameDeployments).set({ isActive: false }).where(and(eq(gameDeployments.gameId, record.gameId), eq(gameDeployments.target, record.target)));
27555
28210
  await tx.insert(gameDeployments).values({
27556
- gameId,
27557
- deploymentId,
28211
+ gameId: record.gameId,
28212
+ deploymentId: record.deploymentId,
27558
28213
  provider: "cloudflare",
27559
- url,
27560
- codeHash,
27561
- resources,
28214
+ target: record.target,
28215
+ url: record.url,
28216
+ codeHash: record.codeHash,
28217
+ resources: record.resources,
27562
28218
  isActive: true
27563
28219
  });
27564
28220
  });
27565
28221
  }
28222
+ async notifyDeploymentFailure(failure) {
28223
+ await this.deps.alerts.notifyDeploymentFailure(failure).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
28224
+ }
27566
28225
  }
27567
28226
  var init_deploy_service = __esm(() => {
27568
28227
  init_drizzle_orm();
27569
28228
  init_playcademy();
27570
28229
  init_src();
28230
+ init_helpers_index();
27571
28231
  init_tables_index();
27572
28232
  init_spans();
27573
28233
  init_tunnel();
27574
28234
  init_errors();
28235
+ init_dashboard_util();
27575
28236
  init_deployment_util();
28237
+ init_worker_keys_util();
27576
28238
  });
27577
28239
 
27578
28240
  // ../api-core/src/services/developer.service.ts
@@ -27817,20 +28479,6 @@ var init_game_member_service = __esm(() => {
27817
28479
  init_spans();
27818
28480
  init_errors();
27819
28481
  });
27820
- // ../data/src/domains/timeback/helpers.ts
27821
- function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
27822
- return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
27823
- }
27824
- var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active", DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
27825
- var init_helpers2 = __esm(() => {
27826
- init_drizzle_orm();
27827
- init_table7();
27828
- });
27829
-
27830
- // ../data/src/helpers.index.ts
27831
- var init_helpers_index = __esm(() => {
27832
- init_helpers2();
27833
- });
27834
28482
 
27835
28483
  // ../utils/src/fns.ts
27836
28484
  function sleep(ms) {
@@ -28274,6 +28922,7 @@ var init_game_service = __esm(() => {
28274
28922
  init_errors();
28275
28923
  init_deployment_util();
28276
28924
  init_timeback_util();
28925
+ init_worker_keys_util();
28277
28926
  inFlightManifestFetches = new Map;
28278
28927
  GameService = class GameService {
28279
28928
  deps;
@@ -28460,7 +29109,7 @@ var init_game_service = __esm(() => {
28460
29109
  GameService.recordGameShape(game2);
28461
29110
  return game2;
28462
29111
  }
28463
- async getBySlug(slug, caller) {
29112
+ async getBySlug(slug, caller, options) {
28464
29113
  const db2 = this.deps.db;
28465
29114
  const game2 = await db2.query.games.findFirst({
28466
29115
  where: eq(games.slug, slug)
@@ -28468,7 +29117,9 @@ var init_game_service = __esm(() => {
28468
29117
  if (!game2) {
28469
29118
  throw new NotFoundError("Game", slug);
28470
29119
  }
28471
- await this.enforceVisibility(game2, caller, slug);
29120
+ if (!options?.scopeAuthorized) {
29121
+ await this.enforceVisibility(game2, caller, slug);
29122
+ }
28472
29123
  GameService.recordGameShape(game2);
28473
29124
  return game2;
28474
29125
  }
@@ -28645,6 +29296,9 @@ var init_game_service = __esm(() => {
28645
29296
  await this.validateDeveloperAccess(user, gameId);
28646
29297
  } else {
28647
29298
  this.validateDeveloperStatus(user);
29299
+ if (slug.endsWith(DASHBOARD_WORKER_SUFFIX)) {
29300
+ throw new ValidationError(`Game slugs may not end with '${DASHBOARD_WORKER_SUFFIX}' — it is reserved for dashboard workers`);
29301
+ }
28648
29302
  }
28649
29303
  const gameDataForDb = {
28650
29304
  displayName: data.displayName,
@@ -28771,15 +29425,21 @@ var init_game_service = __esm(() => {
28771
29425
  if (!gameToDelete?.slug) {
28772
29426
  throw new NotFoundError("Game", gameId);
28773
29427
  }
28774
- const activeDeployment = await db2.query.gameDeployments.findFirst({
28775
- where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.isActive, true)),
28776
- columns: { deploymentId: true, provider: true, resources: true }
28777
- });
28778
- const customHostnames = await db2.select({
28779
- hostname: gameCustomHostnames.hostname,
28780
- cloudflareId: gameCustomHostnames.cloudflareId,
28781
- environment: gameCustomHostnames.environment
28782
- }).from(gameCustomHostnames).where(eq(gameCustomHostnames.gameId, gameId));
29428
+ const [activeDeployment, dashboardDeployment, customHostnames] = await Promise.all([
29429
+ db2.query.gameDeployments.findFirst({
29430
+ where: activeDeploymentWhere(gameId, "game"),
29431
+ columns: { deploymentId: true, provider: true, resources: true }
29432
+ }),
29433
+ db2.query.gameDeployments.findFirst({
29434
+ where: activeDeploymentWhere(gameId, "dashboard"),
29435
+ columns: { deploymentId: true, provider: true, resources: true }
29436
+ }),
29437
+ db2.select({
29438
+ hostname: gameCustomHostnames.hostname,
29439
+ cloudflareId: gameCustomHostnames.cloudflareId,
29440
+ environment: gameCustomHostnames.environment
29441
+ }).from(gameCustomHostnames).where(eq(gameCustomHostnames.gameId, gameId))
29442
+ ]);
28783
29443
  const result = await db2.delete(games).where(eq(games.id, gameId)).returning({ id: games.id });
28784
29444
  if (result.length === 0) {
28785
29445
  throw new NotFoundError("Game", gameId);
@@ -28823,6 +29483,22 @@ var init_game_service = __esm(() => {
28823
29483
  setAttribute("app.game.api_key_cleanup_error", errorMessage(error));
28824
29484
  }
28825
29485
  }
29486
+ if (dashboardDeployment?.provider === "cloudflare" && this.deps.cloudflare) {
29487
+ const dashboardAssetBuckets = dashboardDeployment.resources?.r2?.filter((bucket) => bucket.kind === "assets") ?? [];
29488
+ try {
29489
+ await this.deps.cloudflare.delete(dashboardDeployment.deploymentId, {
29490
+ deleteBindings: dashboardAssetBuckets.length > 0,
29491
+ resources: dashboardAssetBuckets.length > 0 ? { r2: dashboardAssetBuckets } : undefined
29492
+ });
29493
+ setAttribute("app.game.dashboard_cleanup", "succeeded");
29494
+ } catch (error) {
29495
+ setAttributes({
29496
+ "app.game.dashboard_cleanup": "failed",
29497
+ "app.game.dashboard_cleanup_error": errorMessage(error)
29498
+ });
29499
+ }
29500
+ }
29501
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, gameToDelete.slug);
28826
29502
  const corsOrigin = GameService.safeOrigin(gameToDelete);
28827
29503
  if (this.deps.corsKvs && corsOrigin) {
28828
29504
  setAttribute("app.cors_kvs.origin", corsOrigin);
@@ -28946,7 +29622,7 @@ class LogsService {
28946
29622
  }
28947
29623
  async generateToken(user, slug, sstStage) {
28948
29624
  await this.deps.validateDeveloperAccessBySlug(user, slug);
28949
- const workerId = getDeploymentId(slug, sstStage);
29625
+ const workerId = getGameDeploymentId(slug, sstStage);
28950
29626
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
28951
29627
  setAttribute("app.logs.worker_id", workerId);
28952
29628
  return { token, workerId };
@@ -28966,7 +29642,8 @@ function createGameServices(deps) {
28966
29642
  cache,
28967
29643
  cloudflare: cloudflare2,
28968
29644
  corsKvs,
28969
- deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2)
29645
+ deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2),
29646
+ deleteApiKeysByName: auth2.deleteApiKeysByName.bind(auth2)
28970
29647
  });
28971
29648
  const gameMember = new GameMemberService({
28972
29649
  db: db2,
@@ -28979,6 +29656,7 @@ function createGameServices(deps) {
28979
29656
  cloudflare: cloudflare2,
28980
29657
  createAuthApiKey: auth2.createApiKey.bind(auth2),
28981
29658
  deleteAuthApiKey: auth2.deleteApiKey.bind(auth2),
29659
+ deleteApiKeysByName: auth2.deleteApiKeysByName.bind(auth2),
28982
29660
  alerts,
28983
29661
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
28984
29662
  });
@@ -28988,14 +29666,20 @@ function createGameServices(deps) {
28988
29666
  storage,
28989
29667
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
28990
29668
  runDeploy: (slug, request, user, uploadDeps, extractZip) => deploy.deploy(slug, request, user, uploadDeps, extractZip),
28991
- notifyDeploymentFailure: (slug, displayName, error, developerInfo) => deploy.notifyDeploymentFailure(slug, displayName, error, developerInfo)
29669
+ notifyDeploymentFailure: (failure) => deploy.notifyDeploymentFailure(failure)
28992
29670
  });
28993
29671
  const logs = new LogsService({
28994
29672
  mintLogStreamToken: auth2.mintLogStreamToken.bind(auth2),
28995
29673
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
28996
29674
  });
29675
+ const dashboard2 = new DashboardService({
29676
+ db: db2,
29677
+ hashPassword: auth2.hashPassword.bind(auth2),
29678
+ verifyPassword: auth2.verifyPassword.bind(auth2),
29679
+ validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
29680
+ });
28997
29681
  return {
28998
- services: { game: game2, gameMember, developer, deploy, deployJobs, logs },
29682
+ services: { game: game2, gameMember, developer, deploy, deployJobs, logs, dashboard: dashboard2 },
28999
29683
  validators: {
29000
29684
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
29001
29685
  validateDeveloperAccess: (user, gameId) => game2.validateDeveloperAccess(user, gameId),
@@ -29005,6 +29689,7 @@ function createGameServices(deps) {
29005
29689
  };
29006
29690
  }
29007
29691
  var init_game2 = __esm(() => {
29692
+ init_dashboard_service();
29008
29693
  init_deploy_job_service();
29009
29694
  init_deploy_service();
29010
29695
  init_developer_service();
@@ -29189,12 +29874,25 @@ class AlertsService {
29189
29874
  embed.setFooter("Playcademy Developer Platform").setTimestamp();
29190
29875
  await this.sendAlert(discord, embed.build());
29191
29876
  }
29877
+ async notifyDashboardDeployment(dashboard2) {
29878
+ const discord = this.recordAlert("dashboard_deployment");
29879
+ if (!discord) {
29880
+ return;
29881
+ }
29882
+ const embed = new DiscordEmbedBuilder().setTitle("\uD83D\uDCCA Dashboard Deployed").setDescription(`The dashboard for **${dashboard2.displayName || dashboard2.slug}** has been deployed (**${this.getEnvironment()}**).`).setColor(DiscordColors.GREEN).addField("Slug", dashboard2.slug, true).addField("URL", dashboard2.url, false);
29883
+ if (dashboard2.developer) {
29884
+ embed.addField("Developer", dashboard2.developer.email || dashboard2.developer.id, true);
29885
+ }
29886
+ embed.setFooter("Playcademy Developer Platform").setTimestamp();
29887
+ await this.sendAlert(discord, embed.build());
29888
+ }
29192
29889
  async notifyDeploymentFailure(failure) {
29193
29890
  const discord = this.recordAlert("deployment_failure");
29194
29891
  if (!discord) {
29195
29892
  return;
29196
29893
  }
29197
- const embed = new DiscordEmbedBuilder().setTitle("❌ Deployment Failed").setDescription(`Deployment failed for **${failure.displayName || failure.slug}** (**${this.getEnvironment()}**).`).setColor(DiscordColors.RED).addField("Slug", failure.slug, true);
29894
+ const [title, subject] = failure.target === "dashboard" ? ["Dashboard Deployment Failed", "Dashboard deployment"] : ["❌ Deployment Failed", "Deployment"];
29895
+ const embed = new DiscordEmbedBuilder().setTitle(title).setDescription(`${subject} failed for **${failure.displayName || failure.slug}** (**${this.getEnvironment()}**).`).setColor(DiscordColors.RED).addField("Slug", failure.slug, true);
29198
29896
  if (failure.developer) {
29199
29897
  embed.addField("Developer", failure.developer.email || failure.developer.id, true);
29200
29898
  }
@@ -29209,6 +29907,14 @@ class AlertsService {
29209
29907
  const embed = new DiscordEmbedBuilder().setTitle("\uD83D\uDDD1️ Game Deleted").setDescription(`**${game2.displayName || game2.slug}** has been deleted (**${this.getEnvironment()}**).`).setColor(DiscordColors.ORANGE).addField("Slug", game2.slug, true).addField("Developer", game2.developer.email || game2.developer.id, true).setFooter("Playcademy Developer Platform").setTimestamp().build();
29210
29908
  await this.sendAlert(discord, embed);
29211
29909
  }
29910
+ async notifyDashboardRemoval(dashboard2) {
29911
+ const discord = this.recordAlert("dashboard_removal");
29912
+ if (!discord) {
29913
+ return;
29914
+ }
29915
+ const embed = new DiscordEmbedBuilder().setTitle("\uD83D\uDDD1️ Dashboard Removed").setDescription(`The dashboard for **${dashboard2.displayName || dashboard2.slug}** has been removed (**${this.getEnvironment()}**).`).setColor(DiscordColors.ORANGE).addField("Slug", dashboard2.slug, true).addField("Developer", dashboard2.developer.email || dashboard2.developer.id, true).setFooter("Playcademy Developer Platform").setTimestamp().build();
29916
+ await this.sendAlert(discord, embed);
29917
+ }
29212
29918
  async notifySeedFailure(failure) {
29213
29919
  const discord = this.recordAlert("seed_failure");
29214
29920
  if (!discord) {
@@ -29374,6 +30080,7 @@ class KVBackupService {
29374
30080
  async getTargets(gameSlug) {
29375
30081
  const conditions2 = [
29376
30082
  eq(gameDeployments.isActive, true),
30083
+ eq(gameDeployments.target, "game"),
29377
30084
  eq(gameDeployments.provider, "cloudflare")
29378
30085
  ];
29379
30086
  if (gameSlug) {
@@ -29600,7 +30307,7 @@ class BucketService {
29600
30307
  return extensionIndex > 0 ? fileName.slice(extensionIndex + 1).toLowerCase() : "(none)";
29601
30308
  }
29602
30309
  getBucketName(slug) {
29603
- return getDeploymentId(slug, this.deps.sstStage);
30310
+ return getGameDeploymentId(slug, this.deps.sstStage);
29604
30311
  }
29605
30312
  async listFiles(slug, user, prefix) {
29606
30313
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
@@ -29689,6 +30396,32 @@ class DatabaseService {
29689
30396
  }
29690
30397
  return d1;
29691
30398
  }
30399
+ async syncDashboardD1Binding(gameId, d1ResourceName, databaseId) {
30400
+ const dashboardDeployment = await this.deps.db.query.gameDeployments.findFirst({
30401
+ where: activeDeploymentWhere(gameId, "dashboard"),
30402
+ columns: { id: true, deploymentId: true, resources: true }
30403
+ });
30404
+ if (!dashboardDeployment || !this.deps.cloudflare) {
30405
+ return;
30406
+ }
30407
+ try {
30408
+ await this.deps.cloudflare.updateD1Binding(dashboardDeployment.deploymentId, databaseId);
30409
+ if (dashboardDeployment.resources?.d1?.length) {
30410
+ const updatedResources = {
30411
+ ...dashboardDeployment.resources,
30412
+ d1: dashboardDeployment.resources.d1.map((dbResource) => dbResource.name === d1ResourceName ? { ...dbResource, id: databaseId } : dbResource)
30413
+ };
30414
+ await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, dashboardDeployment.id));
30415
+ }
30416
+ setAttribute("app.database.dashboard_binding", "updated");
30417
+ } catch (error) {
30418
+ addEvent("database.dashboard_binding_sync_failed", {
30419
+ "exception.type": errorType(error),
30420
+ "app.error.message": errorMessage(error),
30421
+ "app.deploy.deployment_id": dashboardDeployment.deploymentId
30422
+ });
30423
+ }
30424
+ }
29692
30425
  async reset(slug, user, schema2) {
29693
30426
  setAttributes({
29694
30427
  "app.database.operation": "reset",
@@ -29697,7 +30430,7 @@ class DatabaseService {
29697
30430
  });
29698
30431
  const d1 = this.getD1();
29699
30432
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
29700
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
30433
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
29701
30434
  try {
29702
30435
  const databaseId = await d1.reset(deploymentId);
29703
30436
  setAttribute("app.database.id", databaseId);
@@ -29713,7 +30446,7 @@ class DatabaseService {
29713
30446
  setAttribute("app.database.binding", "skipped_no_provider");
29714
30447
  }
29715
30448
  const activeDeployment = await this.deps.db.query.gameDeployments.findFirst({
29716
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
30449
+ where: activeDeploymentWhere(game2.id),
29717
30450
  columns: { id: true, resources: true }
29718
30451
  });
29719
30452
  setAttribute("app.database.active_deployment_found", Boolean(activeDeployment));
@@ -29724,6 +30457,7 @@ class DatabaseService {
29724
30457
  };
29725
30458
  await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, activeDeployment.id));
29726
30459
  }
30460
+ await this.syncDashboardD1Binding(game2.id, deploymentId, databaseId);
29727
30461
  setAttributes({
29728
30462
  "app.database.deployment_id": deploymentId,
29729
30463
  "app.database.schema_pushed": schemaPushed
@@ -29747,6 +30481,7 @@ class DatabaseService {
29747
30481
  }
29748
30482
  var init_database_service = __esm(() => {
29749
30483
  init_drizzle_orm();
30484
+ init_helpers_index();
29750
30485
  init_tables_index();
29751
30486
  init_spans();
29752
30487
  init_errors();
@@ -29990,7 +30725,7 @@ class KVService {
29990
30725
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
29991
30726
  const db2 = this.deps.db;
29992
30727
  const deployment = await db2.query.gameDeployments.findFirst({
29993
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
30728
+ where: activeDeploymentWhere(game2.id),
29994
30729
  columns: { resources: true }
29995
30730
  });
29996
30731
  if (!deployment?.resources?.kv?.length) {
@@ -30111,8 +30846,7 @@ class KVService {
30111
30846
  }
30112
30847
  }
30113
30848
  var init_kv_service = __esm(() => {
30114
- init_drizzle_orm();
30115
- init_tables_index();
30849
+ init_helpers_index();
30116
30850
  init_spans();
30117
30851
  init_errors();
30118
30852
  });
@@ -30129,13 +30863,13 @@ class SecretsService {
30129
30863
  }
30130
30864
  return this.deps.cloudflare;
30131
30865
  }
30132
- getDeploymentId(slug) {
30133
- return getDeploymentId(slug, this.deps.config.sstStage);
30866
+ getGameDeploymentId(slug) {
30867
+ return getGameDeploymentId(slug, this.deps.config.sstStage);
30134
30868
  }
30135
30869
  async listKeys(slug, user) {
30136
30870
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30137
30871
  const cf = this.getCloudflare();
30138
- const deploymentId = this.getDeploymentId(slug);
30872
+ const deploymentId = this.getGameDeploymentId(slug);
30139
30873
  try {
30140
30874
  const allKeys = await cf.listSecrets(deploymentId);
30141
30875
  const userKeys = allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
@@ -30161,7 +30895,7 @@ class SecretsService {
30161
30895
  async setSecrets(slug, newSecrets, user) {
30162
30896
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30163
30897
  const cf = this.getCloudflare();
30164
- const deploymentId = this.getDeploymentId(slug);
30898
+ const deploymentId = this.getGameDeploymentId(slug);
30165
30899
  const secretKeys = Object.keys(newSecrets);
30166
30900
  if (secretKeys.length === 0) {
30167
30901
  throw new ValidationError("At least one secret must be provided");
@@ -30214,7 +30948,7 @@ class SecretsService {
30214
30948
  }
30215
30949
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30216
30950
  const cf = this.getCloudflare();
30217
- const deploymentId = this.getDeploymentId(slug);
30951
+ const deploymentId = this.getGameDeploymentId(slug);
30218
30952
  try {
30219
30953
  const prefixedKey = `${SECRETS_PREFIX}${key}`;
30220
30954
  const existingKeys = await cf.listSecrets(deploymentId);
@@ -30251,27 +30985,7 @@ var init_secrets_service = __esm(() => {
30251
30985
  init_deployment_util();
30252
30986
  INTERNAL_SECRET_KEYS = ["PLAYCADEMY_API_KEY", "GAME_ID", "PLAYCADEMY_BASE_URL"];
30253
30987
  });
30254
-
30255
- // ../edge-play/src/constants.ts
30256
- var ASSET_ROUTE_PREFIX = "/api/assets/", ROUTES;
30257
- var init_constants3 = __esm(() => {
30258
- init_src();
30259
- ROUTES = {
30260
- INDEX: "/api",
30261
- HEALTH: "/api/health",
30262
- ASSETS: `${ASSET_ROUTE_PREFIX}*`,
30263
- TIMEBACK: {
30264
- END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
30265
- GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
30266
- GET_MASTERY: `/api${TIMEBACK_ROUTES.GET_MASTERY}`,
30267
- GET_HIGHEST_GRADE_MASTERED: `/api${TIMEBACK_ROUTES.GET_HIGHEST_GRADE_MASTERED}`,
30268
- HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
30269
- ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`,
30270
- UNENROLL_COURSE: `/api${TIMEBACK_ROUTES.UNENROLL_COURSE}`
30271
- }
30272
- };
30273
- });
30274
- // ../edge-play/src/entry/setup.ts
30988
+ // ../edge-play/src/game/setup.ts
30275
30989
  function prefixSecrets(secrets) {
30276
30990
  const prefixed = {};
30277
30991
  for (const [key, value] of Object.entries(secrets)) {
@@ -30281,7 +30995,6 @@ function prefixSecrets(secrets) {
30281
30995
  }
30282
30996
  var init_setup2 = __esm(() => {
30283
30997
  init_src();
30284
- init_constants3();
30285
30998
  });
30286
30999
 
30287
31000
  // ../api-core/src/services/seed.service.ts
@@ -30316,7 +31029,7 @@ class SeedService {
30316
31029
  async seed(slug, code, user, secrets) {
30317
31030
  const cf = this.getCloudflare();
30318
31031
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
30319
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
31032
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
30320
31033
  const uniqueSuffix = Date.now().toString(36);
30321
31034
  const seedDeploymentId = `seed-${deploymentId}-${uniqueSuffix}`;
30322
31035
  setAttributes({
@@ -30807,7 +31520,7 @@ var init_emoji = __esm(() => {
30807
31520
  });
30808
31521
 
30809
31522
  // ../data/src/domains/game/schemas.ts
30810
- var HttpUrlSchema, GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
31523
+ var HttpUrlSchema, GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, AddGameDashboardUserSchema, VerifyGameDashboardLoginSchema, AcceptGameDashboardInviteSchema, AcceptGameDashboardResetSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
30811
31524
  var init_schemas2 = __esm(() => {
30812
31525
  init_drizzle_zod();
30813
31526
  init_esm();
@@ -30918,6 +31631,20 @@ var init_schemas2 = __esm(() => {
30918
31631
  UpdateGameMemberRoleSchema = exports_external.object({
30919
31632
  role: exports_external.enum(gameMemberRoleEnum.enumValues)
30920
31633
  }).strict();
31634
+ AddGameDashboardUserSchema = exports_external.object({
31635
+ email: exports_external.string().email().max(255),
31636
+ displayName: exports_external.string().min(1).max(255).optional(),
31637
+ role: exports_external.enum(gameDashboardUserRoleEnum.enumValues).optional().default(DASHBOARD_USER_ROLES.VIEWER)
31638
+ }).strict();
31639
+ VerifyGameDashboardLoginSchema = exports_external.object({
31640
+ email: exports_external.string().email().max(255),
31641
+ password: exports_external.string().min(1).max(1024)
31642
+ }).strict();
31643
+ AcceptGameDashboardInviteSchema = exports_external.object({
31644
+ token: exports_external.string().min(1).max(255),
31645
+ password: exports_external.string().min(8, "Password must be at least 8 characters").max(1024, "Password must be at most 1024 characters")
31646
+ }).strict();
31647
+ AcceptGameDashboardResetSchema = AcceptGameDashboardInviteSchema;
30921
31648
  ALLOWED_UPLOAD_EXTENSIONS = [".zip", ".js"];
30922
31649
  InitiateUploadSchema = exports_external.object({
30923
31650
  fileName: exports_external.string().min(1).refine((name2) => ALLOWED_UPLOAD_EXTENSIONS.some((ext) => name2.endsWith(ext)), {
@@ -30952,6 +31679,7 @@ var init_schemas2 = __esm(() => {
30952
31679
  }))
30953
31680
  });
30954
31681
  DeployRequestSchema = exports_external.object({
31682
+ target: exports_external.enum(deploymentTargetEnum.enumValues).optional().default("game"),
30955
31683
  uploadToken: exports_external.string().optional(),
30956
31684
  code: exports_external.string().optional(),
30957
31685
  codeUploadToken: exports_external.string().optional(),
@@ -30987,6 +31715,9 @@ var init_schemas2 = __esm(() => {
30987
31715
  }).refine((data) => !(data.code && data.codeUploadToken), {
30988
31716
  message: "Specify either code or codeUploadToken, not both",
30989
31717
  path: ["codeUploadToken"]
31718
+ }).refine((data) => !(data.target === "dashboard" && (data.schema || data.bindings)), {
31719
+ message: "Dashboard deployments cannot include schema or bindings — they attach to the game deployment’s existing resources",
31720
+ path: ["target"]
30990
31721
  });
30991
31722
  });
30992
31723
 
@@ -74418,6 +75149,17 @@ function isValidUUID(value) {
74418
75149
  }
74419
75150
  return UUID_REGEX.test(value);
74420
75151
  }
75152
+ async function deterministicUUID(input) {
75153
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
75154
+ const bytes = new Uint8Array(digest).slice(0, 16);
75155
+ bytes[6] = bytes[6] & 15 | 80;
75156
+ bytes[8] = bytes[8] & 63 | 128;
75157
+ const hex3 = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
75158
+ return `${hex3.slice(0, 8)}-${hex3.slice(8, 12)}-${hex3.slice(12, 16)}-${hex3.slice(16, 20)}-${hex3.slice(20)}`;
75159
+ }
75160
+ async function localGameId(slug) {
75161
+ return deterministicUUID(`playcademy:local-game:${slug}`);
75162
+ }
74421
75163
  var UUID_REGEX;
74422
75164
  var init_uuid2 = __esm(() => {
74423
75165
  UUID_REGEX = /^[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}$/;
@@ -76819,7 +77561,7 @@ function deriveTimebackCourseLevelFromGrade(grade) {
76819
77561
  return TIMEBACK_COURSE_DEFAULTS.level.high;
76820
77562
  }
76821
77563
  var ONEROSTER_STATUS2, SCORE_STATUS2, CACHE_DEFAULTS3, RESOURCE_DEFAULTS3;
76822
- var init_constants4 = __esm(() => {
77564
+ var init_constants3 = __esm(() => {
76823
77565
  init_src();
76824
77566
  ONEROSTER_STATUS2 = {
76825
77567
  active: "active",
@@ -77793,7 +78535,7 @@ function qtiIdCandidates(parentLineItem, resource) {
77793
78535
  }
77794
78536
  var GRADE_LEVEL_TEST_TYPES, naturalTitleSort;
77795
78537
  var init_timeback_grade_level_results_util = __esm(() => {
77796
- init_constants4();
78538
+ init_constants3();
77797
78539
  GRADE_LEVEL_TEST_TYPES = [
77798
78540
  "placement",
77799
78541
  "test out",
@@ -77988,7 +78730,7 @@ async function upsertMasteryCompletionEntry(params) {
77988
78730
  }
77989
78731
  var init_timeback_mastery_completion_util = __esm(async () => {
77990
78732
  init_spans();
77991
- init_constants4();
78733
+ init_constants3();
77992
78734
  init_utils6();
77993
78735
  await init_errors8();
77994
78736
  });
@@ -78684,7 +79426,7 @@ class TimebackAdminService {
78684
79426
  columns: { slug: true }
78685
79427
  }),
78686
79428
  this.deps.db.query.gameDeployments.findFirst({
78687
- where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.isActive, true)),
79429
+ where: activeDeploymentWhere(gameId),
78688
79430
  columns: { url: true }
78689
79431
  })
78690
79432
  ]);
@@ -79765,7 +80507,7 @@ var init_timeback_admin_service = __esm(async () => {
79765
80507
  init_schemas_index();
79766
80508
  init_tables_index();
79767
80509
  init_spans();
79768
- init_constants4();
80510
+ init_constants3();
79769
80511
  init_types2();
79770
80512
  init_utils6();
79771
80513
  init_src4();
@@ -80240,7 +80982,7 @@ function timebackConfigMatchesCreateIntegrationRequest(config4, request) {
80240
80982
  return subject === request.subject && grade === request.grade && config4.course.title === request.title && config4.course.courseCode === request.courseCode && config4.course.level === requestedLevel && (getTotalXpFromTimebackConfig(config4) ?? null) === request.totalXp && (getMasterableUnitsFromTimebackConfig(config4) ?? null) === request.masterableUnits;
80241
80983
  }
80242
80984
  var init_timeback_create_integration_util = __esm(() => {
80243
- init_constants4();
80985
+ init_constants3();
80244
80986
  init_types2();
80245
80987
  init_timeback_util();
80246
80988
  });
@@ -83086,6 +83828,19 @@ function createSandboxAuthProvider() {
83086
83828
  }
83087
83829
  return null;
83088
83830
  },
83831
+ async deleteApiKeysByName(name3) {
83832
+ const deleted = [];
83833
+ for (const [id, key] of sandboxApiKeys.entries()) {
83834
+ if (key.name === name3) {
83835
+ sandboxApiKeys.delete(id);
83836
+ deleted.push(id);
83837
+ }
83838
+ }
83839
+ if (deleted.length > 0) {
83840
+ log.debug("[SandboxAuthProvider] Deleted API keys by name", { name: name3, deleted });
83841
+ }
83842
+ return deleted;
83843
+ },
83089
83844
  async mintGameToken(gameId, userId) {
83090
83845
  const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
83091
83846
  const exp = Date.now() + 15 * 60 * 1000;
@@ -83164,6 +83919,12 @@ function createSandboxAuthProvider() {
83164
83919
  } catch {
83165
83920
  return null;
83166
83921
  }
83922
+ },
83923
+ async hashPassword(password) {
83924
+ return `sandbox:${btoa(password)}`;
83925
+ },
83926
+ async verifyPassword(data) {
83927
+ return data.hash === `sandbox:${btoa(data.password)}`;
83167
83928
  }
83168
83929
  };
83169
83930
  }
@@ -83448,7 +84209,7 @@ var init_http_exception = () => {};
83448
84209
 
83449
84210
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/request/constants.js
83450
84211
  var GET_MATCH_RESULT;
83451
- var init_constants5 = __esm(() => {
84212
+ var init_constants4 = __esm(() => {
83452
84213
  GET_MATCH_RESULT = Symbol();
83453
84214
  });
83454
84215
 
@@ -83712,7 +84473,7 @@ var init_url = __esm(() => {
83712
84473
  var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_), HonoRequest;
83713
84474
  var init_request = __esm(() => {
83714
84475
  init_http_exception();
83715
- init_constants5();
84476
+ init_constants4();
83716
84477
  init_body();
83717
84478
  init_url();
83718
84479
  HonoRequest = class {
@@ -84042,7 +84803,7 @@ var init_router = __esm(() => {
84042
84803
 
84043
84804
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/utils/constants.js
84044
84805
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
84045
- var init_constants6 = () => {};
84806
+ var init_constants5 = () => {};
84046
84807
 
84047
84808
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/hono-base.js
84048
84809
  var notFoundHandler = (c) => {
@@ -84264,7 +85025,7 @@ var init_hono_base = __esm(() => {
84264
85025
  init_compose();
84265
85026
  init_context2();
84266
85027
  init_router();
84267
- init_constants6();
85028
+ init_constants5();
84268
85029
  init_url();
84269
85030
  });
84270
85031
 
@@ -92934,7 +93695,7 @@ var require_node3 = __commonJS((exports) => {
92934
93695
  exports2.isAbsolute = function(aPath) {
92935
93696
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
92936
93697
  };
92937
- function relative(aRoot, aPath) {
93698
+ function relative2(aRoot, aPath) {
92938
93699
  if (aRoot === "") {
92939
93700
  aRoot = ".";
92940
93701
  }
@@ -92953,7 +93714,7 @@ var require_node3 = __commonJS((exports) => {
92953
93714
  }
92954
93715
  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
92955
93716
  }
92956
- exports2.relative = relative;
93717
+ exports2.relative = relative2;
92957
93718
  var supportsNullProto = function() {
92958
93719
  var obj = Object.create(null);
92959
93720
  return !("__proto__" in obj);
@@ -94984,7 +95745,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
94984
95745
  });
94985
95746
  };
94986
95747
  }
94987
- var readFile = (fp) => new Promise((resolve2, reject) => {
95748
+ var readFile2 = (fp) => new Promise((resolve2, reject) => {
94988
95749
  _fs.default.readFile(fp, "utf8", (err2, data) => {
94989
95750
  if (err2)
94990
95751
  return reject(err2);
@@ -95182,7 +95943,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
95182
95943
  data: _this3.packageJsonCache.get(filepath)[options.packageKey]
95183
95944
  };
95184
95945
  }
95185
- const data = _this3.options.parseJSON(yield readFile(filepath));
95946
+ const data = _this3.options.parseJSON(yield readFile2(filepath));
95186
95947
  return {
95187
95948
  path: filepath,
95188
95949
  data
@@ -95190,7 +95951,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
95190
95951
  }
95191
95952
  return {
95192
95953
  path: filepath,
95193
- data: yield readFile(filepath)
95954
+ data: yield readFile2(filepath)
95194
95955
  };
95195
95956
  }
95196
95957
  return {};
@@ -100235,7 +100996,7 @@ var __create2, __defProp2, __getOwnPropDesc, __getOwnPropNames2, __getProtoOf2,
100235
100996
  __defProp2(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
100236
100997
  }
100237
100998
  return to;
100238
- }, __toESM5 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target, mod)), __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value), ANSI_BACKGROUND_OFFSET, wrapAnsi16, wrapAnsi256, wrapAnsi16m, styles2, modifierNames, foregroundColorNames, backgroundColorNames, colorNames, ansiStyles, ansi_styles_default, init_ansi_styles, env, flagForceColor, supportsColor, supports_color_default, init_supports_color, init_utilities, stdoutColor, stderrColor, GENERATOR, STYLER, IS_EMPTY, levelMapping, styles22, applyOptions, chalkFactory, getModelAnsi, usedModels, proto, createStyler, createBuilder, applyStyle, chalk, chalkStderr, source_default, init_source, require_old, require_fs, require_path, require_balanced_match, require_brace_expansion, require_minimatch, require_inherits_browser2, require_inherits2, require_common3, require_sync, require_wrappy, require_once, require_inflight, require_glob, require_readline, require_src3, require_utils4, require_lodash, require_hanji, originUUID, snapshotVersion, mapValues, mapKeys, mapEntries, customMapEntries, init_global2, util3, objectUtil2, ZodParsedType2, getParsedType4, init_util4, ZodIssueCode4, ZodError5, init_ZodError2, errorMap2, en_default4, init_en4, overrideErrorMap2, init_errors9, makeIssue2, ParseStatus2, INVALID3, DIRTY2, OK2, isAborted2, isDirty2, isValid2, isAsync2, init_parseUtil2, init_typeAliases2, errorUtil2, init_errorUtil2, ParseInputLazyPath2, handleResult2, ZodType4, cuidRegex2, cuid2Regex2, ulidRegex2, uuidRegex2, nanoidRegex2, jwtRegex2, durationRegex2, emailRegex2, _emojiRegex2, emojiRegex2, ipv4Regex2, ipv4CidrRegex2, ipv6Regex2, ipv6CidrRegex2, base64Regex2, base64urlRegex2, dateRegexSource2, dateRegex2, ZodString4, ZodNumber4, ZodBigInt4, ZodBoolean4, ZodDate4, ZodSymbol4, ZodUndefined4, ZodNull4, ZodAny4, ZodUnknown4, ZodNever4, ZodVoid4, ZodArray4, ZodObject4, ZodUnion4, getDiscriminator2, ZodDiscriminatedUnion4, ZodIntersection4, ZodTuple4, ZodRecord4, ZodMap4, ZodSet4, ZodFunction3, ZodLazy4, ZodLiteral4, ZodEnum4, ZodNativeEnum2, ZodPromise4, ZodEffects2, ZodOptional4, ZodNullable4, ZodDefault4, ZodCatch4, ZodNaN4, BRAND2, ZodBranded2, ZodPipeline2, ZodReadonly4, late2, ZodFirstPartyTypeKind3, stringType2, numberType2, nanType2, bigIntType2, booleanType2, dateType2, symbolType2, undefinedType2, nullType2, anyType2, unknownType2, neverType2, voidType2, arrayType2, objectType2, strictObjectType2, unionType2, discriminatedUnionType2, intersectionType2, tupleType2, recordType2, mapType2, setType2, functionType2, lazyType2, literalType2, enumType2, nativeEnumType2, promiseType2, effectsType2, optionalType2, nullableType2, preprocessType2, pipelineType2, coerce2, init_types5, init_external4, init_v32, init_esm2, enumSchema, enumSchemaV1, indexColumn, index2, fk, sequenceSchema, roleSchema, sequenceSquashed, column2, checkConstraint, columnSquashed, compositePK, uniqueConstraint, policy, policySquashed, viewWithOption, matViewWithOption, mergedViewWithOption, view2, table8, schemaHash, kitInternals, gelSchemaExternal, gelSchemaInternal, tableSquashed, gelSchemaSquashed, gelSchema, dryGel, init_gelSchema, index22, fk2, column22, tableV3, compositePK2, uniqueConstraint2, checkConstraint2, tableV4, table22, viewMeta, view22, kitInternals2, dialect2, schemaHash2, schemaInternalV3, schemaInternalV4, schemaInternalV5, schemaInternal, schemaV3, schemaV4, schemaV5, schema2, tableSquashedV4, tableSquashed2, viewSquashed, schemaSquashed, schemaSquashedV4, MySqlSquasher, squashMysqlScheme, mysqlSchema, mysqlSchemaV5, mysqlSchemaSquashed, backwardCompatibleMysqlSchema, dryMySql, init_mysqlSchema, indexV2, columnV2, tableV2, enumSchemaV12, enumSchema2, pgSchemaV2, references, columnV1, tableV1, pgSchemaV1, indexColumn2, index3, indexV4, indexV5, indexV6, fk3, sequenceSchema2, roleSchema2, sequenceSquashed2, columnV7, column3, checkConstraint3, columnSquashed2, tableV32, compositePK3, uniqueConstraint3, policy2, policySquashed2, viewWithOption2, matViewWithOption2, mergedViewWithOption2, view3, tableV42, tableV5, tableV6, tableV7, table32, schemaHash3, kitInternals3, pgSchemaInternalV3, pgSchemaInternalV4, pgSchemaInternalV5, pgSchemaInternalV6, pgSchemaExternal, pgSchemaInternalV7, pgSchemaInternal, tableSquashed3, tableSquashedV42, pgSchemaSquashedV4, pgSchemaSquashedV6, pgSchemaSquashed, pgSchemaV3, pgSchemaV4, pgSchemaV5, pgSchemaV6, pgSchemaV7, pgSchema, backwardCompatiblePgSchema, PgSquasher, squashPgScheme, dryPg, init_pgSchema, index4, column4, compositePK4, uniqueConstraint4, table42, viewMeta2, kitInternals4, dialect22, schemaHash4, schemaInternal2, schema22, tableSquashed4, schemaSquashed2, SingleStoreSquasher, squashSingleStoreScheme, singlestoreSchema, singlestoreSchemaSquashed, backwardCompatibleSingleStoreSchema, drySingleStore, init_singlestoreSchema, index5, fk4, compositePK5, column5, tableV33, uniqueConstraint5, checkConstraint4, table52, view4, dialect3, schemaHash5, schemaInternalV32, schemaInternalV42, schemaInternalV52, kitInternals5, latestVersion, schemaInternal3, schemaV32, schemaV42, schemaV52, schema3, tableSquashed5, schemaSquashed3, SQLiteSquasher, squashSqliteScheme, drySQLite, sqliteSchemaV5, sqliteSchema, SQLiteSchemaSquashed, backwardCompatibleSqliteSchema, init_sqliteSchema, copy, prepareMigrationMeta, schemaRenameKey, tableRenameKey, columnRenameKey, init_utils7, import_hanji, warning, error88, isRenamePromptItem, ResolveColumnSelect, tableKey, ResolveSelectNamed, ResolveSelect, ResolveSchemasSelect, Spinner2, ProgressView, init_views, glob, init_serializer, fillPgSnapshot, init_migrationPreparator, require_heap, require_heap2, require_difflib, require_difflib2, require_util2, require_styles, require_has_flag2, require_supports_colors, require_trap, require_zalgo, require_america, require_zebra, require_rainbow, require_random, require_colors, require_safe, require_colorize, require_lib4, import_json_diff, mapArraysDiff, findAlternationsInTable, alternationsInColumn, init_jsonDiffer, parseType, Convertor, PgCreateRoleConvertor, PgDropRoleConvertor, PgRenameRoleConvertor, PgAlterRoleConvertor, PgCreatePolicyConvertor, PgDropPolicyConvertor, PgRenamePolicyConvertor, PgAlterPolicyConvertor, PgCreateIndPolicyConvertor, PgDropIndPolicyConvertor, PgRenameIndPolicyConvertor, PgAlterIndPolicyConvertor, PgEnableRlsConvertor, PgDisableRlsConvertor, PgCreateTableConvertor, MySqlCreateTableConvertor, SingleStoreCreateTableConvertor, SQLiteCreateTableConvertor, PgCreateViewConvertor, MySqlCreateViewConvertor, SqliteCreateViewConvertor, PgDropViewConvertor, MySqlDropViewConvertor, SqliteDropViewConvertor, MySqlAlterViewConvertor, PgRenameViewConvertor, MySqlRenameViewConvertor, PgAlterViewSchemaConvertor, PgAlterViewAddWithOptionConvertor, PgAlterViewDropWithOptionConvertor, PgAlterViewAlterTablespaceConvertor, PgAlterViewAlterUsingConvertor, PgAlterTableAlterColumnSetGenerated, PgAlterTableAlterColumnDropGenerated, PgAlterTableAlterColumnAlterGenerated, PgAlterTableAddUniqueConstraintConvertor, PgAlterTableDropUniqueConstraintConvertor, PgAlterTableAddCheckConstraintConvertor, PgAlterTableDeleteCheckConstraintConvertor, MySQLAlterTableAddUniqueConstraintConvertor, MySQLAlterTableDropUniqueConstraintConvertor, MySqlAlterTableAddCheckConstraintConvertor, SingleStoreAlterTableAddUniqueConstraintConvertor, SingleStoreAlterTableDropUniqueConstraintConvertor, MySqlAlterTableDeleteCheckConstraintConvertor, CreatePgSequenceConvertor, DropPgSequenceConvertor, RenamePgSequenceConvertor, MovePgSequenceConvertor, AlterPgSequenceConvertor, CreateTypeEnumConvertor, DropTypeEnumConvertor, AlterTypeAddValueConvertor, AlterTypeSetSchemaConvertor, AlterRenameTypeConvertor, AlterTypeDropValueConvertor, PgDropTableConvertor, MySQLDropTableConvertor, SingleStoreDropTableConvertor, SQLiteDropTableConvertor, PgRenameTableConvertor, SqliteRenameTableConvertor, MySqlRenameTableConvertor, SingleStoreRenameTableConvertor, PgAlterTableRenameColumnConvertor, MySqlAlterTableRenameColumnConvertor, SingleStoreAlterTableRenameColumnConvertor, SQLiteAlterTableRenameColumnConvertor, PgAlterTableDropColumnConvertor, MySqlAlterTableDropColumnConvertor, SingleStoreAlterTableDropColumnConvertor, SQLiteAlterTableDropColumnConvertor, PgAlterTableAddColumnConvertor, MySqlAlterTableAddColumnConvertor, SingleStoreAlterTableAddColumnConvertor, SQLiteAlterTableAddColumnConvertor, PgAlterTableAlterColumnSetTypeConvertor, PgAlterTableAlterColumnSetDefaultConvertor, PgAlterTableAlterColumnDropDefaultConvertor, PgAlterTableAlterColumnDropGeneratedConvertor, PgAlterTableAlterColumnSetExpressionConvertor, PgAlterTableAlterColumnAlterrGeneratedConvertor, SqliteAlterTableAlterColumnDropGeneratedConvertor, SqliteAlterTableAlterColumnSetExpressionConvertor, SqliteAlterTableAlterColumnAlterGeneratedConvertor, MySqlAlterTableAlterColumnAlterrGeneratedConvertor, MySqlAlterTableAddPk, MySqlAlterTableDropPk, LibSQLModifyColumn, MySqlModifyColumn, SingleStoreAlterTableAlterColumnAlterrGeneratedConvertor, SingleStoreAlterTableAddPk, SingleStoreAlterTableDropPk, SingleStoreModifyColumn, PgAlterTableCreateCompositePrimaryKeyConvertor, PgAlterTableDeleteCompositePrimaryKeyConvertor, PgAlterTableAlterCompositePrimaryKeyConvertor, MySqlAlterTableCreateCompositePrimaryKeyConvertor, MySqlAlterTableDeleteCompositePrimaryKeyConvertor, MySqlAlterTableAlterCompositePrimaryKeyConvertor, PgAlterTableAlterColumnSetPrimaryKeyConvertor, PgAlterTableAlterColumnDropPrimaryKeyConvertor, PgAlterTableAlterColumnSetNotNullConvertor, PgAlterTableAlterColumnDropNotNullConvertor, PgCreateForeignKeyConvertor, LibSQLCreateForeignKeyConvertor, MySqlCreateForeignKeyConvertor, PgAlterForeignKeyConvertor, PgDeleteForeignKeyConvertor, MySqlDeleteForeignKeyConvertor, CreatePgIndexConvertor, CreateMySqlIndexConvertor, CreateSingleStoreIndexConvertor, CreateSqliteIndexConvertor, PgDropIndexConvertor, PgCreateSchemaConvertor, PgRenameSchemaConvertor, PgDropSchemaConvertor, PgAlterTableSetSchemaConvertor, PgAlterTableSetNewSchemaConvertor, PgAlterTableRemoveFromSchemaConvertor, SqliteDropIndexConvertor, MySqlDropIndexConvertor, SingleStoreDropIndexConvertor, SQLiteRecreateTableConvertor, LibSQLRecreateTableConvertor, SingleStoreRecreateTableConvertor, convertors, init_sqlgenerator, _moveDataStatements, getOldTableName, getNewTableName, logSuggestionsAndReturn, init_sqlitePushUtils, preparePgCreateTableJson, prepareMySqlCreateTableJson, prepareSingleStoreCreateTableJson, prepareSQLiteCreateTable, prepareDropTableJson, prepareRenameTableJson, prepareCreateEnumJson, prepareAddValuesToEnumJson, prepareDropEnumValues, prepareDropEnumJson, prepareMoveEnumJson, prepareRenameEnumJson, prepareCreateSequenceJson, prepareAlterSequenceJson, prepareDropSequenceJson, prepareMoveSequenceJson, prepareRenameSequenceJson, prepareCreateRoleJson, prepareAlterRoleJson, prepareDropRoleJson, prepareRenameRoleJson, prepareCreateSchemasJson, prepareRenameSchemasJson, prepareDeleteSchemasJson, prepareRenameColumns, _prepareDropColumns, _prepareAddColumns, _prepareSqliteAddColumns, prepareAlterColumnsMysql, preparePgAlterColumns, prepareSqliteAlterColumns, prepareRenamePolicyJsons, prepareRenameIndPolicyJsons, prepareCreatePolicyJsons, prepareCreateIndPolicyJsons, prepareDropPolicyJsons, prepareDropIndPolicyJsons, prepareAlterPolicyJson, prepareAlterIndPolicyJson, preparePgCreateIndexesJson, prepareCreateIndexesJson, prepareCreateReferencesJson, prepareLibSQLCreateReferencesJson, prepareDropReferencesJson, prepareLibSQLDropReferencesJson, prepareAlterReferencesJson, prepareDropIndexesJson, prepareAddCompositePrimaryKeySqlite, prepareDeleteCompositePrimaryKeySqlite, prepareAlterCompositePrimaryKeySqlite, prepareAddCompositePrimaryKeyPg, prepareDeleteCompositePrimaryKeyPg, prepareAlterCompositePrimaryKeyPg, prepareAddUniqueConstraintPg, prepareDeleteUniqueConstraintPg, prepareAddCheckConstraint, prepareDeleteCheckConstraint, prepareAddCompositePrimaryKeyMySql, prepareDeleteCompositePrimaryKeyMySql, prepareAlterCompositePrimaryKeyMySql, preparePgCreateViewJson, prepareMySqlCreateViewJson, prepareSqliteCreateViewJson, prepareDropViewJson, prepareRenameViewJson, preparePgAlterViewAlterSchemaJson, preparePgAlterViewAddWithOptionJson, preparePgAlterViewDropWithOptionJson, preparePgAlterViewAlterTablespaceJson, preparePgAlterViewAlterUsingJson, prepareMySqlAlterView, init_jsonStatements, prepareLibSQLRecreateTable, prepareSQLiteRecreateTable, libSQLCombineStatements, sqliteCombineStatements, prepareSingleStoreRecreateTable, singleStoreCombineStatements, init_statementCombiner, snapshotsDiffer_exports, makeChanged, makeSelfOrChanged, makePatched, makeSelfOrPatched, columnSchema, alteredColumnSchema, enumSchema3, changedEnumSchema, tableScheme, alteredTableScheme, alteredViewCommon, alteredPgViewSchema, alteredMySqlViewSchema, diffResultScheme, diffResultSchemeMysql, diffResultSchemeSingleStore, diffResultSchemeSQLite, schemaChangeFor, nameChangeFor, nameSchemaChangeFor, columnChangeFor, applyPgSnapshotsDiff, applyMysqlSnapshotsDiff, applySingleStoreSnapshotsDiff, applySqliteSnapshotsDiff, applyLibSQLSnapshotsDiff, init_snapshotsDiffer, init_words, dialects, dialect4, commonSquashedSchema, commonSchema, init_schemaValidator, sqliteDriversLiterals, postgresqlDriversLiterals, prefixes, prefix, casingTypes, casingType, sqliteDriver, postgresDriver, driver2, configMigrations, configCommonSchema, casing, introspectParams, configIntrospectCliSchema, configGenerateSchema, configPushSchema, init_common2, withStyle, init_outputs, import_hanji2, schemasResolver, tablesResolver, viewsResolver, mySqlViewsResolver, sqliteViewsResolver, sequencesResolver, roleResolver, policyResolver, indPolicyResolver, enumsResolver, columnsResolver, promptColumnsConflicts, promptNamedConflict, promptNamedWithSchemasConflict, promptSchemasConflict, BREAKPOINT, init_migrate, posixClasses, braceEscape, regexpEscape, rangesToString, parseClass, init_brace_expressions, escape, init_escape, unescape, init_unescape, import_brace_expansion, minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path, sep, GLOBSTAR, plTypes, qmark, star, twoStarDot, twoStarNoDot, charSet, reSpecials, addPatternStartSet, filter, ext, defaults, braceExpand, MAX_PATTERN_LENGTH, assertValidPattern, makeRe, match2, globUnescape, globMagic, regExpEscape, Minimatch, init_mjs, entityKind2, hasOwnEntityKind2, init_entity2, _a3, Column2, init_column2, _a22, ColumnBuilder2, init_column_builder2, TableName2, init_table_utils2, _a32, ForeignKeyBuilder2, _a4, ForeignKey2, init_foreign_keys2, init_tracing_utils2, _a5, UniqueConstraintBuilder, _a6, UniqueOnConstraintBuilder, _a7, UniqueConstraint, init_unique_constraint2, init_array2, _a8, _b, PgColumnBuilder2, _a9, _b2, PgColumn2, _a10, _b3, ExtraConfigColumn2, _a11, IndexedColumn2, _a12, _b4, PgArrayBuilder2, _a13, _b5, _PgArray, PgArray2, init_common22, _a14, _b6, PgEnumObjectColumnBuilder2, _a15, _b7, PgEnumObjectColumn2, isPgEnumSym2, _a16, _b8, PgEnumColumnBuilder2, _a17, _b9, PgEnumColumn2, init_enum2, _a18, Subquery2, _a19, _b10, WithSubquery2, init_subquery2, version4, init_version2, otel2, rawTracer2, tracer2, init_tracing2, ViewBaseConfig2, init_view_common3, Schema2, Columns2, ExtraConfigColumns2, OriginalName2, BaseName2, IsAlias2, ExtraConfigBuilder2, IsDrizzleTable2, _a20, _b11, _c, _d, _e3, _f, _g, _h, _i, _j, Table2, init_table8, _a21, FakePrimitiveParam, _a222, StringChunk2, _a23, _SQL, SQL2, _a24, Name2, noopDecoder2, noopEncoder2, noopMapper2, _a25, Param2, _a26, Placeholder2, IsDrizzleView2, _a27, _b12, _c2, View3, init_sql3, _a28, ColumnAliasProxyHandler2, _a29, TableAliasProxyHandler2, _a30, RelationTableAliasProxyHandler, init_alias3, _a31, _b13, DrizzleError2, DrizzleQueryError, _a322, _b14, TransactionRollbackError2, init_errors22, _a33, ConsoleLogWriter2, _a34, DefaultLogger2, _a35, NoopLogger2, init_logger3, init_operations, _a36, _b15, QueryPromise2, init_query_promise2, textDecoder, init_utils22, _a37, _b16, PgIntColumnBaseBuilder2, init_int_common2, _a38, _b17, PgBigInt53Builder2, _a39, _b18, PgBigInt532, _a40, _b19, PgBigInt64Builder2, _a41, _b20, PgBigInt642, init_bigint2, _a42, _b21, PgBigSerial53Builder2, _a43, _b22, PgBigSerial532, _a44, _b23, PgBigSerial64Builder2, _a45, _b24, PgBigSerial642, init_bigserial2, _a46, _b25, PgBooleanBuilder2, _a47, _b26, PgBoolean2, init_boolean2, _a48, _b27, PgCharBuilder2, _a49, _b28, PgChar2, init_char2, _a50, _b29, PgCidrBuilder2, _a51, _b30, PgCidr2, init_cidr2, _a52, _b31, PgCustomColumnBuilder2, _a53, _b32, PgCustomColumn2, init_custom2, _a54, _b33, PgDateColumnBaseBuilder2, init_date_common2, _a55, _b34, PgDateBuilder2, _a56, _b35, PgDate2, _a57, _b36, PgDateStringBuilder2, _a58, _b37, PgDateString2, init_date2, _a59, _b38, PgDoublePrecisionBuilder2, _a60, _b39, PgDoublePrecision2, init_double_precision2, _a61, _b40, PgInetBuilder2, _a62, _b41, PgInet2, init_inet2, _a63, _b42, PgIntegerBuilder2, _a64, _b43, PgInteger2, init_integer2, _a65, _b44, PgIntervalBuilder2, _a66, _b45, PgInterval2, init_interval2, _a67, _b46, PgJsonBuilder2, _a68, _b47, PgJson2, init_json2, _a69, _b48, PgJsonbBuilder2, _a70, _b49, PgJsonb2, init_jsonb2, _a71, _b50, PgLineBuilder2, _a72, _b51, PgLineTuple2, _a73, _b52, PgLineABCBuilder2, _a74, _b53, PgLineABC2, init_line2, _a75, _b54, PgMacaddrBuilder2, _a76, _b55, PgMacaddr2, init_macaddr2, _a77, _b56, PgMacaddr8Builder2, _a78, _b57, PgMacaddr82, init_macaddr82, _a79, _b58, PgNumericBuilder2, _a80, _b59, PgNumeric2, _a81, _b60, PgNumericNumberBuilder2, _a82, _b61, PgNumericNumber2, _a83, _b62, PgNumericBigIntBuilder2, _a84, _b63, PgNumericBigInt2, init_numeric2, _a85, _b64, PgPointTupleBuilder2, _a86, _b65, PgPointTuple2, _a87, _b66, PgPointObjectBuilder2, _a88, _b67, PgPointObject2, init_point2, init_utils32, _a89, _b68, PgGeometryBuilder2, _a90, _b69, PgGeometry2, _a91, _b70, PgGeometryObjectBuilder2, _a92, _b71, PgGeometryObject2, init_geometry2, _a93, _b72, PgRealBuilder2, _a94, _b73, PgReal2, init_real2, _a95, _b74, PgSerialBuilder2, _a96, _b75, PgSerial2, init_serial2, _a97, _b76, PgSmallIntBuilder2, _a98, _b77, PgSmallInt2, init_smallint2, _a99, _b78, PgSmallSerialBuilder2, _a100, _b79, PgSmallSerial2, init_smallserial2, _a101, _b80, PgTextBuilder2, _a102, _b81, PgText2, init_text2, _a103, _b82, PgTimeBuilder2, _a104, _b83, PgTime2, init_time2, _a105, _b84, PgTimestampBuilder2, _a106, _b85, PgTimestamp2, _a107, _b86, PgTimestampStringBuilder2, _a108, _b87, PgTimestampString2, init_timestamp2, _a109, _b88, PgUUIDBuilder2, _a110, _b89, PgUUID2, init_uuid3, _a111, _b90, PgVarcharBuilder2, _a112, _b91, PgVarchar2, init_varchar2, _a113, _b92, PgBinaryVectorBuilder2, _a114, _b93, PgBinaryVector2, init_bit2, _a115, _b94, PgHalfVectorBuilder2, _a116, _b95, PgHalfVector2, init_halfvec2, _a117, _b96, PgSparseVectorBuilder2, _a118, _b97, PgSparseVector2, init_sparsevec2, _a119, _b98, PgVectorBuilder2, _a120, _b99, PgVector2, init_vector3, init_all2, InlineForeignKeys2, EnableRLS2, _a121, _b100, _c3, _d2, _e22, _f2, PgTable2, pgTable2, init_table22, _a122, PrimaryKeyBuilder2, _a123, PrimaryKey2, init_primary_keys2, eq2, ne4, gt3, gte2, lt4, lte2, init_conditions2, init_select3, init_expressions2, _a124, Relation2, _a125, Relations2, _a126, _b101, _One, One2, _a127, _b102, _Many, Many2, init_relations2, init_aggregate2, init_vector22, init_functions2, init_sql22, dist_exports, init_dist9, init_alias22, _a128, CheckBuilder, _a129, Check, init_checks6, init_columns2, _a130, _SelectionProxyHandler, SelectionProxyHandler2, init_selection_proxy2, _a131, IndexBuilderOn2, _a132, IndexBuilder2, _a133, Index2, init_indexes2, _a134, PgPolicy, init_policies2, PgViewConfig2, init_view_common22, _a135, CasingCache2, init_casing2, _a136, _b103, PgViewBase2, init_view_base2, _a137, PgDialect2, init_dialect2, _a138, TypedQueryBuilder2, init_query_builder3, _a139, PgSelectBuilder2, _a140, _b104, PgSelectQueryBuilderBase2, _a141, _b105, PgSelectBase2, getPgSetOperators2, union22, unionAll2, intersect2, intersectAll2, except2, exceptAll2, init_select22, _a142, QueryBuilder2, init_query_builder22, _a143, DefaultViewBuilderCore, _a144, _b106, ViewBuilder, _a145, _b107, ManualViewBuilder, _a146, MaterializedViewBuilderCore, _a147, _b108, MaterializedViewBuilder, _a148, _b109, ManualMaterializedViewBuilder, _a149, _b110, _c4, PgView2, PgMaterializedViewConfig2, _a150, _b111, _c5, PgMaterializedView, init_view2, init_utils42, _a151, _b112, PgDeleteBase2, init_delete2, _a152, PgInsertBuilder2, _a153, _b113, PgInsertBase2, init_insert2, _a154, _b114, PgRefreshMaterializedView2, init_refresh_materialized_view2, init_select_types, _a155, PgUpdateBuilder2, _a156, _b115, PgUpdateBase2, init_update2, init_query_builders2, _a157, _b116, _c6, _PgCountBuilder, PgCountBuilder2, init_count2, _a158, RelationalQueryBuilder2, _a159, _b117, PgRelationalQuery2, init_query2, _a160, _b118, PgRaw2, init_raw2, _a161, PgDatabase2, init_db2, _a162, PgRole, init_roles2, _a163, PgSequence, init_sequence2, _a164, PgSchema5, init_schema3, _a165, Cache, _a166, _b119, NoopCache, init_cache, _a167, PgPreparedQuery2, _a168, PgSession2, _a169, _b120, PgTransaction2, init_session3, init_subquery22, init_utils52, init_pg_core2, vectorOps, init_vector32, sqlToStr, init_utils62, indexName, generatePgSnapshot, trimChar, fromDatabase, defaultForColumn, getColumnsInfoQuery, init_pgSerializer, import_hanji4, Select, init_selector_ui, init_alias32, _a170, CheckBuilder2, _a171, Check2, init_checks22, _a172, ForeignKeyBuilder22, _a173, ForeignKey22, init_foreign_keys22, _a174, UniqueConstraintBuilder2, _a175, UniqueOnConstraintBuilder2, _a176, UniqueConstraint2, init_unique_constraint22, _a177, _b121, SQLiteColumnBuilder, _a178, _b122, SQLiteColumn, init_common3, _a179, _b123, SQLiteBigIntBuilder, _a180, _b124, SQLiteBigInt, _a181, _b125, SQLiteBlobJsonBuilder, _a182, _b126, SQLiteBlobJson, _a183, _b127, SQLiteBlobBufferBuilder, _a184, _b128, SQLiteBlobBuffer, init_blob, _a185, _b129, SQLiteCustomColumnBuilder, _a186, _b130, SQLiteCustomColumn, init_custom22, _a187, _b131, SQLiteBaseIntegerBuilder, _a188, _b132, SQLiteBaseInteger, _a189, _b133, SQLiteIntegerBuilder, _a190, _b134, SQLiteInteger, _a191, _b135, SQLiteTimestampBuilder, _a192, _b136, SQLiteTimestamp, _a193, _b137, SQLiteBooleanBuilder, _a194, _b138, SQLiteBoolean, init_integer22, _a195, _b139, SQLiteNumericBuilder, _a196, _b140, SQLiteNumeric, _a197, _b141, SQLiteNumericNumberBuilder, _a198, _b142, SQLiteNumericNumber, _a199, _b143, SQLiteNumericBigIntBuilder, _a200, _b144, SQLiteNumericBigInt, init_numeric22, _a201, _b145, SQLiteRealBuilder, _a202, _b146, SQLiteReal, init_real22, _a203, _b147, SQLiteTextBuilder, _a204, _b148, SQLiteText, _a205, _b149, SQLiteTextJsonBuilder, _a206, _b150, SQLiteTextJson, init_text22, init_columns22, init_all22, InlineForeignKeys22, _a207, _b151, _c7, _d3, _e32, SQLiteTable, sqliteTable, init_table32, _a208, IndexBuilderOn22, _a209, IndexBuilder22, _a210, Index4, init_indexes22, _a211, PrimaryKeyBuilder22, _a212, PrimaryKey22, init_primary_keys22, init_utils72, _a213, _b152, SQLiteDeleteBase, init_delete22, _a214, _b153, SQLiteViewBase, init_view_base22, _a215, SQLiteDialect, _a216, _b154, SQLiteSyncDialect, _a217, _b155, SQLiteAsyncDialect, init_dialect22, _a218, SQLiteSelectBuilder, _a219, _b156, SQLiteSelectQueryBuilderBase, _a220, _b157, SQLiteSelectBase, getSQLiteSetOperators, union32, unionAll22, intersect22, except22, init_select32, _a221, QueryBuilder22, init_query_builder32, _a2222, SQLiteInsertBuilder, _a223, _b158, SQLiteInsertBase, init_insert22, init_select_types2, _a224, SQLiteUpdateBuilder, _a225, _b159, SQLiteUpdateBase, init_update22, init_query_builders22, _a226, _b160, _c8, _SQLiteCountBuilder, SQLiteCountBuilder, init_count22, _a227, RelationalQueryBuilder22, _a228, _b161, SQLiteRelationalQuery, _a229, _b162, SQLiteSyncRelationalQuery, init_query22, _a230, _b163, SQLiteRaw, init_raw22, _a231, BaseSQLiteDatabase, init_db22, _a232, _b164, ExecuteResultSync, _a233, SQLitePreparedQuery, _a234, SQLiteSession, _a235, _b165, SQLiteTransaction, init_session22, init_subquery3, _a236, ViewBuilderCore, _a237, _b166, ViewBuilder2, _a238, _b167, ManualViewBuilder2, _a239, _b168, SQLiteView2, init_view22, init_sqlite_core, generateSqliteSnapshot, fromDatabase2, init_sqliteSerializer, getTablesFilterByExtensions, init_getTablesFilterByExtensions, init_alias4, _a240, CheckBuilder3, _a241, Check3, init_checks32, _a242, ForeignKeyBuilder3, _a243, ForeignKey3, init_foreign_keys3, _a244, UniqueConstraintBuilder3, _a245, UniqueOnConstraintBuilder3, _a246, UniqueConstraint3, init_unique_constraint3, _a247, _b169, MySqlColumnBuilder, _a248, _b170, MySqlColumn, _a249, _b171, MySqlColumnBuilderWithAutoIncrement, _a250, _b172, MySqlColumnWithAutoIncrement, init_common4, _a251, _b173, MySqlBigInt53Builder, _a252, _b174, MySqlBigInt53, _a253, _b175, MySqlBigInt64Builder, _a254, _b176, MySqlBigInt64, init_bigint22, _a255, _b177, MySqlBinaryBuilder, _a256, _b178, MySqlBinary, init_binary, _a257, _b179, MySqlBooleanBuilder, _a258, _b180, MySqlBoolean, init_boolean22, _a259, _b181, MySqlCharBuilder, _a260, _b182, MySqlChar, init_char22, _a261, _b183, MySqlCustomColumnBuilder, _a262, _b184, MySqlCustomColumn, init_custom3, _a263, _b185, MySqlDateBuilder, _a264, _b186, MySqlDate, _a265, _b187, MySqlDateStringBuilder, _a266, _b188, MySqlDateString, init_date22, _a267, _b189, MySqlDateTimeBuilder, _a268, _b190, MySqlDateTime, _a269, _b191, MySqlDateTimeStringBuilder, _a270, _b192, MySqlDateTimeString, init_datetime, _a271, _b193, MySqlDecimalBuilder, _a272, _b194, MySqlDecimal, _a273, _b195, MySqlDecimalNumberBuilder, _a274, _b196, MySqlDecimalNumber, _a275, _b197, MySqlDecimalBigIntBuilder, _a276, _b198, MySqlDecimalBigInt, init_decimal, _a277, _b199, MySqlDoubleBuilder, _a278, _b200, MySqlDouble, init_double, _a279, _b201, MySqlEnumColumnBuilder, _a280, _b202, MySqlEnumColumn, _a281, _b203, MySqlEnumObjectColumnBuilder, _a282, _b204, MySqlEnumObjectColumn, init_enum22, _a283, _b205, MySqlFloatBuilder, _a284, _b206, MySqlFloat, init_float, _a285, _b207, MySqlIntBuilder, _a286, _b208, MySqlInt, init_int, _a287, _b209, MySqlJsonBuilder, _a288, _b210, MySqlJson, init_json22, _a289, _b211, MySqlMediumIntBuilder, _a290, _b212, MySqlMediumInt, init_mediumint, _a291, _b213, MySqlRealBuilder, _a292, _b214, MySqlReal, init_real3, _a293, _b215, MySqlSerialBuilder, _a294, _b216, MySqlSerial, init_serial22, _a295, _b217, MySqlSmallIntBuilder, _a296, _b218, MySqlSmallInt, init_smallint22, _a297, _b219, MySqlTextBuilder, _a298, _b220, MySqlText, init_text3, _a299, _b221, MySqlTimeBuilder, _a300, _b222, MySqlTime, init_time22, _a301, _b223, MySqlDateColumnBaseBuilder, _a302, _b224, MySqlDateBaseColumn, init_date_common22, _a303, _b225, MySqlTimestampBuilder, _a304, _b226, MySqlTimestamp, _a305, _b227, MySqlTimestampStringBuilder, _a306, _b228, MySqlTimestampString, init_timestamp22, _a307, _b229, MySqlTinyIntBuilder, _a308, _b230, MySqlTinyInt, init_tinyint, _a309, _b231, MySqlVarBinaryBuilder, _a310, _b232, MySqlVarBinary, init_varbinary, _a311, _b233, MySqlVarCharBuilder, _a312, _b234, MySqlVarChar, init_varchar22, _a313, _b235, MySqlYearBuilder, _a314, _b236, MySqlYear, init_year, init_columns3, _a315, _b237, _c9, _MySqlCountBuilder, MySqlCountBuilder, init_count3, _a316, IndexBuilderOn3, _a317, IndexBuilder3, _a318, Index5, init_indexes3, init_all3, InlineForeignKeys3, _a319, _b238, _c10, _d4, _e4, MySqlTable, mysqlTable, init_table42, _a320, PrimaryKeyBuilder3, _a321, PrimaryKey3, init_primary_keys3, MySqlViewConfig, init_view_common32, init_utils8, _a3222, _b239, MySqlDeleteBase, init_delete3, _a323, _b240, MySqlViewBase, init_view_base3, _a324, MySqlDialect, init_dialect3, _a325, MySqlSelectBuilder, _a326, _b241, MySqlSelectQueryBuilderBase, _a327, _b242, MySqlSelectBase, getMySqlSetOperators, union4, unionAll3, intersect3, intersectAll22, except3, exceptAll22, init_select4, _a328, QueryBuilder3, init_query_builder4, _a329, MySqlInsertBuilder, _a330, _b243, MySqlInsertBase, init_insert3, init_select_types3, _a331, MySqlUpdateBuilder, _a332, _b244, MySqlUpdateBase, init_update3, init_query_builders3, _a333, RelationalQueryBuilder3, _a334, _b245, MySqlRelationalQuery, init_query3, _a335, MySqlDatabase, init_db3, _a336, ViewBuilderCore2, _a337, _b246, ViewBuilder3, _a338, _b247, ManualViewBuilder3, _a339, _b248, _c11, MySqlView2, init_view3, _a340, MySqlSchema5, init_schema22, _a341, MySqlPreparedQuery, _a342, MySqlSession, _a343, _b249, MySqlTransaction, init_session32, init_subquery4, init_mysql_core, handleEnumType, generateMySqlSnapshot, fromDatabase3, init_mysqlSerializer, cliConfigGenerate, pushParams, pullParams, configCheck, cliConfigCheck, init_cli, gelCredentials, init_gel, libSQLCredentials, init_libsql, mysqlCredentials, init_mysql, postgresCredentials, init_postgres, singlestoreCredentials, init_singlestore, sqliteCredentials, init_sqlite, credentials, studioCliParams, studioConfig, init_studio, es5_exports, _3, es5_default, init_es5, import_hanji7, assertES5, safeRegister, migrateConfig, init_utils9, prepareFromExports, init_pgImports, init_alias5, _a344, UniqueConstraintBuilder4, _a345, UniqueOnConstraintBuilder4, _a346, UniqueConstraint4, init_unique_constraint4, _a347, _b250, SingleStoreColumnBuilder, _a348, _b251, SingleStoreColumn, _a349, _b252, SingleStoreColumnBuilderWithAutoIncrement, _a350, _b253, SingleStoreColumnWithAutoIncrement, init_common5, _a351, _b254, SingleStoreBigInt53Builder, _a352, _b255, SingleStoreBigInt53, _a353, _b256, SingleStoreBigInt64Builder, _a354, _b257, SingleStoreBigInt64, init_bigint3, _a355, _b258, SingleStoreBinaryBuilder, _a356, _b259, SingleStoreBinary, init_binary2, _a357, _b260, SingleStoreBooleanBuilder, _a358, _b261, SingleStoreBoolean, init_boolean3, _a359, _b262, SingleStoreCharBuilder, _a360, _b263, SingleStoreChar, init_char3, _a361, _b264, SingleStoreCustomColumnBuilder, _a362, _b265, SingleStoreCustomColumn, init_custom4, _a363, _b266, SingleStoreDateBuilder, _a364, _b267, SingleStoreDate, _a365, _b268, SingleStoreDateStringBuilder, _a366, _b269, SingleStoreDateString, init_date3, _a367, _b270, SingleStoreDateTimeBuilder, _a368, _b271, SingleStoreDateTime, _a369, _b272, SingleStoreDateTimeStringBuilder, _a370, _b273, SingleStoreDateTimeString, init_datetime2, _a371, _b274, SingleStoreDecimalBuilder, _a372, _b275, SingleStoreDecimal, _a373, _b276, SingleStoreDecimalNumberBuilder, _a374, _b277, SingleStoreDecimalNumber, _a375, _b278, SingleStoreDecimalBigIntBuilder, _a376, _b279, SingleStoreDecimalBigInt, init_decimal2, _a377, _b280, SingleStoreDoubleBuilder, _a378, _b281, SingleStoreDouble, init_double2, _a379, _b282, SingleStoreEnumColumnBuilder, _a380, _b283, SingleStoreEnumColumn, init_enum3, _a381, _b284, SingleStoreFloatBuilder, _a382, _b285, SingleStoreFloat, init_float2, _a383, _b286, SingleStoreIntBuilder, _a384, _b287, SingleStoreInt, init_int2, _a385, _b288, SingleStoreJsonBuilder, _a386, _b289, SingleStoreJson, init_json3, _a387, _b290, SingleStoreMediumIntBuilder, _a388, _b291, SingleStoreMediumInt, init_mediumint2, _a389, _b292, SingleStoreRealBuilder, _a390, _b293, SingleStoreReal, init_real4, _a391, _b294, SingleStoreSerialBuilder, _a392, _b295, SingleStoreSerial, init_serial3, _a393, _b296, SingleStoreSmallIntBuilder, _a394, _b297, SingleStoreSmallInt, init_smallint3, _a395, _b298, SingleStoreTextBuilder, _a396, _b299, SingleStoreText, init_text4, _a397, _b300, SingleStoreTimeBuilder, _a398, _b301, SingleStoreTime, init_time3, _a399, _b302, SingleStoreDateColumnBaseBuilder, _a400, _b303, SingleStoreDateBaseColumn, init_date_common3, _a401, _b304, SingleStoreTimestampBuilder, _a402, _b305, SingleStoreTimestamp, _a403, _b306, SingleStoreTimestampStringBuilder, _a404, _b307, SingleStoreTimestampString, init_timestamp3, _a405, _b308, SingleStoreTinyIntBuilder, _a406, _b309, SingleStoreTinyInt, init_tinyint2, _a407, _b310, SingleStoreVarBinaryBuilder, _a408, _b311, SingleStoreVarBinary, init_varbinary2, _a409, _b312, SingleStoreVarCharBuilder, _a410, _b313, SingleStoreVarChar, init_varchar3, _a411, _b314, SingleStoreVectorBuilder, _a412, _b315, SingleStoreVector, init_vector4, _a413, _b316, SingleStoreYearBuilder, _a414, _b317, SingleStoreYear, init_year2, init_columns4, _a415, _b318, _c12, _SingleStoreCountBuilder, SingleStoreCountBuilder, init_count4, _a416, IndexBuilderOn4, _a417, IndexBuilder4, _a418, Index6, init_indexes4, init_all4, _a419, _b319, _c13, _d5, SingleStoreTable, init_table52, _a420, PrimaryKeyBuilder4, _a421, PrimaryKey4, init_primary_keys4, init_utils10, _a422, _b320, SingleStoreDeleteBase, init_delete4, _a423, SingleStoreInsertBuilder, _a424, _b321, SingleStoreInsertBase, init_insert4, _a425, SingleStoreDialect, init_dialect4, _a426, SingleStoreSelectBuilder, _a427, _b322, SingleStoreSelectQueryBuilderBase, _a428, _b323, SingleStoreSelectBase, getSingleStoreSetOperators, union5, unionAll4, intersect4, except4, minus, init_select5, _a429, QueryBuilder4, init_query_builder5, init_select_types4, _a430, SingleStoreUpdateBuilder, _a431, _b324, SingleStoreUpdateBase, init_update4, init_query_builders4, _a432, SingleStoreDatabase, init_db4, _a433, SingleStoreSchema5, init_schema32, _a434, SingleStorePreparedQuery, _a435, SingleStoreSession, _a436, _b325, SingleStoreTransaction, init_session4, init_subquery5, init_singlestore_core, dialect5, generateSingleStoreSnapshot, fromDatabase4, init_singlestoreSerializer, sqliteImports_exports, prepareFromExports2, prepareFromSqliteImports, init_sqliteImports, mysqlImports_exports, prepareFromExports3, prepareFromMySqlImports, init_mysqlImports, mysqlPushUtils_exports, import_hanji8, filterStatements, logSuggestionsAndReturn2, init_mysqlPushUtils, mysqlIntrospect_exports, import_hanji9, mysqlPushIntrospect, init_mysqlIntrospect, singlestoreImports_exports, prepareFromExports4, prepareFromSingleStoreImports, init_singlestoreImports, singlestorePushUtils_exports, import_hanji10, filterStatements2, logSuggestionsAndReturn3, init_singlestorePushUtils, singlestoreIntrospect_exports, import_hanji11, singlestorePushIntrospect, init_singlestoreIntrospect, import_hanji3, pgPushIntrospect = async (db2, filters, schemaFilters, entities, tsSchema) => {
100999
+ }, __toESM5 = (mod, isNodeMode, target) => (target = mod != null ? __create2(__getProtoOf2(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp2(target, "default", { value: mod, enumerable: true }) : target, mod)), __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value), ANSI_BACKGROUND_OFFSET, wrapAnsi16, wrapAnsi256, wrapAnsi16m, styles2, modifierNames, foregroundColorNames, backgroundColorNames, colorNames, ansiStyles, ansi_styles_default, init_ansi_styles, env, flagForceColor, supportsColor, supports_color_default, init_supports_color, init_utilities, stdoutColor, stderrColor, GENERATOR, STYLER, IS_EMPTY, levelMapping, styles22, applyOptions, chalkFactory, getModelAnsi, usedModels, proto, createStyler, createBuilder, applyStyle, chalk, chalkStderr, source_default, init_source, require_old, require_fs, require_path, require_balanced_match, require_brace_expansion, require_minimatch, require_inherits_browser2, require_inherits2, require_common3, require_sync, require_wrappy, require_once, require_inflight, require_glob, require_readline, require_src3, require_utils4, require_lodash, require_hanji, originUUID, snapshotVersion, mapValues, mapKeys, mapEntries, customMapEntries, init_global2, util3, objectUtil2, ZodParsedType2, getParsedType4, init_util4, ZodIssueCode4, ZodError5, init_ZodError2, errorMap2, en_default4, init_en4, overrideErrorMap2, init_errors9, makeIssue2, ParseStatus2, INVALID3, DIRTY2, OK2, isAborted2, isDirty2, isValid2, isAsync2, init_parseUtil2, init_typeAliases2, errorUtil2, init_errorUtil2, ParseInputLazyPath2, handleResult2, ZodType4, cuidRegex2, cuid2Regex2, ulidRegex2, uuidRegex2, nanoidRegex2, jwtRegex2, durationRegex2, emailRegex2, _emojiRegex2, emojiRegex2, ipv4Regex2, ipv4CidrRegex2, ipv6Regex2, ipv6CidrRegex2, base64Regex2, base64urlRegex2, dateRegexSource2, dateRegex2, ZodString4, ZodNumber4, ZodBigInt4, ZodBoolean4, ZodDate4, ZodSymbol4, ZodUndefined4, ZodNull4, ZodAny4, ZodUnknown4, ZodNever4, ZodVoid4, ZodArray4, ZodObject4, ZodUnion4, getDiscriminator2, ZodDiscriminatedUnion4, ZodIntersection4, ZodTuple4, ZodRecord4, ZodMap4, ZodSet4, ZodFunction3, ZodLazy4, ZodLiteral4, ZodEnum4, ZodNativeEnum2, ZodPromise4, ZodEffects2, ZodOptional4, ZodNullable4, ZodDefault4, ZodCatch4, ZodNaN4, BRAND2, ZodBranded2, ZodPipeline2, ZodReadonly4, late2, ZodFirstPartyTypeKind3, stringType2, numberType2, nanType2, bigIntType2, booleanType2, dateType2, symbolType2, undefinedType2, nullType2, anyType2, unknownType2, neverType2, voidType2, arrayType2, objectType2, strictObjectType2, unionType2, discriminatedUnionType2, intersectionType2, tupleType2, recordType2, mapType2, setType2, functionType2, lazyType2, literalType2, enumType2, nativeEnumType2, promiseType2, effectsType2, optionalType2, nullableType2, preprocessType2, pipelineType2, coerce2, init_types5, init_external4, init_v32, init_esm2, enumSchema, enumSchemaV1, indexColumn, index2, fk, sequenceSchema, roleSchema, sequenceSquashed, column2, checkConstraint, columnSquashed, compositePK, uniqueConstraint, policy, policySquashed, viewWithOption, matViewWithOption, mergedViewWithOption, view2, table8, schemaHash, kitInternals, gelSchemaExternal, gelSchemaInternal, tableSquashed, gelSchemaSquashed, gelSchema, dryGel, init_gelSchema, index22, fk2, column22, tableV3, compositePK2, uniqueConstraint2, checkConstraint2, tableV4, table22, viewMeta, view22, kitInternals2, dialect2, schemaHash2, schemaInternalV3, schemaInternalV4, schemaInternalV5, schemaInternal, schemaV3, schemaV4, schemaV5, schema2, tableSquashedV4, tableSquashed2, viewSquashed, schemaSquashed, schemaSquashedV4, MySqlSquasher, squashMysqlScheme, mysqlSchema, mysqlSchemaV5, mysqlSchemaSquashed, backwardCompatibleMysqlSchema, dryMySql, init_mysqlSchema, indexV2, columnV2, tableV2, enumSchemaV12, enumSchema2, pgSchemaV2, references, columnV1, tableV1, pgSchemaV1, indexColumn2, index3, indexV4, indexV5, indexV6, fk3, sequenceSchema2, roleSchema2, sequenceSquashed2, columnV7, column3, checkConstraint3, columnSquashed2, tableV32, compositePK3, uniqueConstraint3, policy2, policySquashed2, viewWithOption2, matViewWithOption2, mergedViewWithOption2, view3, tableV42, tableV5, tableV6, tableV7, table32, schemaHash3, kitInternals3, pgSchemaInternalV3, pgSchemaInternalV4, pgSchemaInternalV5, pgSchemaInternalV6, pgSchemaExternal, pgSchemaInternalV7, pgSchemaInternal, tableSquashed3, tableSquashedV42, pgSchemaSquashedV4, pgSchemaSquashedV6, pgSchemaSquashed, pgSchemaV3, pgSchemaV4, pgSchemaV5, pgSchemaV6, pgSchemaV7, pgSchema, backwardCompatiblePgSchema, PgSquasher, squashPgScheme, dryPg, init_pgSchema, index4, column4, compositePK4, uniqueConstraint4, table42, viewMeta2, kitInternals4, dialect22, schemaHash4, schemaInternal2, schema22, tableSquashed4, schemaSquashed2, SingleStoreSquasher, squashSingleStoreScheme, singlestoreSchema, singlestoreSchemaSquashed, backwardCompatibleSingleStoreSchema, drySingleStore, init_singlestoreSchema, index5, fk4, compositePK5, column5, tableV33, uniqueConstraint5, checkConstraint4, table52, view4, dialect3, schemaHash5, schemaInternalV32, schemaInternalV42, schemaInternalV52, kitInternals5, latestVersion, schemaInternal3, schemaV32, schemaV42, schemaV52, schema3, tableSquashed5, schemaSquashed3, SQLiteSquasher, squashSqliteScheme, drySQLite, sqliteSchemaV5, sqliteSchema, SQLiteSchemaSquashed, backwardCompatibleSqliteSchema, init_sqliteSchema, copy, prepareMigrationMeta, schemaRenameKey, tableRenameKey, columnRenameKey, init_utils7, import_hanji, warning, error88, isRenamePromptItem, ResolveColumnSelect, tableKey, ResolveSelectNamed, ResolveSelect, ResolveSchemasSelect, Spinner2, ProgressView, init_views, glob, init_serializer, fillPgSnapshot, init_migrationPreparator, require_heap, require_heap2, require_difflib, require_difflib2, require_util2, require_styles, require_has_flag2, require_supports_colors, require_trap, require_zalgo, require_america, require_zebra, require_rainbow, require_random, require_colors, require_safe, require_colorize, require_lib4, import_json_diff, mapArraysDiff, findAlternationsInTable, alternationsInColumn, init_jsonDiffer, parseType, Convertor, PgCreateRoleConvertor, PgDropRoleConvertor, PgRenameRoleConvertor, PgAlterRoleConvertor, PgCreatePolicyConvertor, PgDropPolicyConvertor, PgRenamePolicyConvertor, PgAlterPolicyConvertor, PgCreateIndPolicyConvertor, PgDropIndPolicyConvertor, PgRenameIndPolicyConvertor, PgAlterIndPolicyConvertor, PgEnableRlsConvertor, PgDisableRlsConvertor, PgCreateTableConvertor, MySqlCreateTableConvertor, SingleStoreCreateTableConvertor, SQLiteCreateTableConvertor, PgCreateViewConvertor, MySqlCreateViewConvertor, SqliteCreateViewConvertor, PgDropViewConvertor, MySqlDropViewConvertor, SqliteDropViewConvertor, MySqlAlterViewConvertor, PgRenameViewConvertor, MySqlRenameViewConvertor, PgAlterViewSchemaConvertor, PgAlterViewAddWithOptionConvertor, PgAlterViewDropWithOptionConvertor, PgAlterViewAlterTablespaceConvertor, PgAlterViewAlterUsingConvertor, PgAlterTableAlterColumnSetGenerated, PgAlterTableAlterColumnDropGenerated, PgAlterTableAlterColumnAlterGenerated, PgAlterTableAddUniqueConstraintConvertor, PgAlterTableDropUniqueConstraintConvertor, PgAlterTableAddCheckConstraintConvertor, PgAlterTableDeleteCheckConstraintConvertor, MySQLAlterTableAddUniqueConstraintConvertor, MySQLAlterTableDropUniqueConstraintConvertor, MySqlAlterTableAddCheckConstraintConvertor, SingleStoreAlterTableAddUniqueConstraintConvertor, SingleStoreAlterTableDropUniqueConstraintConvertor, MySqlAlterTableDeleteCheckConstraintConvertor, CreatePgSequenceConvertor, DropPgSequenceConvertor, RenamePgSequenceConvertor, MovePgSequenceConvertor, AlterPgSequenceConvertor, CreateTypeEnumConvertor, DropTypeEnumConvertor, AlterTypeAddValueConvertor, AlterTypeSetSchemaConvertor, AlterRenameTypeConvertor, AlterTypeDropValueConvertor, PgDropTableConvertor, MySQLDropTableConvertor, SingleStoreDropTableConvertor, SQLiteDropTableConvertor, PgRenameTableConvertor, SqliteRenameTableConvertor, MySqlRenameTableConvertor, SingleStoreRenameTableConvertor, PgAlterTableRenameColumnConvertor, MySqlAlterTableRenameColumnConvertor, SingleStoreAlterTableRenameColumnConvertor, SQLiteAlterTableRenameColumnConvertor, PgAlterTableDropColumnConvertor, MySqlAlterTableDropColumnConvertor, SingleStoreAlterTableDropColumnConvertor, SQLiteAlterTableDropColumnConvertor, PgAlterTableAddColumnConvertor, MySqlAlterTableAddColumnConvertor, SingleStoreAlterTableAddColumnConvertor, SQLiteAlterTableAddColumnConvertor, PgAlterTableAlterColumnSetTypeConvertor, PgAlterTableAlterColumnSetDefaultConvertor, PgAlterTableAlterColumnDropDefaultConvertor, PgAlterTableAlterColumnDropGeneratedConvertor, PgAlterTableAlterColumnSetExpressionConvertor, PgAlterTableAlterColumnAlterrGeneratedConvertor, SqliteAlterTableAlterColumnDropGeneratedConvertor, SqliteAlterTableAlterColumnSetExpressionConvertor, SqliteAlterTableAlterColumnAlterGeneratedConvertor, MySqlAlterTableAlterColumnAlterrGeneratedConvertor, MySqlAlterTableAddPk, MySqlAlterTableDropPk, LibSQLModifyColumn, MySqlModifyColumn, SingleStoreAlterTableAlterColumnAlterrGeneratedConvertor, SingleStoreAlterTableAddPk, SingleStoreAlterTableDropPk, SingleStoreModifyColumn, PgAlterTableCreateCompositePrimaryKeyConvertor, PgAlterTableDeleteCompositePrimaryKeyConvertor, PgAlterTableAlterCompositePrimaryKeyConvertor, MySqlAlterTableCreateCompositePrimaryKeyConvertor, MySqlAlterTableDeleteCompositePrimaryKeyConvertor, MySqlAlterTableAlterCompositePrimaryKeyConvertor, PgAlterTableAlterColumnSetPrimaryKeyConvertor, PgAlterTableAlterColumnDropPrimaryKeyConvertor, PgAlterTableAlterColumnSetNotNullConvertor, PgAlterTableAlterColumnDropNotNullConvertor, PgCreateForeignKeyConvertor, LibSQLCreateForeignKeyConvertor, MySqlCreateForeignKeyConvertor, PgAlterForeignKeyConvertor, PgDeleteForeignKeyConvertor, MySqlDeleteForeignKeyConvertor, CreatePgIndexConvertor, CreateMySqlIndexConvertor, CreateSingleStoreIndexConvertor, CreateSqliteIndexConvertor, PgDropIndexConvertor, PgCreateSchemaConvertor, PgRenameSchemaConvertor, PgDropSchemaConvertor, PgAlterTableSetSchemaConvertor, PgAlterTableSetNewSchemaConvertor, PgAlterTableRemoveFromSchemaConvertor, SqliteDropIndexConvertor, MySqlDropIndexConvertor, SingleStoreDropIndexConvertor, SQLiteRecreateTableConvertor, LibSQLRecreateTableConvertor, SingleStoreRecreateTableConvertor, convertors, init_sqlgenerator, _moveDataStatements, getOldTableName, getNewTableName, logSuggestionsAndReturn, init_sqlitePushUtils, preparePgCreateTableJson, prepareMySqlCreateTableJson, prepareSingleStoreCreateTableJson, prepareSQLiteCreateTable, prepareDropTableJson, prepareRenameTableJson, prepareCreateEnumJson, prepareAddValuesToEnumJson, prepareDropEnumValues, prepareDropEnumJson, prepareMoveEnumJson, prepareRenameEnumJson, prepareCreateSequenceJson, prepareAlterSequenceJson, prepareDropSequenceJson, prepareMoveSequenceJson, prepareRenameSequenceJson, prepareCreateRoleJson, prepareAlterRoleJson, prepareDropRoleJson, prepareRenameRoleJson, prepareCreateSchemasJson, prepareRenameSchemasJson, prepareDeleteSchemasJson, prepareRenameColumns, _prepareDropColumns, _prepareAddColumns, _prepareSqliteAddColumns, prepareAlterColumnsMysql, preparePgAlterColumns, prepareSqliteAlterColumns, prepareRenamePolicyJsons, prepareRenameIndPolicyJsons, prepareCreatePolicyJsons, prepareCreateIndPolicyJsons, prepareDropPolicyJsons, prepareDropIndPolicyJsons, prepareAlterPolicyJson, prepareAlterIndPolicyJson, preparePgCreateIndexesJson, prepareCreateIndexesJson, prepareCreateReferencesJson, prepareLibSQLCreateReferencesJson, prepareDropReferencesJson, prepareLibSQLDropReferencesJson, prepareAlterReferencesJson, prepareDropIndexesJson, prepareAddCompositePrimaryKeySqlite, prepareDeleteCompositePrimaryKeySqlite, prepareAlterCompositePrimaryKeySqlite, prepareAddCompositePrimaryKeyPg, prepareDeleteCompositePrimaryKeyPg, prepareAlterCompositePrimaryKeyPg, prepareAddUniqueConstraintPg, prepareDeleteUniqueConstraintPg, prepareAddCheckConstraint, prepareDeleteCheckConstraint, prepareAddCompositePrimaryKeyMySql, prepareDeleteCompositePrimaryKeyMySql, prepareAlterCompositePrimaryKeyMySql, preparePgCreateViewJson, prepareMySqlCreateViewJson, prepareSqliteCreateViewJson, prepareDropViewJson, prepareRenameViewJson, preparePgAlterViewAlterSchemaJson, preparePgAlterViewAddWithOptionJson, preparePgAlterViewDropWithOptionJson, preparePgAlterViewAlterTablespaceJson, preparePgAlterViewAlterUsingJson, prepareMySqlAlterView, init_jsonStatements, prepareLibSQLRecreateTable, prepareSQLiteRecreateTable, libSQLCombineStatements, sqliteCombineStatements, prepareSingleStoreRecreateTable, singleStoreCombineStatements, init_statementCombiner, snapshotsDiffer_exports, makeChanged, makeSelfOrChanged, makePatched, makeSelfOrPatched, columnSchema, alteredColumnSchema, enumSchema3, changedEnumSchema, tableScheme, alteredTableScheme, alteredViewCommon, alteredPgViewSchema, alteredMySqlViewSchema, diffResultScheme, diffResultSchemeMysql, diffResultSchemeSingleStore, diffResultSchemeSQLite, schemaChangeFor, nameChangeFor, nameSchemaChangeFor, columnChangeFor, applyPgSnapshotsDiff, applyMysqlSnapshotsDiff, applySingleStoreSnapshotsDiff, applySqliteSnapshotsDiff, applyLibSQLSnapshotsDiff, init_snapshotsDiffer, init_words, dialects, dialect4, commonSquashedSchema, commonSchema, init_schemaValidator, sqliteDriversLiterals, postgresqlDriversLiterals, prefixes, prefix, casingTypes, casingType, sqliteDriver, postgresDriver, driver2, configMigrations, configCommonSchema, casing, introspectParams, configIntrospectCliSchema, configGenerateSchema, configPushSchema, init_common2, withStyle, init_outputs, import_hanji2, schemasResolver, tablesResolver, viewsResolver, mySqlViewsResolver, sqliteViewsResolver, sequencesResolver, roleResolver, policyResolver, indPolicyResolver, enumsResolver, columnsResolver, promptColumnsConflicts, promptNamedConflict, promptNamedWithSchemasConflict, promptSchemasConflict, BREAKPOINT, init_migrate, posixClasses, braceEscape, regexpEscape, rangesToString, parseClass, init_brace_expressions, escape, init_escape, unescape, init_unescape, import_brace_expansion, minimatch, starDotExtRE, starDotExtTest, starDotExtTestDot, starDotExtTestNocase, starDotExtTestNocaseDot, starDotStarRE, starDotStarTest, starDotStarTestDot, dotStarRE, dotStarTest, starRE, starTest, starTestDot, qmarksRE, qmarksTestNocase, qmarksTestNocaseDot, qmarksTestDot, qmarksTest, qmarksTestNoExt, qmarksTestNoExtDot, defaultPlatform, path, sep2, GLOBSTAR, plTypes, qmark, star, twoStarDot, twoStarNoDot, charSet, reSpecials, addPatternStartSet, filter, ext, defaults, braceExpand, MAX_PATTERN_LENGTH, assertValidPattern, makeRe, match2, globUnescape, globMagic, regExpEscape, Minimatch, init_mjs, entityKind2, hasOwnEntityKind2, init_entity2, _a3, Column2, init_column2, _a22, ColumnBuilder2, init_column_builder2, TableName2, init_table_utils2, _a32, ForeignKeyBuilder2, _a4, ForeignKey2, init_foreign_keys2, init_tracing_utils2, _a5, UniqueConstraintBuilder, _a6, UniqueOnConstraintBuilder, _a7, UniqueConstraint, init_unique_constraint2, init_array2, _a8, _b, PgColumnBuilder2, _a9, _b2, PgColumn2, _a10, _b3, ExtraConfigColumn2, _a11, IndexedColumn2, _a12, _b4, PgArrayBuilder2, _a13, _b5, _PgArray, PgArray2, init_common22, _a14, _b6, PgEnumObjectColumnBuilder2, _a15, _b7, PgEnumObjectColumn2, isPgEnumSym2, _a16, _b8, PgEnumColumnBuilder2, _a17, _b9, PgEnumColumn2, init_enum2, _a18, Subquery2, _a19, _b10, WithSubquery2, init_subquery2, version4, init_version2, otel2, rawTracer2, tracer2, init_tracing2, ViewBaseConfig2, init_view_common3, Schema2, Columns2, ExtraConfigColumns2, OriginalName2, BaseName2, IsAlias2, ExtraConfigBuilder2, IsDrizzleTable2, _a20, _b11, _c, _d, _e3, _f, _g, _h, _i, _j, Table2, init_table8, _a21, FakePrimitiveParam, _a222, StringChunk2, _a23, _SQL, SQL2, _a24, Name2, noopDecoder2, noopEncoder2, noopMapper2, _a25, Param2, _a26, Placeholder2, IsDrizzleView2, _a27, _b12, _c2, View3, init_sql3, _a28, ColumnAliasProxyHandler2, _a29, TableAliasProxyHandler2, _a30, RelationTableAliasProxyHandler, init_alias3, _a31, _b13, DrizzleError2, DrizzleQueryError, _a322, _b14, TransactionRollbackError2, init_errors22, _a33, ConsoleLogWriter2, _a34, DefaultLogger2, _a35, NoopLogger2, init_logger3, init_operations, _a36, _b15, QueryPromise2, init_query_promise2, textDecoder, init_utils22, _a37, _b16, PgIntColumnBaseBuilder2, init_int_common2, _a38, _b17, PgBigInt53Builder2, _a39, _b18, PgBigInt532, _a40, _b19, PgBigInt64Builder2, _a41, _b20, PgBigInt642, init_bigint2, _a42, _b21, PgBigSerial53Builder2, _a43, _b22, PgBigSerial532, _a44, _b23, PgBigSerial64Builder2, _a45, _b24, PgBigSerial642, init_bigserial2, _a46, _b25, PgBooleanBuilder2, _a47, _b26, PgBoolean2, init_boolean2, _a48, _b27, PgCharBuilder2, _a49, _b28, PgChar2, init_char2, _a50, _b29, PgCidrBuilder2, _a51, _b30, PgCidr2, init_cidr2, _a52, _b31, PgCustomColumnBuilder2, _a53, _b32, PgCustomColumn2, init_custom2, _a54, _b33, PgDateColumnBaseBuilder2, init_date_common2, _a55, _b34, PgDateBuilder2, _a56, _b35, PgDate2, _a57, _b36, PgDateStringBuilder2, _a58, _b37, PgDateString2, init_date2, _a59, _b38, PgDoublePrecisionBuilder2, _a60, _b39, PgDoublePrecision2, init_double_precision2, _a61, _b40, PgInetBuilder2, _a62, _b41, PgInet2, init_inet2, _a63, _b42, PgIntegerBuilder2, _a64, _b43, PgInteger2, init_integer2, _a65, _b44, PgIntervalBuilder2, _a66, _b45, PgInterval2, init_interval2, _a67, _b46, PgJsonBuilder2, _a68, _b47, PgJson2, init_json2, _a69, _b48, PgJsonbBuilder2, _a70, _b49, PgJsonb2, init_jsonb2, _a71, _b50, PgLineBuilder2, _a72, _b51, PgLineTuple2, _a73, _b52, PgLineABCBuilder2, _a74, _b53, PgLineABC2, init_line2, _a75, _b54, PgMacaddrBuilder2, _a76, _b55, PgMacaddr2, init_macaddr2, _a77, _b56, PgMacaddr8Builder2, _a78, _b57, PgMacaddr82, init_macaddr82, _a79, _b58, PgNumericBuilder2, _a80, _b59, PgNumeric2, _a81, _b60, PgNumericNumberBuilder2, _a82, _b61, PgNumericNumber2, _a83, _b62, PgNumericBigIntBuilder2, _a84, _b63, PgNumericBigInt2, init_numeric2, _a85, _b64, PgPointTupleBuilder2, _a86, _b65, PgPointTuple2, _a87, _b66, PgPointObjectBuilder2, _a88, _b67, PgPointObject2, init_point2, init_utils32, _a89, _b68, PgGeometryBuilder2, _a90, _b69, PgGeometry2, _a91, _b70, PgGeometryObjectBuilder2, _a92, _b71, PgGeometryObject2, init_geometry2, _a93, _b72, PgRealBuilder2, _a94, _b73, PgReal2, init_real2, _a95, _b74, PgSerialBuilder2, _a96, _b75, PgSerial2, init_serial2, _a97, _b76, PgSmallIntBuilder2, _a98, _b77, PgSmallInt2, init_smallint2, _a99, _b78, PgSmallSerialBuilder2, _a100, _b79, PgSmallSerial2, init_smallserial2, _a101, _b80, PgTextBuilder2, _a102, _b81, PgText2, init_text2, _a103, _b82, PgTimeBuilder2, _a104, _b83, PgTime2, init_time2, _a105, _b84, PgTimestampBuilder2, _a106, _b85, PgTimestamp2, _a107, _b86, PgTimestampStringBuilder2, _a108, _b87, PgTimestampString2, init_timestamp2, _a109, _b88, PgUUIDBuilder2, _a110, _b89, PgUUID2, init_uuid3, _a111, _b90, PgVarcharBuilder2, _a112, _b91, PgVarchar2, init_varchar2, _a113, _b92, PgBinaryVectorBuilder2, _a114, _b93, PgBinaryVector2, init_bit2, _a115, _b94, PgHalfVectorBuilder2, _a116, _b95, PgHalfVector2, init_halfvec2, _a117, _b96, PgSparseVectorBuilder2, _a118, _b97, PgSparseVector2, init_sparsevec2, _a119, _b98, PgVectorBuilder2, _a120, _b99, PgVector2, init_vector3, init_all2, InlineForeignKeys2, EnableRLS2, _a121, _b100, _c3, _d2, _e22, _f2, PgTable2, pgTable2, init_table22, _a122, PrimaryKeyBuilder2, _a123, PrimaryKey2, init_primary_keys2, eq2, ne4, gt3, gte2, lt4, lte2, init_conditions2, init_select3, init_expressions2, _a124, Relation2, _a125, Relations2, _a126, _b101, _One, One2, _a127, _b102, _Many, Many2, init_relations2, init_aggregate2, init_vector22, init_functions2, init_sql22, dist_exports, init_dist9, init_alias22, _a128, CheckBuilder, _a129, Check, init_checks6, init_columns2, _a130, _SelectionProxyHandler, SelectionProxyHandler2, init_selection_proxy2, _a131, IndexBuilderOn2, _a132, IndexBuilder2, _a133, Index2, init_indexes2, _a134, PgPolicy, init_policies2, PgViewConfig2, init_view_common22, _a135, CasingCache2, init_casing2, _a136, _b103, PgViewBase2, init_view_base2, _a137, PgDialect2, init_dialect2, _a138, TypedQueryBuilder2, init_query_builder3, _a139, PgSelectBuilder2, _a140, _b104, PgSelectQueryBuilderBase2, _a141, _b105, PgSelectBase2, getPgSetOperators2, union22, unionAll2, intersect2, intersectAll2, except2, exceptAll2, init_select22, _a142, QueryBuilder2, init_query_builder22, _a143, DefaultViewBuilderCore, _a144, _b106, ViewBuilder, _a145, _b107, ManualViewBuilder, _a146, MaterializedViewBuilderCore, _a147, _b108, MaterializedViewBuilder, _a148, _b109, ManualMaterializedViewBuilder, _a149, _b110, _c4, PgView2, PgMaterializedViewConfig2, _a150, _b111, _c5, PgMaterializedView, init_view2, init_utils42, _a151, _b112, PgDeleteBase2, init_delete2, _a152, PgInsertBuilder2, _a153, _b113, PgInsertBase2, init_insert2, _a154, _b114, PgRefreshMaterializedView2, init_refresh_materialized_view2, init_select_types, _a155, PgUpdateBuilder2, _a156, _b115, PgUpdateBase2, init_update2, init_query_builders2, _a157, _b116, _c6, _PgCountBuilder, PgCountBuilder2, init_count2, _a158, RelationalQueryBuilder2, _a159, _b117, PgRelationalQuery2, init_query2, _a160, _b118, PgRaw2, init_raw2, _a161, PgDatabase2, init_db2, _a162, PgRole, init_roles2, _a163, PgSequence, init_sequence2, _a164, PgSchema5, init_schema3, _a165, Cache, _a166, _b119, NoopCache, init_cache, _a167, PgPreparedQuery2, _a168, PgSession2, _a169, _b120, PgTransaction2, init_session3, init_subquery22, init_utils52, init_pg_core2, vectorOps, init_vector32, sqlToStr, init_utils62, indexName, generatePgSnapshot, trimChar, fromDatabase, defaultForColumn, getColumnsInfoQuery, init_pgSerializer, import_hanji4, Select, init_selector_ui, init_alias32, _a170, CheckBuilder2, _a171, Check2, init_checks22, _a172, ForeignKeyBuilder22, _a173, ForeignKey22, init_foreign_keys22, _a174, UniqueConstraintBuilder2, _a175, UniqueOnConstraintBuilder2, _a176, UniqueConstraint2, init_unique_constraint22, _a177, _b121, SQLiteColumnBuilder, _a178, _b122, SQLiteColumn, init_common3, _a179, _b123, SQLiteBigIntBuilder, _a180, _b124, SQLiteBigInt, _a181, _b125, SQLiteBlobJsonBuilder, _a182, _b126, SQLiteBlobJson, _a183, _b127, SQLiteBlobBufferBuilder, _a184, _b128, SQLiteBlobBuffer, init_blob, _a185, _b129, SQLiteCustomColumnBuilder, _a186, _b130, SQLiteCustomColumn, init_custom22, _a187, _b131, SQLiteBaseIntegerBuilder, _a188, _b132, SQLiteBaseInteger, _a189, _b133, SQLiteIntegerBuilder, _a190, _b134, SQLiteInteger, _a191, _b135, SQLiteTimestampBuilder, _a192, _b136, SQLiteTimestamp, _a193, _b137, SQLiteBooleanBuilder, _a194, _b138, SQLiteBoolean, init_integer22, _a195, _b139, SQLiteNumericBuilder, _a196, _b140, SQLiteNumeric, _a197, _b141, SQLiteNumericNumberBuilder, _a198, _b142, SQLiteNumericNumber, _a199, _b143, SQLiteNumericBigIntBuilder, _a200, _b144, SQLiteNumericBigInt, init_numeric22, _a201, _b145, SQLiteRealBuilder, _a202, _b146, SQLiteReal, init_real22, _a203, _b147, SQLiteTextBuilder, _a204, _b148, SQLiteText, _a205, _b149, SQLiteTextJsonBuilder, _a206, _b150, SQLiteTextJson, init_text22, init_columns22, init_all22, InlineForeignKeys22, _a207, _b151, _c7, _d3, _e32, SQLiteTable, sqliteTable, init_table32, _a208, IndexBuilderOn22, _a209, IndexBuilder22, _a210, Index4, init_indexes22, _a211, PrimaryKeyBuilder22, _a212, PrimaryKey22, init_primary_keys22, init_utils72, _a213, _b152, SQLiteDeleteBase, init_delete22, _a214, _b153, SQLiteViewBase, init_view_base22, _a215, SQLiteDialect, _a216, _b154, SQLiteSyncDialect, _a217, _b155, SQLiteAsyncDialect, init_dialect22, _a218, SQLiteSelectBuilder, _a219, _b156, SQLiteSelectQueryBuilderBase, _a220, _b157, SQLiteSelectBase, getSQLiteSetOperators, union32, unionAll22, intersect22, except22, init_select32, _a221, QueryBuilder22, init_query_builder32, _a2222, SQLiteInsertBuilder, _a223, _b158, SQLiteInsertBase, init_insert22, init_select_types2, _a224, SQLiteUpdateBuilder, _a225, _b159, SQLiteUpdateBase, init_update22, init_query_builders22, _a226, _b160, _c8, _SQLiteCountBuilder, SQLiteCountBuilder, init_count22, _a227, RelationalQueryBuilder22, _a228, _b161, SQLiteRelationalQuery, _a229, _b162, SQLiteSyncRelationalQuery, init_query22, _a230, _b163, SQLiteRaw, init_raw22, _a231, BaseSQLiteDatabase, init_db22, _a232, _b164, ExecuteResultSync, _a233, SQLitePreparedQuery, _a234, SQLiteSession, _a235, _b165, SQLiteTransaction, init_session22, init_subquery3, _a236, ViewBuilderCore, _a237, _b166, ViewBuilder2, _a238, _b167, ManualViewBuilder2, _a239, _b168, SQLiteView2, init_view22, init_sqlite_core, generateSqliteSnapshot, fromDatabase2, init_sqliteSerializer, getTablesFilterByExtensions, init_getTablesFilterByExtensions, init_alias4, _a240, CheckBuilder3, _a241, Check3, init_checks32, _a242, ForeignKeyBuilder3, _a243, ForeignKey3, init_foreign_keys3, _a244, UniqueConstraintBuilder3, _a245, UniqueOnConstraintBuilder3, _a246, UniqueConstraint3, init_unique_constraint3, _a247, _b169, MySqlColumnBuilder, _a248, _b170, MySqlColumn, _a249, _b171, MySqlColumnBuilderWithAutoIncrement, _a250, _b172, MySqlColumnWithAutoIncrement, init_common4, _a251, _b173, MySqlBigInt53Builder, _a252, _b174, MySqlBigInt53, _a253, _b175, MySqlBigInt64Builder, _a254, _b176, MySqlBigInt64, init_bigint22, _a255, _b177, MySqlBinaryBuilder, _a256, _b178, MySqlBinary, init_binary, _a257, _b179, MySqlBooleanBuilder, _a258, _b180, MySqlBoolean, init_boolean22, _a259, _b181, MySqlCharBuilder, _a260, _b182, MySqlChar, init_char22, _a261, _b183, MySqlCustomColumnBuilder, _a262, _b184, MySqlCustomColumn, init_custom3, _a263, _b185, MySqlDateBuilder, _a264, _b186, MySqlDate, _a265, _b187, MySqlDateStringBuilder, _a266, _b188, MySqlDateString, init_date22, _a267, _b189, MySqlDateTimeBuilder, _a268, _b190, MySqlDateTime, _a269, _b191, MySqlDateTimeStringBuilder, _a270, _b192, MySqlDateTimeString, init_datetime, _a271, _b193, MySqlDecimalBuilder, _a272, _b194, MySqlDecimal, _a273, _b195, MySqlDecimalNumberBuilder, _a274, _b196, MySqlDecimalNumber, _a275, _b197, MySqlDecimalBigIntBuilder, _a276, _b198, MySqlDecimalBigInt, init_decimal, _a277, _b199, MySqlDoubleBuilder, _a278, _b200, MySqlDouble, init_double, _a279, _b201, MySqlEnumColumnBuilder, _a280, _b202, MySqlEnumColumn, _a281, _b203, MySqlEnumObjectColumnBuilder, _a282, _b204, MySqlEnumObjectColumn, init_enum22, _a283, _b205, MySqlFloatBuilder, _a284, _b206, MySqlFloat, init_float, _a285, _b207, MySqlIntBuilder, _a286, _b208, MySqlInt, init_int, _a287, _b209, MySqlJsonBuilder, _a288, _b210, MySqlJson, init_json22, _a289, _b211, MySqlMediumIntBuilder, _a290, _b212, MySqlMediumInt, init_mediumint, _a291, _b213, MySqlRealBuilder, _a292, _b214, MySqlReal, init_real3, _a293, _b215, MySqlSerialBuilder, _a294, _b216, MySqlSerial, init_serial22, _a295, _b217, MySqlSmallIntBuilder, _a296, _b218, MySqlSmallInt, init_smallint22, _a297, _b219, MySqlTextBuilder, _a298, _b220, MySqlText, init_text3, _a299, _b221, MySqlTimeBuilder, _a300, _b222, MySqlTime, init_time22, _a301, _b223, MySqlDateColumnBaseBuilder, _a302, _b224, MySqlDateBaseColumn, init_date_common22, _a303, _b225, MySqlTimestampBuilder, _a304, _b226, MySqlTimestamp, _a305, _b227, MySqlTimestampStringBuilder, _a306, _b228, MySqlTimestampString, init_timestamp22, _a307, _b229, MySqlTinyIntBuilder, _a308, _b230, MySqlTinyInt, init_tinyint, _a309, _b231, MySqlVarBinaryBuilder, _a310, _b232, MySqlVarBinary, init_varbinary, _a311, _b233, MySqlVarCharBuilder, _a312, _b234, MySqlVarChar, init_varchar22, _a313, _b235, MySqlYearBuilder, _a314, _b236, MySqlYear, init_year, init_columns3, _a315, _b237, _c9, _MySqlCountBuilder, MySqlCountBuilder, init_count3, _a316, IndexBuilderOn3, _a317, IndexBuilder3, _a318, Index5, init_indexes3, init_all3, InlineForeignKeys3, _a319, _b238, _c10, _d4, _e4, MySqlTable, mysqlTable, init_table42, _a320, PrimaryKeyBuilder3, _a321, PrimaryKey3, init_primary_keys3, MySqlViewConfig, init_view_common32, init_utils8, _a3222, _b239, MySqlDeleteBase, init_delete3, _a323, _b240, MySqlViewBase, init_view_base3, _a324, MySqlDialect, init_dialect3, _a325, MySqlSelectBuilder, _a326, _b241, MySqlSelectQueryBuilderBase, _a327, _b242, MySqlSelectBase, getMySqlSetOperators, union4, unionAll3, intersect3, intersectAll22, except3, exceptAll22, init_select4, _a328, QueryBuilder3, init_query_builder4, _a329, MySqlInsertBuilder, _a330, _b243, MySqlInsertBase, init_insert3, init_select_types3, _a331, MySqlUpdateBuilder, _a332, _b244, MySqlUpdateBase, init_update3, init_query_builders3, _a333, RelationalQueryBuilder3, _a334, _b245, MySqlRelationalQuery, init_query3, _a335, MySqlDatabase, init_db3, _a336, ViewBuilderCore2, _a337, _b246, ViewBuilder3, _a338, _b247, ManualViewBuilder3, _a339, _b248, _c11, MySqlView2, init_view3, _a340, MySqlSchema5, init_schema22, _a341, MySqlPreparedQuery, _a342, MySqlSession, _a343, _b249, MySqlTransaction, init_session32, init_subquery4, init_mysql_core, handleEnumType, generateMySqlSnapshot, fromDatabase3, init_mysqlSerializer, cliConfigGenerate, pushParams, pullParams, configCheck, cliConfigCheck, init_cli, gelCredentials, init_gel, libSQLCredentials, init_libsql, mysqlCredentials, init_mysql, postgresCredentials, init_postgres, singlestoreCredentials, init_singlestore, sqliteCredentials, init_sqlite, credentials, studioCliParams, studioConfig, init_studio, es5_exports, _3, es5_default, init_es5, import_hanji7, assertES5, safeRegister, migrateConfig, init_utils9, prepareFromExports, init_pgImports, init_alias5, _a344, UniqueConstraintBuilder4, _a345, UniqueOnConstraintBuilder4, _a346, UniqueConstraint4, init_unique_constraint4, _a347, _b250, SingleStoreColumnBuilder, _a348, _b251, SingleStoreColumn, _a349, _b252, SingleStoreColumnBuilderWithAutoIncrement, _a350, _b253, SingleStoreColumnWithAutoIncrement, init_common5, _a351, _b254, SingleStoreBigInt53Builder, _a352, _b255, SingleStoreBigInt53, _a353, _b256, SingleStoreBigInt64Builder, _a354, _b257, SingleStoreBigInt64, init_bigint3, _a355, _b258, SingleStoreBinaryBuilder, _a356, _b259, SingleStoreBinary, init_binary2, _a357, _b260, SingleStoreBooleanBuilder, _a358, _b261, SingleStoreBoolean, init_boolean3, _a359, _b262, SingleStoreCharBuilder, _a360, _b263, SingleStoreChar, init_char3, _a361, _b264, SingleStoreCustomColumnBuilder, _a362, _b265, SingleStoreCustomColumn, init_custom4, _a363, _b266, SingleStoreDateBuilder, _a364, _b267, SingleStoreDate, _a365, _b268, SingleStoreDateStringBuilder, _a366, _b269, SingleStoreDateString, init_date3, _a367, _b270, SingleStoreDateTimeBuilder, _a368, _b271, SingleStoreDateTime, _a369, _b272, SingleStoreDateTimeStringBuilder, _a370, _b273, SingleStoreDateTimeString, init_datetime2, _a371, _b274, SingleStoreDecimalBuilder, _a372, _b275, SingleStoreDecimal, _a373, _b276, SingleStoreDecimalNumberBuilder, _a374, _b277, SingleStoreDecimalNumber, _a375, _b278, SingleStoreDecimalBigIntBuilder, _a376, _b279, SingleStoreDecimalBigInt, init_decimal2, _a377, _b280, SingleStoreDoubleBuilder, _a378, _b281, SingleStoreDouble, init_double2, _a379, _b282, SingleStoreEnumColumnBuilder, _a380, _b283, SingleStoreEnumColumn, init_enum3, _a381, _b284, SingleStoreFloatBuilder, _a382, _b285, SingleStoreFloat, init_float2, _a383, _b286, SingleStoreIntBuilder, _a384, _b287, SingleStoreInt, init_int2, _a385, _b288, SingleStoreJsonBuilder, _a386, _b289, SingleStoreJson, init_json3, _a387, _b290, SingleStoreMediumIntBuilder, _a388, _b291, SingleStoreMediumInt, init_mediumint2, _a389, _b292, SingleStoreRealBuilder, _a390, _b293, SingleStoreReal, init_real4, _a391, _b294, SingleStoreSerialBuilder, _a392, _b295, SingleStoreSerial, init_serial3, _a393, _b296, SingleStoreSmallIntBuilder, _a394, _b297, SingleStoreSmallInt, init_smallint3, _a395, _b298, SingleStoreTextBuilder, _a396, _b299, SingleStoreText, init_text4, _a397, _b300, SingleStoreTimeBuilder, _a398, _b301, SingleStoreTime, init_time3, _a399, _b302, SingleStoreDateColumnBaseBuilder, _a400, _b303, SingleStoreDateBaseColumn, init_date_common3, _a401, _b304, SingleStoreTimestampBuilder, _a402, _b305, SingleStoreTimestamp, _a403, _b306, SingleStoreTimestampStringBuilder, _a404, _b307, SingleStoreTimestampString, init_timestamp3, _a405, _b308, SingleStoreTinyIntBuilder, _a406, _b309, SingleStoreTinyInt, init_tinyint2, _a407, _b310, SingleStoreVarBinaryBuilder, _a408, _b311, SingleStoreVarBinary, init_varbinary2, _a409, _b312, SingleStoreVarCharBuilder, _a410, _b313, SingleStoreVarChar, init_varchar3, _a411, _b314, SingleStoreVectorBuilder, _a412, _b315, SingleStoreVector, init_vector4, _a413, _b316, SingleStoreYearBuilder, _a414, _b317, SingleStoreYear, init_year2, init_columns4, _a415, _b318, _c12, _SingleStoreCountBuilder, SingleStoreCountBuilder, init_count4, _a416, IndexBuilderOn4, _a417, IndexBuilder4, _a418, Index6, init_indexes4, init_all4, _a419, _b319, _c13, _d5, SingleStoreTable, init_table52, _a420, PrimaryKeyBuilder4, _a421, PrimaryKey4, init_primary_keys4, init_utils10, _a422, _b320, SingleStoreDeleteBase, init_delete4, _a423, SingleStoreInsertBuilder, _a424, _b321, SingleStoreInsertBase, init_insert4, _a425, SingleStoreDialect, init_dialect4, _a426, SingleStoreSelectBuilder, _a427, _b322, SingleStoreSelectQueryBuilderBase, _a428, _b323, SingleStoreSelectBase, getSingleStoreSetOperators, union5, unionAll4, intersect4, except4, minus, init_select5, _a429, QueryBuilder4, init_query_builder5, init_select_types4, _a430, SingleStoreUpdateBuilder, _a431, _b324, SingleStoreUpdateBase, init_update4, init_query_builders4, _a432, SingleStoreDatabase, init_db4, _a433, SingleStoreSchema5, init_schema32, _a434, SingleStorePreparedQuery, _a435, SingleStoreSession, _a436, _b325, SingleStoreTransaction, init_session4, init_subquery5, init_singlestore_core, dialect5, generateSingleStoreSnapshot, fromDatabase4, init_singlestoreSerializer, sqliteImports_exports, prepareFromExports2, prepareFromSqliteImports, init_sqliteImports, mysqlImports_exports, prepareFromExports3, prepareFromMySqlImports, init_mysqlImports, mysqlPushUtils_exports, import_hanji8, filterStatements, logSuggestionsAndReturn2, init_mysqlPushUtils, mysqlIntrospect_exports, import_hanji9, mysqlPushIntrospect, init_mysqlIntrospect, singlestoreImports_exports, prepareFromExports4, prepareFromSingleStoreImports, init_singlestoreImports, singlestorePushUtils_exports, import_hanji10, filterStatements2, logSuggestionsAndReturn3, init_singlestorePushUtils, singlestoreIntrospect_exports, import_hanji11, singlestorePushIntrospect, init_singlestoreIntrospect, import_hanji3, pgPushIntrospect = async (db2, filters, schemaFilters, entities, tsSchema) => {
100239
101000
  const matchers = filters.map((it3) => {
100240
101001
  return new Minimatch(it3);
100241
101002
  });
@@ -119281,8 +120042,8 @@ ${BREAKPOINT}ALTER TABLE ${tableNameWithSchema} ADD CONSTRAINT "${statement.newC
119281
120042
  win32: { sep: "\\" },
119282
120043
  posix: { sep: "/" }
119283
120044
  };
119284
- sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
119285
- minimatch.sep = sep;
120045
+ sep2 = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
120046
+ minimatch.sep = sep2;
119286
120047
  GLOBSTAR = Symbol("globstar **");
119287
120048
  minimatch.GLOBSTAR = GLOBSTAR;
119288
120049
  plTypes = {
@@ -138184,7 +138945,7 @@ async function seedCoreGames(db2) {
138184
138945
  }
138185
138946
  async function seedCurrentProjectGame(db2, project) {
138186
138947
  const now2 = new Date;
138187
- const desiredGameId = project.gameId?.trim() || undefined;
138948
+ const desiredGameId = project.gameId?.trim() || await localGameId(project.slug);
138188
138949
  try {
138189
138950
  const existingGame = await db2.query.games.findFirst({
138190
138951
  where: (row, operators) => operators.eq(row.slug, project.slug)
@@ -138200,7 +138961,7 @@ async function seedCurrentProjectGame(db2, project) {
138200
138961
  }
138201
138962
  }
138202
138963
  const gameRecord = {
138203
- id: desiredGameId ?? crypto.randomUUID(),
138964
+ id: desiredGameId,
138204
138965
  slug: project.slug,
138205
138966
  displayName: project.displayName,
138206
138967
  version: project.version,
@@ -138235,6 +138996,7 @@ var init_games = __esm(() => {
138235
138996
  init_drizzle_orm();
138236
138997
  init_src();
138237
138998
  init_tables_index();
138999
+ init_uuid2();
138238
139000
  init_constants();
138239
139001
  init_logging();
138240
139002
  init_timeback5();
@@ -138820,19 +139582,50 @@ var init_types6 = () => {};
138820
139582
  function hasGameManagementAccess(user) {
138821
139583
  return user.role === "admin" || user.role === "teacher" || user.role === "developer" && user.developerStatus === "approved";
138822
139584
  }
139585
+ function isDashboardWorkerKey(key) {
139586
+ return Boolean(key?.permissions?.[DASHBOARD_PERMISSION_NAMESPACE]);
139587
+ }
139588
+ function workerKeyGrantsGameRead(key, slug2) {
139589
+ if (!isDashboardWorkerKey(key) && !isGameWorkerKey(key)) {
139590
+ return false;
139591
+ }
139592
+ const games2 = key?.permissions?.games;
139593
+ return Boolean(games2?.includes(`read:${slug2}`) || games2?.includes(`write:${slug2}`));
139594
+ }
139595
+ function isGameWorkerKey(key) {
139596
+ return Boolean(key?.name?.startsWith(GAME_WORKER_KEY_PREFIX));
139597
+ }
139598
+ function deniedWorkerKeyRequest(key, method, pathname) {
139599
+ if (isDashboardWorkerKey(key) && !isAllowedDashboardWorkerRequest(method, pathname, key.permissions)) {
139600
+ return DASHBOARD_WORKER_KEY_RESTRICTED;
139601
+ }
139602
+ return null;
139603
+ }
139604
+ function rejectDashboardWorkerKey(ctx) {
139605
+ if (isDashboardWorkerKey(ctx.apiKey)) {
139606
+ throw ApiError.forbidden(DASHBOARD_WORKER_KEY_RESTRICTED);
139607
+ }
139608
+ }
139609
+ function assertAuthenticatedRequest(ctx) {
139610
+ if (!isAuthenticated(ctx)) {
139611
+ throw ApiError.unauthorized("Valid session or bearer token required");
139612
+ }
139613
+ if (ctx.apiKey) {
139614
+ const denied = deniedWorkerKeyRequest(ctx.apiKey, ctx.request.method, ctx.url.pathname);
139615
+ if (denied) {
139616
+ throw ApiError.forbidden(denied);
139617
+ }
139618
+ }
139619
+ }
138823
139620
  function requireAuth(handler) {
138824
139621
  return async (ctx) => {
138825
- if (!isAuthenticated(ctx)) {
138826
- throw ApiError.unauthorized("Valid session or bearer token required");
138827
- }
139622
+ assertAuthenticatedRequest(ctx);
138828
139623
  return handler(ctx);
138829
139624
  };
138830
139625
  }
138831
139626
  function requireNonAnonymous(handler) {
138832
139627
  return async (ctx) => {
138833
- if (!isAuthenticated(ctx)) {
138834
- throw ApiError.unauthorized("Valid session or bearer token required");
138835
- }
139628
+ assertAuthenticatedRequest(ctx);
138836
139629
  if (ctx.user.isAnonymous) {
138837
139630
  throw ApiError.forbidden("This operation is not available for demo/anonymous users");
138838
139631
  }
@@ -138841,9 +139634,7 @@ function requireNonAnonymous(handler) {
138841
139634
  }
138842
139635
  function requireAnonymous(handler) {
138843
139636
  return async (ctx) => {
138844
- if (!isAuthenticated(ctx)) {
138845
- throw ApiError.unauthorized("Valid session or bearer token required");
138846
- }
139637
+ assertAuthenticatedRequest(ctx);
138847
139638
  if (!ctx.user.isAnonymous) {
138848
139639
  throw ApiError.forbidden("This operation is only available for demo/anonymous users");
138849
139640
  }
@@ -138852,9 +139643,8 @@ function requireAnonymous(handler) {
138852
139643
  }
138853
139644
  function requireAdmin(handler) {
138854
139645
  return async (ctx) => {
138855
- if (!isAuthenticated(ctx)) {
138856
- throw ApiError.unauthorized("Valid session or bearer token required");
138857
- }
139646
+ assertAuthenticatedRequest(ctx);
139647
+ rejectDashboardWorkerKey(ctx);
138858
139648
  if (ctx.user.role !== "admin") {
138859
139649
  throw ApiError.forbidden("Admin access required");
138860
139650
  }
@@ -138863,9 +139653,8 @@ function requireAdmin(handler) {
138863
139653
  }
138864
139654
  function requireDeveloper(handler) {
138865
139655
  return async (ctx) => {
138866
- if (!isAuthenticated(ctx)) {
138867
- throw ApiError.unauthorized("Valid session or bearer token required");
138868
- }
139656
+ assertAuthenticatedRequest(ctx);
139657
+ rejectDashboardWorkerKey(ctx);
138869
139658
  const isAdmin = ctx.user.role === "admin";
138870
139659
  const isApprovedDev = ctx.user.role === "developer" && ctx.user.developerStatus === "approved";
138871
139660
  if (!isAdmin && !isApprovedDev) {
@@ -138874,20 +139663,30 @@ function requireDeveloper(handler) {
138874
139663
  return handler(ctx);
138875
139664
  };
138876
139665
  }
139666
+ function requireHumanDeveloper(handler) {
139667
+ return requireDeveloper(async (ctx) => {
139668
+ if (isDashboardWorkerKey(ctx.apiKey) || isGameWorkerKey(ctx.apiKey)) {
139669
+ throw ApiError.forbidden(WORKER_KEY_HUMAN_ONLY);
139670
+ }
139671
+ return handler(ctx);
139672
+ });
139673
+ }
138877
139674
  function requireGameManagementAccess(handler) {
138878
139675
  return async (ctx) => {
138879
- if (!isAuthenticated(ctx)) {
138880
- throw ApiError.unauthorized("Valid session or bearer token required");
138881
- }
139676
+ assertAuthenticatedRequest(ctx);
139677
+ rejectDashboardWorkerKey(ctx);
138882
139678
  if (!hasGameManagementAccess(ctx.user)) {
138883
139679
  throw ApiError.forbidden("Game management access required");
138884
139680
  }
138885
139681
  return handler(ctx);
138886
139682
  };
138887
139683
  }
139684
+ var WORKER_KEY_HUMAN_ONLY = "This operation requires your own credential — platform worker keys are refused";
138888
139685
  var init_auth_util = __esm(() => {
138889
139686
  init_errors();
138890
139687
  init_types6();
139688
+ init_dashboard_util();
139689
+ init_deployment_util();
138891
139690
  });
138892
139691
 
138893
139692
  // ../api-core/src/utils/controller.util.ts
@@ -139031,6 +139830,21 @@ function requireGameId(gameId) {
139031
139830
  }
139032
139831
  return gameId;
139033
139832
  }
139833
+ function requireSlug(slug2) {
139834
+ if (!slug2) {
139835
+ throw ApiError.badRequest("Missing game slug");
139836
+ }
139837
+ return slug2;
139838
+ }
139839
+ function requireUserId(userId) {
139840
+ if (!userId) {
139841
+ throw ApiError.badRequest("Missing user ID");
139842
+ }
139843
+ if (!isValidUUID(userId)) {
139844
+ throw ApiError.unprocessableEntity("userId must be a valid UUID format");
139845
+ }
139846
+ return userId;
139847
+ }
139034
139848
  var init_params_util = __esm(() => {
139035
139849
  init_src4();
139036
139850
  init_errors();
@@ -139079,6 +139893,7 @@ var init_validation_util = __esm(() => {
139079
139893
  // ../api-core/src/utils/index.ts
139080
139894
  var init_utils11 = __esm(() => {
139081
139895
  init_auth_util();
139896
+ init_dashboard_util();
139082
139897
  init_deployment_util();
139083
139898
  init_leaderboard_util();
139084
139899
  init_lti_util();
@@ -139171,6 +139986,83 @@ var init_bucket_controller = __esm(() => {
139171
139986
  });
139172
139987
  });
139173
139988
 
139989
+ // ../api-core/src/controllers/dashboard.controller.ts
139990
+ function requireDashboardWorkerKey(ctx, slug2) {
139991
+ const scopes = ctx.apiKey?.permissions?.[DASHBOARD_PERMISSION_NAMESPACE];
139992
+ if (!scopes?.includes(dashboardCredentialScope(slug2))) {
139993
+ throw ApiError.forbidden("This endpoint requires the dashboard worker key for this game");
139994
+ }
139995
+ }
139996
+ var listUsers, addUser, issueRecoveryLink, removeUser, remove, verifyLogin, acceptInvite, revokeSessions, acceptReset, dashboard2;
139997
+ var init_dashboard_controller = __esm(() => {
139998
+ init_schemas_index();
139999
+ init_errors();
140000
+ init_utils11();
140001
+ listUsers = requireHumanDeveloper(async (ctx) => {
140002
+ const slug2 = requireSlug(ctx.params.slug);
140003
+ const users2 = await ctx.services.dashboard.listUsers(slug2, ctx.user);
140004
+ return { users: users2 };
140005
+ });
140006
+ addUser = requireHumanDeveloper(async (ctx) => {
140007
+ const slug2 = requireSlug(ctx.params.slug);
140008
+ const body2 = await parseRequestBody(ctx.request, AddGameDashboardUserSchema);
140009
+ return ctx.services.dashboard.addUser(slug2, ctx.user, body2);
140010
+ });
140011
+ issueRecoveryLink = requireHumanDeveloper(async (ctx) => {
140012
+ const slug2 = requireSlug(ctx.params.slug);
140013
+ const userId = requireUserId(ctx.params.userId);
140014
+ return ctx.services.dashboard.issueRecoveryLink(slug2, ctx.user, userId);
140015
+ });
140016
+ removeUser = requireHumanDeveloper(async (ctx) => {
140017
+ const slug2 = requireSlug(ctx.params.slug);
140018
+ const userId = requireUserId(ctx.params.userId);
140019
+ await ctx.services.dashboard.removeUser(slug2, ctx.user, userId);
140020
+ return { success: true };
140021
+ });
140022
+ remove = requireHumanDeveloper(async (ctx) => {
140023
+ const slug2 = requireSlug(ctx.params.slug);
140024
+ await ctx.services.deploy.removeDashboard(slug2, ctx.user);
140025
+ return { success: true };
140026
+ });
140027
+ verifyLogin = requireAuth(async (ctx) => {
140028
+ const slug2 = requireSlug(ctx.params.slug);
140029
+ requireDashboardWorkerKey(ctx, slug2);
140030
+ const body2 = await parseRequestBody(ctx.request, VerifyGameDashboardLoginSchema);
140031
+ const user = await ctx.services.dashboard.verifyLogin(slug2, body2);
140032
+ return { user };
140033
+ });
140034
+ acceptInvite = requireAuth(async (ctx) => {
140035
+ const slug2 = requireSlug(ctx.params.slug);
140036
+ requireDashboardWorkerKey(ctx, slug2);
140037
+ const body2 = await parseRequestBody(ctx.request, AcceptGameDashboardInviteSchema);
140038
+ const user = await ctx.services.dashboard.acceptInvite(slug2, body2);
140039
+ return { user };
140040
+ });
140041
+ revokeSessions = requireHumanDeveloper(async (ctx) => {
140042
+ const slug2 = requireSlug(ctx.params.slug);
140043
+ await ctx.services.deploy.rotateDashboardSessionSecret(slug2, ctx.user);
140044
+ return { success: true };
140045
+ });
140046
+ acceptReset = requireAuth(async (ctx) => {
140047
+ const slug2 = requireSlug(ctx.params.slug);
140048
+ requireDashboardWorkerKey(ctx, slug2);
140049
+ const body2 = await parseRequestBody(ctx.request, AcceptGameDashboardResetSchema);
140050
+ const user = await ctx.services.dashboard.acceptReset(slug2, body2);
140051
+ return { user };
140052
+ });
140053
+ dashboard2 = defineControllerNames("dashboard", {
140054
+ listUsers,
140055
+ addUser,
140056
+ removeUser,
140057
+ remove,
140058
+ issueRecoveryLink,
140059
+ revokeSessions,
140060
+ verifyLogin,
140061
+ acceptInvite,
140062
+ acceptReset
140063
+ });
140064
+ });
140065
+
139174
140066
  // ../api-core/src/controllers/database.controller.ts
139175
140067
  var reset, database;
139176
140068
  var init_database_controller = __esm(() => {
@@ -139265,7 +140157,7 @@ var init_developer_controller = __esm(() => {
139265
140157
  });
139266
140158
 
139267
140159
  // ../api-core/src/controllers/domain.controller.ts
139268
- var add, list, getStatus2, remove, domains2;
140160
+ var add, list, getStatus2, remove2, domains2;
139269
140161
  var init_domain_controller = __esm(() => {
139270
140162
  init_esm();
139271
140163
  init_schemas_index();
@@ -139314,7 +140206,7 @@ var init_domain_controller = __esm(() => {
139314
140206
  const environment = getPlatformEnvironment(ctx.config);
139315
140207
  return ctx.services.domain.getStatus(slug2, hostname4, environment, ctx.user, refresh);
139316
140208
  });
139317
- remove = requireDeveloper(async (ctx) => {
140209
+ remove2 = requireDeveloper(async (ctx) => {
139318
140210
  const slug2 = ctx.params.slug;
139319
140211
  const hostname4 = ctx.params.hostname;
139320
140212
  if (!slug2) {
@@ -139330,12 +140222,12 @@ var init_domain_controller = __esm(() => {
139330
140222
  add,
139331
140223
  list,
139332
140224
  getStatus: getStatus2,
139333
- remove
140225
+ remove: remove2
139334
140226
  });
139335
140227
  });
139336
140228
 
139337
140229
  // ../api-core/src/controllers/game-member.controller.ts
139338
- function requireUserId(userId) {
140230
+ function requireUserId2(userId) {
139339
140231
  if (!userId) {
139340
140232
  throw ApiError.badRequest("Missing user ID");
139341
140233
  }
@@ -139357,13 +140249,13 @@ var init_game_member_controller = __esm(() => {
139357
140249
  });
139358
140250
  updateMemberRole = requireNonAnonymous(async (ctx) => {
139359
140251
  const gameId = requireGameId(ctx.params.gameId);
139360
- const userId = requireUserId(ctx.params.userId);
140252
+ const userId = requireUserId2(ctx.params.userId);
139361
140253
  const body2 = await parseRequestBody(ctx.request, UpdateGameMemberRoleSchema);
139362
140254
  return ctx.services.gameMember.updateRole(gameId, userId, ctx.user, body2);
139363
140255
  });
139364
140256
  removeMember = requireNonAnonymous(async (ctx) => {
139365
140257
  const gameId = requireGameId(ctx.params.gameId);
139366
- const userId = requireUserId(ctx.params.userId);
140258
+ const userId = requireUserId2(ctx.params.userId);
139367
140259
  await ctx.services.gameMember.remove(gameId, userId, ctx.user);
139368
140260
  });
139369
140261
  searchUsersForMember = requireNonAnonymous(async (ctx) => {
@@ -139381,12 +140273,13 @@ var init_game_member_controller = __esm(() => {
139381
140273
  });
139382
140274
 
139383
140275
  // ../api-core/src/controllers/game.controller.ts
139384
- var list2, listAccessible, getSubjects, getTimebackSummaries, getById, getBySlug, getManifest, upsertBySlug, patchGameMetadata, remove2, games2;
140276
+ var list2, listAccessible, getSubjects, getTimebackSummaries, getById, getBySlug, getManifest, upsertBySlug, patchGameMetadata, remove3, games2;
139385
140277
  var init_game_controller = __esm(() => {
139386
140278
  init_esm();
139387
140279
  init_schemas_index();
139388
140280
  init_errors();
139389
140281
  init_utils11();
140282
+ init_auth_util();
139390
140283
  list2 = requireNonAnonymous(async (ctx) => ctx.services.game.list(ctx.user));
139391
140284
  listAccessible = requireNonAnonymous(async (ctx) => ctx.services.game.listAccessible(ctx.user));
139392
140285
  getSubjects = requireNonAnonymous(async (ctx) => ctx.services.game.getSubjects());
@@ -139400,7 +140293,9 @@ var init_game_controller = __esm(() => {
139400
140293
  if (!slug2) {
139401
140294
  throw ApiError.badRequest("Missing game slug");
139402
140295
  }
139403
- return ctx.services.game.getBySlug(slug2, ctx.user);
140296
+ return ctx.services.game.getBySlug(slug2, ctx.user, {
140297
+ scopeAuthorized: workerKeyGrantsGameRead(ctx.apiKey, slug2)
140298
+ });
139404
140299
  });
139405
140300
  getManifest = requireNonAnonymous(async (ctx) => {
139406
140301
  const gameId = requireGameId(ctx.params.gameId);
@@ -139430,7 +140325,7 @@ var init_game_controller = __esm(() => {
139430
140325
  const body2 = await parseRequestBody(ctx.request, PatchGameMetadataSchema);
139431
140326
  return ctx.services.game.updateMetadata(gameId, body2, ctx.user);
139432
140327
  });
139433
- remove2 = requireNonAnonymous(async (ctx) => {
140328
+ remove3 = requireNonAnonymous(async (ctx) => {
139434
140329
  const gameId = requireGameId(ctx.params.gameId);
139435
140330
  await ctx.services.game.delete(gameId, ctx.user);
139436
140331
  });
@@ -139444,7 +140339,7 @@ var init_game_controller = __esm(() => {
139444
140339
  getBySlug,
139445
140340
  upsertBySlug,
139446
140341
  patchGameMetadata,
139447
- remove: remove2
140342
+ remove: remove3
139448
140343
  });
139449
140344
  });
139450
140345
 
@@ -140598,6 +141493,7 @@ var init_verify_controller = __esm(() => {
140598
141493
  // ../api-core/src/controllers/index.ts
140599
141494
  var init_controllers = __esm(() => {
140600
141495
  init_bucket_controller();
141496
+ init_dashboard_controller();
140601
141497
  init_database_controller();
140602
141498
  init_deploy_controller();
140603
141499
  init_developer_controller();
@@ -143746,7 +144642,7 @@ import { resolve as resolve3 } from "node:path";
143746
144642
 
143747
144643
  // ../utils/src/file-loader.ts
143748
144644
  import { existsSync as existsSync3, readdirSync, statSync } from "fs";
143749
- import { readFile } from "fs/promises";
144645
+ import { readFile as readFile2 } from "fs/promises";
143750
144646
  import { dirname as dirname2, parse as parse9, resolve as resolve2 } from "path";
143751
144647
  function findFilePath(filename, startDir, maxLevels = 3) {
143752
144648
  const filenames = Array.isArray(filename) ? filename : [filename];
@@ -143815,7 +144711,7 @@ async function loadFile(filename, options = {}) {
143815
144711
  return null;
143816
144712
  }
143817
144713
  try {
143818
- let content = await readFile(fileResult.path, "utf8");
144714
+ let content = await readFile2(fileResult.path, "utf8");
143819
144715
  if (parseJson) {
143820
144716
  if (stripComments) {
143821
144717
  content = stripJsonComments(content);
@@ -144044,6 +144940,9 @@ program2.name("playcademy-sandbox").description("Local development server for Pl
144044
144940
  process.on("SIGTERM", cleanup);
144045
144941
  } catch (error89) {
144046
144942
  console.error(`❌ Failed to start sandbox: ${error89}`);
144943
+ if (error89 instanceof Error && error89.message.includes("already in use")) {
144944
+ console.error("Stop the other server or specify a different port with --port <number>.");
144945
+ }
144047
144946
  process.exit(1);
144048
144947
  }
144049
144948
  });