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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -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.6",
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),
@@ -5661,6 +5689,14 @@ var init_schema = __esm(() => {
5661
5689
  platformServiceJwt: platformServiceJwtConfigSchema.optional(),
5662
5690
  uploadBucket: exports_external.string().optional(),
5663
5691
  queueIngressSecret: exports_external.string().optional()
5692
+ }).superRefine((config2, ctx) => {
5693
+ if (config2.isLocal && !config2.baseUrl) {
5694
+ ctx.addIssue({
5695
+ code: exports_external.ZodIssueCode.custom,
5696
+ path: ["baseUrl"],
5697
+ message: "baseUrl is required when isLocal is true"
5698
+ });
5699
+ }
5664
5700
  });
5665
5701
  });
5666
5702
 
@@ -11181,7 +11217,7 @@ var init_table4 = __esm(() => {
11181
11217
  });
11182
11218
 
11183
11219
  // ../data/src/domains/game/table.ts
11184
- var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
11220
+ var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, gameDashboardUserRoleEnum, gameDashboardUsers, gameDashboardUsersRelations, deploymentProviderEnum, deploymentTargetEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
11185
11221
  var init_table5 = __esm(() => {
11186
11222
  init_drizzle_orm();
11187
11223
  init_pg_core();
@@ -11225,7 +11261,37 @@ var init_table5 = __esm(() => {
11225
11261
  references: [users.id]
11226
11262
  })
11227
11263
  }));
11264
+ gameDashboardUserRoleEnum = pgEnum("game_dashboard_user_role", [
11265
+ DASHBOARD_USER_ROLES.ADMIN,
11266
+ DASHBOARD_USER_ROLES.VIEWER
11267
+ ]);
11268
+ gameDashboardUsers = pgTable("game_dashboard_users", {
11269
+ id: uuid("id").primaryKey().defaultRandom(),
11270
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
11271
+ email: text("email").notNull(),
11272
+ displayName: text("display_name"),
11273
+ role: gameDashboardUserRoleEnum("role").notNull().default(DASHBOARD_USER_ROLES.VIEWER),
11274
+ passwordHash: text("password_hash"),
11275
+ inviteTokenHash: text("invite_token_hash"),
11276
+ inviteExpiresAt: timestamp("invite_expires_at", { withTimezone: true }),
11277
+ resetTokenHash: text("reset_token_hash"),
11278
+ resetExpiresAt: timestamp("reset_expires_at", { withTimezone: true }),
11279
+ failedLoginAttempts: integer("failed_login_attempts").notNull().default(0),
11280
+ lastFailedLoginAt: timestamp("last_failed_login_at", { withTimezone: true }),
11281
+ platformUserId: text("platform_user_id").references(() => users.id, {
11282
+ onDelete: "set null"
11283
+ }),
11284
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
11285
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
11286
+ }, (table3) => [uniqueIndex("game_dashboard_users_game_email_idx").on(table3.gameId, table3.email)]);
11287
+ gameDashboardUsersRelations = relations(gameDashboardUsers, ({ one }) => ({
11288
+ game: one(games, {
11289
+ fields: [gameDashboardUsers.gameId],
11290
+ references: [games.id]
11291
+ })
11292
+ }));
11228
11293
  deploymentProviderEnum = pgEnum("deployment_provider", ["cloudflare", "aws"]);
11294
+ deploymentTargetEnum = pgEnum("deployment_target", ["game", "dashboard"]);
11229
11295
  deployJobStatusEnum = pgEnum("deploy_job_status", [
11230
11296
  "pending",
11231
11297
  "running",
@@ -11237,6 +11303,7 @@ var init_table5 = __esm(() => {
11237
11303
  gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
11238
11304
  deploymentId: text("deployment_id").notNull(),
11239
11305
  provider: deploymentProviderEnum("provider").notNull(),
11306
+ target: deploymentTargetEnum("target").notNull().default("game"),
11240
11307
  url: text("url").notNull(),
11241
11308
  codeHash: text("code_hash"),
11242
11309
  isActive: boolean("is_active").notNull().default(false),
@@ -11424,8 +11491,12 @@ __export(exports_tables_index, {
11424
11491
  gameMemberRoleEnum: () => gameMemberRoleEnum,
11425
11492
  gameDeployments: () => gameDeployments,
11426
11493
  gameDeployJobs: () => gameDeployJobs,
11494
+ gameDashboardUsersRelations: () => gameDashboardUsersRelations,
11495
+ gameDashboardUsers: () => gameDashboardUsers,
11496
+ gameDashboardUserRoleEnum: () => gameDashboardUserRoleEnum,
11427
11497
  gameCustomHostnames: () => gameCustomHostnames,
11428
11498
  developerStatusEnum: () => developerStatusEnum,
11499
+ deploymentTargetEnum: () => deploymentTargetEnum,
11429
11500
  deploymentProviderEnum: () => deploymentProviderEnum,
11430
11501
  deployJobStatusEnum: () => deployJobStatusEnum,
11431
11502
  customHostnameStatusEnum: () => customHostnameStatusEnum,
@@ -12929,6 +13000,10 @@ function errorMessage(err2) {
12929
13000
  function errorType(err2) {
12930
13001
  return err2 instanceof Error ? err2.constructor.name : typeof err2;
12931
13002
  }
13003
+ function isUniqueViolation(err2) {
13004
+ return typeof err2 === "object" && err2 !== null && err2.code === PG_UNIQUE_VIOLATION;
13005
+ }
13006
+ var PG_UNIQUE_VIOLATION = "23505";
12932
13007
 
12933
13008
  // ../otel/src/spans.ts
12934
13009
  function truncate(value) {
@@ -13007,6 +13082,310 @@ var init_spans = __esm(() => {
13007
13082
  ROOT_SPAN_KEY = import_api2.createContextKey("playcademy.root_span");
13008
13083
  });
13009
13084
 
13085
+ // ../utils/src/random.ts
13086
+ async function generateSecureRandomString(length) {
13087
+ const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
13088
+ const randomValues = new Uint8Array(length);
13089
+ crypto.getRandomValues(randomValues);
13090
+ return [...randomValues].map((byte) => charset[byte % charset.length]).join("");
13091
+ }
13092
+
13093
+ // ../api-core/src/utils/hash.util.ts
13094
+ async function sha256Hex(input) {
13095
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
13096
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
13097
+ }
13098
+
13099
+ // ../api-core/src/utils/dashboard.util.ts
13100
+ function dashboardCredentialScope(slug) {
13101
+ return `${CREDENTIAL_SCOPE_PREFIX}${slug}`;
13102
+ }
13103
+ function dashboardCredentialSlugs(permissions) {
13104
+ 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);
13105
+ }
13106
+ function isAllowedDashboardWorkerRequest(method, pathname, permissions) {
13107
+ const path = pathname.replace(/\/+$/, "");
13108
+ return dashboardCredentialSlugs(permissions).some((slug) => {
13109
+ if (method === "GET") {
13110
+ return path === `/api/games/${slug}`;
13111
+ }
13112
+ if (method === "POST") {
13113
+ return Object.values(DASHBOARD_WORKER_ROUTES).some((subpath) => path === `/api/games/${slug}/dashboard${subpath}`);
13114
+ }
13115
+ return false;
13116
+ });
13117
+ }
13118
+ function hashDashboardToken(token) {
13119
+ return sha256Hex(token);
13120
+ }
13121
+ function loginBackoffMs(failedAttempts) {
13122
+ if (failedAttempts < LOGIN_BACKOFF_THRESHOLD) {
13123
+ return 0;
13124
+ }
13125
+ return Math.min(LOGIN_BACKOFF_CAP_MS, LOGIN_BACKOFF_BASE_MS * 2 ** (failedAttempts - LOGIN_BACKOFF_THRESHOLD));
13126
+ }
13127
+ 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;
13128
+ var init_dashboard_util = __esm(() => {
13129
+ init_src();
13130
+ LOGIN_BACKOFF_CAP_MS = 5 * 60 * 1000;
13131
+ });
13132
+
13133
+ // ../api-core/src/services/dashboard.service.ts
13134
+ function alreadyExists(gameId) {
13135
+ return new AlreadyExistsError("A dashboard user with this email already exists", { gameId });
13136
+ }
13137
+ var DashboardService;
13138
+ var init_dashboard_service = __esm(() => {
13139
+ init_drizzle_orm();
13140
+ init_tables_index();
13141
+ init_spans();
13142
+ init_errors();
13143
+ init_dashboard_util();
13144
+ DashboardService = class DashboardService {
13145
+ deps;
13146
+ static INVITE_TOKEN_LENGTH = 48;
13147
+ static INVITE_TTL_MS = 72 * 60 * 60 * 1000;
13148
+ static RESET_TTL_MS = 24 * 60 * 60 * 1000;
13149
+ constructor(deps) {
13150
+ this.deps = deps;
13151
+ }
13152
+ async listUsers(slug, user) {
13153
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13154
+ const rows = await this.deps.db.select({
13155
+ id: gameDashboardUsers.id,
13156
+ gameId: gameDashboardUsers.gameId,
13157
+ email: gameDashboardUsers.email,
13158
+ displayName: gameDashboardUsers.displayName,
13159
+ role: gameDashboardUsers.role,
13160
+ platformUserId: gameDashboardUsers.platformUserId,
13161
+ createdAt: gameDashboardUsers.createdAt,
13162
+ updatedAt: gameDashboardUsers.updatedAt,
13163
+ pending: sql`(${gameDashboardUsers.passwordHash} IS NULL)`
13164
+ }).from(gameDashboardUsers).where(eq(gameDashboardUsers.gameId, game2.id)).orderBy(asc(gameDashboardUsers.createdAt));
13165
+ setAttributes({
13166
+ "app.dashboard.operation": "list_users",
13167
+ "app.dashboard.result_count": rows.length
13168
+ });
13169
+ return rows;
13170
+ }
13171
+ async addUser(slug, user, input) {
13172
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13173
+ const email = input.email.toLowerCase();
13174
+ setAttributes({
13175
+ "app.dashboard.operation": "add_user",
13176
+ "app.dashboard.user_role": input.role
13177
+ });
13178
+ const existing = await this.findByEmail(game2.id, email);
13179
+ if (existing) {
13180
+ throw alreadyExists(game2.id);
13181
+ }
13182
+ const inviteToken = await generateSecureRandomString(DashboardService.INVITE_TOKEN_LENGTH);
13183
+ let created;
13184
+ try {
13185
+ [created] = await this.deps.db.insert(gameDashboardUsers).values({
13186
+ gameId: game2.id,
13187
+ email,
13188
+ displayName: input.displayName,
13189
+ role: input.role,
13190
+ inviteTokenHash: await hashDashboardToken(inviteToken),
13191
+ inviteExpiresAt: new Date(Date.now() + DashboardService.INVITE_TTL_MS),
13192
+ platformUserId: email === user.email.toLowerCase() ? user.id : undefined
13193
+ }).returning();
13194
+ } catch (error) {
13195
+ if (isUniqueViolation(error)) {
13196
+ throw alreadyExists(game2.id);
13197
+ }
13198
+ throw error;
13199
+ }
13200
+ if (!created) {
13201
+ throw new ValidationError("Failed to create dashboard user");
13202
+ }
13203
+ return { user: DashboardService.toSummary(created), token: inviteToken, kind: "invite" };
13204
+ }
13205
+ async removeUser(slug, user, dashboardUserId) {
13206
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13207
+ setAttributes({
13208
+ "app.dashboard.operation": "remove_user",
13209
+ "app.dashboard.user_id": dashboardUserId
13210
+ });
13211
+ const [deleted] = await this.deps.db.delete(gameDashboardUsers).where(and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.id, dashboardUserId))).returning({ id: gameDashboardUsers.id });
13212
+ if (!deleted) {
13213
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13214
+ }
13215
+ }
13216
+ async verifyLogin(slug, input) {
13217
+ const game2 = await this.getGameForRuntime(slug);
13218
+ setAttributes({ "app.dashboard.operation": "verify_login" });
13219
+ const dashboardUser = await this.findByEmail(game2.id, input.email.toLowerCase());
13220
+ if (!dashboardUser?.passwordHash) {
13221
+ throw new UnauthorizedError("Invalid credentials");
13222
+ }
13223
+ if (dashboardUser.lastFailedLoginAt) {
13224
+ const retryAt = dashboardUser.lastFailedLoginAt.getTime() + loginBackoffMs(dashboardUser.failedLoginAttempts);
13225
+ if (retryAt > Date.now()) {
13226
+ addEvent("dashboard.login_throttled", {
13227
+ "app.dashboard.failed_attempts": dashboardUser.failedLoginAttempts
13228
+ });
13229
+ throw new RateLimitError("Too many failed attempts — try again shortly", {
13230
+ retryAfterSeconds: Math.ceil((retryAt - Date.now()) / 1000)
13231
+ });
13232
+ }
13233
+ }
13234
+ const valid = await this.deps.verifyPassword({
13235
+ hash: dashboardUser.passwordHash,
13236
+ password: input.password
13237
+ });
13238
+ if (!valid) {
13239
+ await this.deps.db.update(gameDashboardUsers).set({
13240
+ failedLoginAttempts: sql`${gameDashboardUsers.failedLoginAttempts} + 1`,
13241
+ lastFailedLoginAt: new Date
13242
+ }).where(eq(gameDashboardUsers.id, dashboardUser.id));
13243
+ throw new UnauthorizedError("Invalid credentials");
13244
+ }
13245
+ if (dashboardUser.failedLoginAttempts > 0) {
13246
+ await this.deps.db.update(gameDashboardUsers).set({ failedLoginAttempts: 0, lastFailedLoginAt: null }).where(eq(gameDashboardUsers.id, dashboardUser.id));
13247
+ }
13248
+ return DashboardService.toSummary(dashboardUser);
13249
+ }
13250
+ async acceptInvite(slug, input) {
13251
+ const game2 = await this.getGameForRuntime(slug);
13252
+ setAttributes({ "app.dashboard.operation": "accept_invite" });
13253
+ const tokenHash = await hashDashboardToken(input.token);
13254
+ const invited = await this.deps.db.query.gameDashboardUsers.findFirst({
13255
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.inviteTokenHash, tokenHash))
13256
+ });
13257
+ if (!invited) {
13258
+ throw new UnauthorizedError("Invalid or already-used invite token");
13259
+ }
13260
+ if (invited.inviteExpiresAt && invited.inviteExpiresAt.getTime() < Date.now()) {
13261
+ throw new UnauthorizedError("This invite has expired — ask for a new one");
13262
+ }
13263
+ const passwordHash = await this.deps.hashPassword(input.password);
13264
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({
13265
+ passwordHash,
13266
+ inviteTokenHash: null,
13267
+ inviteExpiresAt: null,
13268
+ updatedAt: new Date
13269
+ }).where(and(eq(gameDashboardUsers.id, invited.id), eq(gameDashboardUsers.inviteTokenHash, tokenHash), isNull(gameDashboardUsers.passwordHash))).returning();
13270
+ if (!updated) {
13271
+ throw new UnauthorizedError("Invalid or already-used invite token");
13272
+ }
13273
+ return DashboardService.toSummary(updated);
13274
+ }
13275
+ async acceptReset(slug, input) {
13276
+ const game2 = await this.getGameForRuntime(slug);
13277
+ setAttributes({ "app.dashboard.operation": "accept_reset" });
13278
+ const tokenHash = await hashDashboardToken(input.token);
13279
+ const found = await this.deps.db.query.gameDashboardUsers.findFirst({
13280
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.resetTokenHash, tokenHash))
13281
+ });
13282
+ if (!found) {
13283
+ throw new UnauthorizedError("Invalid or already-used reset token");
13284
+ }
13285
+ if (found.resetExpiresAt && found.resetExpiresAt.getTime() < Date.now()) {
13286
+ throw new UnauthorizedError("This reset link has expired — ask for a new one");
13287
+ }
13288
+ const passwordHash = await this.deps.hashPassword(input.password);
13289
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({
13290
+ passwordHash,
13291
+ resetTokenHash: null,
13292
+ resetExpiresAt: null,
13293
+ inviteTokenHash: null,
13294
+ inviteExpiresAt: null,
13295
+ failedLoginAttempts: 0,
13296
+ lastFailedLoginAt: null,
13297
+ updatedAt: new Date
13298
+ }).where(and(eq(gameDashboardUsers.id, found.id), eq(gameDashboardUsers.resetTokenHash, tokenHash))).returning();
13299
+ if (!updated) {
13300
+ throw new UnauthorizedError("Invalid or already-used reset token");
13301
+ }
13302
+ return DashboardService.toSummary(updated);
13303
+ }
13304
+ async issueRecoveryLink(slug, user, dashboardUserId) {
13305
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13306
+ const existing = await this.deps.db.query.gameDashboardUsers.findFirst({
13307
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.id, dashboardUserId))
13308
+ });
13309
+ if (!existing) {
13310
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13311
+ }
13312
+ const kind = existing.passwordHash ? "reset" : "invite";
13313
+ setAttributes({
13314
+ "app.dashboard.operation": "issue_recovery_link",
13315
+ "app.dashboard.recovery_kind": kind
13316
+ });
13317
+ const token = await generateSecureRandomString(DashboardService.INVITE_TOKEN_LENGTH);
13318
+ const tokenHash = await hashDashboardToken(token);
13319
+ const fields = kind === "invite" ? {
13320
+ inviteTokenHash: tokenHash,
13321
+ inviteExpiresAt: new Date(Date.now() + DashboardService.INVITE_TTL_MS)
13322
+ } : {
13323
+ resetTokenHash: tokenHash,
13324
+ resetExpiresAt: new Date(Date.now() + DashboardService.RESET_TTL_MS)
13325
+ };
13326
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({ ...fields, updatedAt: new Date }).where(eq(gameDashboardUsers.id, existing.id)).returning();
13327
+ if (!updated) {
13328
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13329
+ }
13330
+ return { user: DashboardService.toSummary(updated), token, kind };
13331
+ }
13332
+ async getGameForRuntime(slug) {
13333
+ const game2 = await this.deps.db.query.games.findFirst({
13334
+ where: eq(games.slug, slug),
13335
+ columns: { id: true }
13336
+ });
13337
+ if (!game2) {
13338
+ throw new NotFoundError("Game", slug);
13339
+ }
13340
+ return game2;
13341
+ }
13342
+ async findByEmail(gameId, email) {
13343
+ const row = await this.deps.db.query.gameDashboardUsers.findFirst({
13344
+ where: and(eq(gameDashboardUsers.gameId, gameId), eq(gameDashboardUsers.email, email))
13345
+ });
13346
+ return row ?? null;
13347
+ }
13348
+ static toSummary(row) {
13349
+ return {
13350
+ id: row.id,
13351
+ gameId: row.gameId,
13352
+ email: row.email,
13353
+ displayName: row.displayName,
13354
+ role: row.role,
13355
+ platformUserId: row.platformUserId,
13356
+ createdAt: row.createdAt,
13357
+ updatedAt: row.updatedAt,
13358
+ pending: !row.passwordHash
13359
+ };
13360
+ }
13361
+ };
13362
+ });
13363
+
13364
+ // ../data/src/domains/game/helpers.ts
13365
+ function activeDeploymentWhere(gameId, target = "game") {
13366
+ return and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.target, target), eq(gameDeployments.isActive, true));
13367
+ }
13368
+ var init_helpers = __esm(() => {
13369
+ init_drizzle_orm();
13370
+ init_table5();
13371
+ });
13372
+
13373
+ // ../data/src/domains/timeback/helpers.ts
13374
+ function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
13375
+ return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
13376
+ }
13377
+ var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active", DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
13378
+ var init_helpers2 = __esm(() => {
13379
+ init_drizzle_orm();
13380
+ init_table7();
13381
+ });
13382
+
13383
+ // ../data/src/helpers.index.ts
13384
+ var init_helpers_index = __esm(() => {
13385
+ init_helpers();
13386
+ init_helpers2();
13387
+ });
13388
+
13010
13389
  // ../../node_modules/.bun/process-nextick-args@2.0.1/node_modules/process-nextick-args/index.js
13011
13390
  var require_process_nextick_args = __commonJS((exports, module2) => {
13012
13391
  if (typeof process === "undefined" || !process.version || process.version.indexOf("v0.") === 0 || process.version.indexOf("v1.") === 0 && process.version.indexOf("v1.8.") !== 0) {
@@ -22087,8 +22466,8 @@ var require_lib3 = __commonJS((exports, module2) => {
22087
22466
  });
22088
22467
 
22089
22468
  // ../utils/src/zip.ts
22090
- import { mkdir, writeFile } from "fs/promises";
22091
- import { join as join2 } from "path";
22469
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
22470
+ import { join as join2, relative, sep } from "path";
22092
22471
  async function extractZipToDirectory(zipBuffer, targetDir) {
22093
22472
  try {
22094
22473
  const zip = await import_jszip.default.loadAsync(zipBuffer);
@@ -22114,9 +22493,10 @@ async function extractZipToDirectory(zipBuffer, targetDir) {
22114
22493
  fileCount: Object.keys(zip.files).length
22115
22494
  });
22116
22495
  } catch (error) {
22117
- const errorMessage2 = error instanceof Error ? error.message : String(error);
22118
22496
  log.error("[utils/zip] Failed to extract ZIP", { targetDir, error });
22119
- throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage2}`, { cause: error });
22497
+ throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage(error)}`, {
22498
+ cause: error
22499
+ });
22120
22500
  }
22121
22501
  }
22122
22502
  var import_jszip;
@@ -22386,6 +22766,7 @@ class DeployJobService {
22386
22766
  jobId,
22387
22767
  leaseId,
22388
22768
  leaseLost,
22769
+ target,
22389
22770
  game: game2,
22390
22771
  user,
22391
22772
  error
@@ -22415,9 +22796,12 @@ class DeployJobService {
22415
22796
  }
22416
22797
  }
22417
22798
  if (!effectiveLeaseLost) {
22418
- await this.deps.notifyDeploymentFailure(game2.slug, game2.displayName, message, {
22419
- id: user.id,
22420
- email: user.email
22799
+ await this.deps.notifyDeploymentFailure({
22800
+ slug: game2.slug,
22801
+ displayName: game2.displayName,
22802
+ error: message,
22803
+ target,
22804
+ developer: { id: user.id, email: user.email }
22421
22805
  });
22422
22806
  }
22423
22807
  await this.deleteCodeBundle(jobId);
@@ -22519,7 +22903,7 @@ class DeployJobService {
22519
22903
  }
22520
22904
  assertLease();
22521
22905
  const activeDeployment = await this.deps.db.query.gameDeployments.findFirst({
22522
- where: and(eq(gameDeployments.gameId, job.gameId), eq(gameDeployments.isActive, true)),
22906
+ where: activeDeploymentWhere(job.gameId, request.target ?? "game"),
22523
22907
  columns: {
22524
22908
  deploymentId: true,
22525
22909
  url: true
@@ -22542,7 +22926,15 @@ class DeployJobService {
22542
22926
  } catch (error) {
22543
22927
  clearInterval(heartbeat);
22544
22928
  setAttribute("app.deploy_job.run_duration_ms", DeployJobService.elapsedMsSince(new Date(runStartedAt)));
22545
- await this.handleRunError({ jobId, leaseId, leaseLost, game: game2, user, error });
22929
+ await this.handleRunError({
22930
+ jobId,
22931
+ leaseId,
22932
+ leaseLost,
22933
+ target: request.target ?? "game",
22934
+ game: game2,
22935
+ user,
22936
+ error
22937
+ });
22546
22938
  throw error;
22547
22939
  }
22548
22940
  }
@@ -22561,6 +22953,7 @@ async function forceMarkDeployJobFailed(db2, jobId, error) {
22561
22953
  var STATUS_MAP2, DEPLOY_JOB_LEASE_MS, DEPLOY_JOB_HEARTBEAT_MS, DEPLOY_JOB_CODE_BUNDLE_PREFIX = "deploy-job-code";
22562
22954
  var init_deploy_job_service = __esm(() => {
22563
22955
  init_drizzle_orm();
22956
+ init_helpers_index();
22564
22957
  init_tables_index();
22565
22958
  init_spans();
22566
22959
  init_zip();
@@ -24704,7 +25097,7 @@ var init_bindings = __esm(() => {
24704
25097
  });
24705
25098
 
24706
25099
  // ../cloudflare/src/playcademy/provider/helpers.ts
24707
- var init_helpers = __esm(() => {
25100
+ var init_helpers3 = __esm(() => {
24708
25101
  init_mime();
24709
25102
  });
24710
25103
 
@@ -24716,7 +25109,7 @@ var init_provider = __esm(() => {
24716
25109
  init_constants2();
24717
25110
  init_assets_config();
24718
25111
  init_bindings();
24719
- init_helpers();
25112
+ init_helpers3();
24720
25113
  });
24721
25114
  // ../cloudflare/src/playcademy/provider/index.ts
24722
25115
  var init_provider2 = __esm(() => {
@@ -27135,26 +27528,70 @@ var init_playcademy = __esm(() => {
27135
27528
  });
27136
27529
 
27137
27530
  // ../utils/src/tunnel.ts
27138
- async function getTunnelUrl() {
27139
- let response;
27531
+ function servicePort(service) {
27532
+ if (!service) {
27533
+ return null;
27534
+ }
27140
27535
  try {
27141
- response = await fetch(`${METRICS_BASE}/config`);
27536
+ const url = new URL(service);
27537
+ return url.port || (url.protocol === "https:" ? "443" : "80");
27142
27538
  } catch {
27143
- throw new Error("Local tunnel is not running. Start it with `bun dev` or `bun scripts/infra/tunnel.ts`.");
27539
+ return null;
27144
27540
  }
27145
- if (!response.ok) {
27146
- throw new Error(`Tunnel metrics endpoint returned ${response.status}`);
27541
+ }
27542
+ async function readTunnelIngress(port) {
27543
+ try {
27544
+ const response = await fetch(`http://127.0.0.1:${port}/config`, {
27545
+ signal: AbortSignal.timeout(1000)
27546
+ });
27547
+ if (!response.ok) {
27548
+ return null;
27549
+ }
27550
+ const data = await response.json();
27551
+ return data.config?.ingress ?? [];
27552
+ } catch {
27553
+ return null;
27554
+ }
27555
+ }
27556
+ function matchingHostname(rules, expectedPort) {
27557
+ for (const rule of rules ?? []) {
27558
+ if (rule.hostname && servicePort(rule.service) === expectedPort) {
27559
+ return rule.hostname;
27560
+ }
27561
+ }
27562
+ return null;
27563
+ }
27564
+ async function getTunnelUrl(platformBaseUrl) {
27565
+ const expectedPort = servicePort(platformBaseUrl);
27566
+ if (!expectedPort) {
27567
+ throw new Error(`Cannot resolve a tunnel for unparseable platform URL '${platformBaseUrl}'`);
27568
+ }
27569
+ const cachedPort = matchedPorts.get(expectedPort);
27570
+ if (cachedPort !== undefined) {
27571
+ const hostname = matchingHostname(await readTunnelIngress(cachedPort), expectedPort);
27572
+ if (hostname) {
27573
+ return `https://${hostname}`;
27574
+ }
27575
+ matchedPorts.delete(expectedPort);
27576
+ }
27577
+ const ports = Array.from({ length: TUNNEL_METRICS_PORT_RANGE }, (_, offset) => TUNNEL_METRICS_PORT + offset);
27578
+ const results = await Promise.all(ports.map(async (port) => ({ port, rules: await readTunnelIngress(port) })));
27579
+ for (const { port, rules } of results) {
27580
+ const hostname = matchingHostname(rules, expectedPort);
27581
+ if (hostname) {
27582
+ matchedPorts.set(expectedPort, port);
27583
+ return `https://${hostname}`;
27584
+ }
27147
27585
  }
27148
- const data = await response.json();
27149
- const hostname = data.config.ingress.find((r) => r.hostname)?.hostname;
27150
- if (!hostname) {
27151
- throw new Error("Tunnel is running but no hostname found in ingress config");
27586
+ const foreign = results.flatMap(({ rules }) => (rules ?? []).filter((rule) => rule.hostname).map((rule) => `${rule.hostname} -> ${rule.service}`));
27587
+ if (foreign.length > 0) {
27588
+ throw new Error(`A local tunnel is running, but none route to the platform at ${platformBaseUrl} (found: ${foreign.join(", ")}). ${TUNNEL_START_HINT}`);
27152
27589
  }
27153
- return `https://${hostname}`;
27590
+ throw new Error(`Local tunnel is not running. ${TUNNEL_START_HINT}`);
27154
27591
  }
27155
- var TUNNEL_METRICS_PORT = 20241, METRICS_BASE;
27592
+ var TUNNEL_METRICS_PORT = 20241, TUNNEL_METRICS_PORT_RANGE = 10, TUNNEL_START_HINT = "Start this platform's tunnel with `bun dev` or `bun run tunnel`.", matchedPorts;
27156
27593
  var init_tunnel = __esm(() => {
27157
- METRICS_BASE = `http://127.0.0.1:${TUNNEL_METRICS_PORT}`;
27594
+ matchedPorts = new Map;
27158
27595
  });
27159
27596
 
27160
27597
  // ../utils/src/stages.ts
@@ -27167,7 +27604,7 @@ var init_stages = __esm(() => {
27167
27604
  });
27168
27605
 
27169
27606
  // ../api-core/src/utils/deployment.util.ts
27170
- function getDeploymentId(gameSlug, sstStage) {
27607
+ function getGameDeploymentId(gameSlug, sstStage) {
27171
27608
  if (sstStage === "production") {
27172
27609
  return gameSlug;
27173
27610
  }
@@ -27176,45 +27613,57 @@ function getDeploymentId(gameSlug, sstStage) {
27176
27613
  }
27177
27614
  return `${WORKER_NAMING.LOCAL_PREFIX}${sstStage}-${gameSlug}`;
27178
27615
  }
27616
+ function getDashboardDeploymentId(gameSlug, sstStage) {
27617
+ return `${getGameDeploymentId(gameSlug, sstStage)}${DASHBOARD_WORKER_SUFFIX}`;
27618
+ }
27179
27619
  function getGameWorkerApiKeyName(slug) {
27180
- return `game-worker-${slug}`.substring(0, 32);
27620
+ return `${GAME_WORKER_KEY_PREFIX}${slug}`.substring(0, 32);
27621
+ }
27622
+ function getDashboardWorkerApiKeyName(slug) {
27623
+ return `${DASHBOARD_WORKER_KEY_PREFIX}${slug}`;
27181
27624
  }
27182
27625
  function toBindingName(queueKey) {
27183
27626
  return `${queueKey.replace(/-/g, "_").toUpperCase()}_QUEUE`;
27184
27627
  }
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("");
27628
+ function generateDeploymentHash(code) {
27629
+ return sha256Hex(code);
27191
27630
  }
27631
+ var GAME_WORKER_KEY_PREFIX = "game-worker-", DASHBOARD_WORKER_KEY_PREFIX = "dash-worker-";
27192
27632
  var init_deployment_util = __esm(() => {
27193
27633
  init_src();
27194
27634
  init_stages();
27195
27635
  });
27196
27636
 
27637
+ // ../api-core/src/utils/worker-keys.util.ts
27638
+ async function sweepDashboardWorkerKeys(deleteApiKeysByName, slug) {
27639
+ try {
27640
+ await deleteApiKeysByName(getDashboardWorkerApiKeyName(slug));
27641
+ } catch (error) {
27642
+ addEvent("dashboard.worker_key_sweep_failed", {
27643
+ "exception.type": errorType(error),
27644
+ "app.error.message": errorMessage(error)
27645
+ });
27646
+ }
27647
+ }
27648
+ var init_worker_keys_util = __esm(() => {
27649
+ init_spans();
27650
+ init_deployment_util();
27651
+ });
27652
+
27197
27653
  // ../api-core/src/services/deploy.service.ts
27654
+ function readDashboardTheme(config2) {
27655
+ const theme = config2?.dashboard?.theme;
27656
+ return {
27657
+ ...typeof theme?.primary === "string" && { primary: theme.primary },
27658
+ ...typeof theme?.secondary === "string" && { secondary: theme.secondary }
27659
+ };
27660
+ }
27661
+
27198
27662
  class DeployService {
27199
27663
  deps;
27200
27664
  constructor(deps) {
27201
27665
  this.deps = deps;
27202
27666
  }
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
27667
  getCloudflare() {
27219
27668
  const cf = this.deps.cloudflare;
27220
27669
  if (!cf) {
@@ -27222,86 +27671,307 @@ class DeployService {
27222
27671
  }
27223
27672
  return cf;
27224
27673
  }
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
- });
27674
+ async* deploy(slug, request, user, uploadDeps, extractZip) {
27675
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
27676
+ const context2 = { slug, request, user, game: game2, uploadDeps, extractZip };
27677
+ switch (request.target ?? "game") {
27678
+ case "dashboard": {
27679
+ yield* this.deployDashboard(context2);
27680
+ break;
27681
+ }
27682
+ case "game": {
27683
+ yield* this.deployGame(context2);
27684
+ break;
27685
+ }
27686
+ default: {
27687
+ throw new ValidationError("Invalid deployment target");
27249
27688
  }
27250
27689
  }
27251
- return this.createApiKey(user, slug, keyName);
27252
27690
  }
27253
- async ensureApiKeyOnWorker(user, slug, deploymentId) {
27691
+ async* deployGame(context2) {
27692
+ const { slug, request, user, game: game2, uploadDeps, extractZip } = context2;
27254
27693
  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)),
27694
+ const db2 = this.deps.db;
27695
+ const flags2 = this.validateGameRequest(request);
27696
+ const { hasBackend, hasFrontend } = flags2;
27697
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
27698
+ let frontendAssetsPath;
27699
+ let tempDir;
27700
+ if (hasFrontend) {
27701
+ if (!uploadDeps || !extractZip) {
27702
+ throw new ValidationError("Upload dependencies not configured for frontend deployment");
27703
+ }
27704
+ yield { type: "status", data: { message: "Fetching temporary files" } };
27705
+ const extracted = await this.fetchAndExtractFrontendAssets(slug, game2.id, request.uploadToken, uploadDeps, extractZip);
27706
+ tempDir = extracted.tempDir;
27707
+ frontendAssetsPath = extracted.assetsPath;
27708
+ yield { type: "status", data: { message: "Extracting assets" } };
27709
+ }
27710
+ const platformBaseUrl = await this.resolvePlatformBaseUrl();
27711
+ const env = {
27712
+ GAME_ID: game2.id,
27713
+ PLAYCADEMY_BASE_URL: platformBaseUrl,
27714
+ PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
27715
+ };
27716
+ yield {
27717
+ type: "status",
27718
+ data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
27719
+ };
27720
+ const keepAssets = hasBackend && !hasFrontend;
27721
+ const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings, request.schema);
27722
+ const bindings = deploymentOptions?.bindings;
27723
+ setAttributes({
27724
+ "app.deploy.has_d1": Boolean(bindings?.d1?.length),
27725
+ "app.deploy.has_kv": Boolean(bindings?.kv?.length),
27726
+ "app.deploy.has_r2": Boolean(bindings?.r2?.length),
27727
+ "app.deploy.queue_count": bindings?.queues?.length ?? 0,
27728
+ "app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
27729
+ });
27730
+ const activeDeployment = await db2.query.gameDeployments.findFirst({
27731
+ where: activeDeploymentWhere(game2.id, "game"),
27732
+ columns: { resources: true }
27733
+ });
27734
+ if (deploymentOptions?.bindings?.d1?.length) {
27735
+ await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
27736
+ }
27737
+ const result = await this.deployToCloudflare({
27738
+ deploymentId,
27739
+ code: request.code,
27740
+ env,
27741
+ tempDir,
27742
+ options: {
27743
+ ...deploymentOptions,
27744
+ compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27745
+ compatibilityFlags: request.compatibilityFlags,
27746
+ existingResources: activeDeployment?.resources ?? undefined,
27747
+ assetsPath: frontendAssetsPath,
27748
+ keepAssets
27749
+ }
27750
+ });
27751
+ yield { type: "status", data: { message: "Finalizing deployment" } };
27752
+ const updatedGame = await this.finalizeGameDeployment({
27753
+ game: game2,
27754
+ slug,
27755
+ deploymentId,
27756
+ result,
27757
+ request,
27758
+ user,
27759
+ flags: flags2
27760
+ });
27761
+ yield { type: "complete", data: updatedGame };
27762
+ }
27763
+ async* deployDashboard(context2) {
27764
+ const { slug, request, user, game: game2 } = context2;
27765
+ const db2 = this.deps.db;
27766
+ const contract = this.validateDashboardRequest(request, context2.uploadDeps, context2.extractZip);
27767
+ const deploymentId = getDashboardDeploymentId(slug, this.deps.config.sstStage);
27768
+ if (deploymentId.length > MAX_WORKER_NAME_LENGTH) {
27769
+ throw new ValidationError(`Dashboard worker name '${deploymentId}' exceeds Cloudflare's ${MAX_WORKER_NAME_LENGTH}-character limit — shorten the game slug`);
27770
+ }
27771
+ const collidingGame = await db2.query.games.findFirst({
27772
+ where: eq(games.slug, `${slug}${DASHBOARD_WORKER_SUFFIX}`),
27258
27773
  columns: { id: true }
27259
27774
  });
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
- });
27775
+ if (collidingGame) {
27776
+ throw new ValidationError(`Cannot deploy this dashboard: an existing game's slug is '${slug}${DASHBOARD_WORKER_SUFFIX}', which owns the '${deploymentId}' worker name`);
27777
+ }
27778
+ const gameDeployment = await db2.query.gameDeployments.findFirst({
27779
+ where: activeDeploymentWhere(game2.id, "game"),
27780
+ columns: { resources: true }
27781
+ });
27782
+ if (!gameDeployment) {
27783
+ throw new ValidationError("Deploy the game before its dashboard — the dashboard attaches to the game deployment");
27784
+ }
27785
+ setAttributes({
27786
+ "app.deploy.type": "dashboard",
27787
+ "app.deploy.deployment_id": deploymentId,
27788
+ "app.deploy.code_size": contract.code.length,
27789
+ "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27790
+ "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0
27791
+ });
27792
+ yield { type: "status", data: { message: "Fetching temporary files" } };
27793
+ const [{ tempDir, assetsPath }, platformBaseUrl, dashboardActive] = await Promise.all([
27794
+ this.fetchAndExtractFrontendAssets(slug, game2.id, contract.uploadToken, contract.uploadDeps, contract.extractZip),
27795
+ this.resolvePlatformBaseUrl(),
27796
+ db2.query.gameDeployments.findFirst({
27797
+ where: activeDeploymentWhere(game2.id, "dashboard"),
27798
+ columns: { resources: true }
27799
+ })
27800
+ ]);
27801
+ yield { type: "status", data: { message: "Extracting assets" } };
27802
+ const theme = readDashboardTheme(request.config);
27803
+ const env = {
27804
+ GAME_ID: game2.id,
27805
+ GAME_SLUG: slug,
27806
+ GAME_NAME: game2.displayName,
27807
+ ...game2.metadata?.emoji && { GAME_EMOJI: game2.metadata.emoji },
27808
+ ...theme.primary && { DASHBOARD_THEME_PRIMARY: theme.primary },
27809
+ ...theme.secondary && { DASHBOARD_THEME_SECONDARY: theme.secondary },
27810
+ PLAYCADEMY_BASE_URL: platformBaseUrl,
27811
+ PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
27812
+ };
27813
+ const gameResources = gameDeployment.resources;
27814
+ const bindings = {
27815
+ d1: gameResources?.d1?.map((database) => database.name) ?? [],
27816
+ kv: gameResources?.kv?.map((namespace) => namespace.name) ?? [],
27817
+ r2: gameResources?.r2?.filter((bucket) => bucket.kind !== "assets").map((bucket) => bucket.name) ?? []
27818
+ };
27819
+ const existingResources = {
27820
+ ...dashboardActive?.resources,
27821
+ d1: gameResources?.d1,
27822
+ kv: gameResources?.kv,
27823
+ r2: gameResources?.r2
27824
+ };
27825
+ yield { type: "status", data: { message: "Deploying dashboard" } };
27826
+ const result = await this.deployToCloudflare({
27827
+ deploymentId,
27828
+ code: contract.code,
27829
+ env,
27830
+ tempDir,
27831
+ options: {
27832
+ bindings,
27833
+ compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27834
+ compatibilityFlags: request.compatibilityFlags,
27835
+ existingResources,
27836
+ assetsPath,
27837
+ keepAssets: false,
27838
+ assetsConfig: { run_worker_first: true }
27275
27839
  }
27276
- apiKey = await this.regenerateApiKey(user, slug, existingKey.id, keyName);
27840
+ });
27841
+ yield { type: "status", data: { message: "Finalizing deployment" } };
27842
+ await withSpan("deploy.configure_worker_secrets", async () => {
27843
+ const existingSecrets = await this.listWorkerSecretNames(result.deploymentId);
27844
+ await this.ensureWorkerApiKeyOnWorker(user, DeployService.dashboardWorkerKeySpec(slug), result.deploymentId, existingSecrets);
27845
+ await this.ensureDashboardSessionSecret(result.deploymentId, existingSecrets, Boolean(dashboardActive));
27846
+ });
27847
+ const codeHash = await generateDeploymentHash(contract.code);
27848
+ await this.saveDeployment({
27849
+ gameId: game2.id,
27850
+ deploymentId: result.deploymentId,
27851
+ url: result.url,
27852
+ codeHash,
27853
+ resources: result.resources,
27854
+ target: "dashboard"
27855
+ });
27856
+ setAttribute("app.deploy.code_hash", codeHash);
27857
+ try {
27858
+ await withSpan("deploy.send_alert", () => this.deps.alerts.notifyDashboardDeployment({
27859
+ slug,
27860
+ displayName: game2.displayName,
27861
+ url: result.url,
27862
+ developer: { id: user.id, email: user.email }
27863
+ }));
27864
+ } catch (error) {
27865
+ setAttribute("app.alerts.delivery", "failed");
27866
+ setAttribute("app.alerts.delivery_error", errorMessage(error));
27867
+ setAttribute("app.alerts.type", "dashboard_deployment");
27277
27868
  }
27278
- await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
27869
+ yield { type: "complete", data: game2 };
27279
27870
  }
27280
- async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
27281
- const secret = this.deps.config.queueIngressSecret;
27282
- if (!secret) {
27283
- return;
27871
+ validateGameRequest(request) {
27872
+ const hasBackend = Boolean(request.code);
27873
+ const hasFrontend = Boolean(request.uploadToken);
27874
+ const hasMetadata = Boolean(request.metadata);
27875
+ setAttributes({
27876
+ "app.deploy.has_backend": hasBackend,
27877
+ "app.deploy.has_frontend": hasFrontend,
27878
+ "app.deploy.has_metadata": hasMetadata,
27879
+ "app.deploy.type": DeployService.classifyGameDeployType({
27880
+ hasBackend,
27881
+ hasFrontend,
27882
+ hasMetadata
27883
+ }),
27884
+ "app.deploy.code_size": request.code?.length ?? 0,
27885
+ "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27886
+ "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
27887
+ "app.deploy.has_schema": Boolean(request.schema)
27888
+ });
27889
+ if (!hasBackend && !hasFrontend && !hasMetadata) {
27890
+ throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
27284
27891
  }
27285
- const cf = this.getCloudflare();
27286
- await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
27892
+ if (!this.deps.config.baseUrl) {
27893
+ throw new ValidationError("baseUrl is not configured");
27894
+ }
27895
+ return { hasBackend, hasFrontend, hasMetadata };
27287
27896
  }
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");
27897
+ validateDashboardRequest(request, uploadDeps, extractZip) {
27898
+ if (!request.code || !request.uploadToken) {
27899
+ throw new ValidationError("Dashboard deployments require both worker code and an asset upload");
27292
27900
  }
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 };
27901
+ if (request.metadata) {
27902
+ throw new ValidationError("Dashboard deployments cannot update game metadata");
27903
+ }
27904
+ if (!uploadDeps || !extractZip) {
27905
+ throw new ValidationError("Upload dependencies not configured for frontend deployment");
27906
+ }
27907
+ if (!this.deps.config.baseUrl) {
27908
+ throw new ValidationError("baseUrl is not configured");
27909
+ }
27910
+ return { code: request.code, uploadToken: request.uploadToken, uploadDeps, extractZip };
27911
+ }
27912
+ static classifyGameDeployType(flags2) {
27913
+ if (flags2.hasBackend && flags2.hasFrontend) {
27914
+ return "full_stack";
27915
+ }
27916
+ if (flags2.hasFrontend) {
27917
+ return "frontend_only";
27918
+ }
27919
+ if (flags2.hasBackend) {
27920
+ return "backend_only";
27921
+ }
27922
+ return "metadata_only";
27923
+ }
27924
+ mapGameBindingsToOptions(deploymentId, bindings, schema2) {
27925
+ if (!bindings && !schema2) {
27926
+ return;
27927
+ }
27928
+ const workerBindings = {};
27929
+ if (bindings?.database) {
27930
+ workerBindings.d1 = [deploymentId];
27931
+ }
27932
+ if (bindings?.keyValue) {
27933
+ workerBindings.kv = [deploymentId];
27934
+ }
27935
+ if (bindings?.bucket) {
27936
+ workerBindings.r2 = [deploymentId];
27937
+ }
27938
+ if (bindings?.queues) {
27939
+ let toQueueName = function(queueKey) {
27940
+ return `${QUEUE_NAME_PREFIX}-${deploymentId}--${queueKey}`;
27941
+ };
27942
+ const queueNameByKey = new Map;
27943
+ for (const queueKey of Object.keys(bindings.queues)) {
27944
+ queueNameByKey.set(queueKey, toQueueName(queueKey));
27945
+ }
27946
+ workerBindings.queues = Object.entries(bindings.queues).map(([queueKey, queueConfig]) => {
27947
+ const config2 = queueConfig === true ? undefined : queueConfig;
27948
+ const deadLetterQueue = config2?.deadLetterQueue && queueNameByKey.get(config2.deadLetterQueue) ? queueNameByKey.get(config2.deadLetterQueue) : undefined;
27949
+ return {
27950
+ bindingName: toBindingName(queueKey),
27951
+ queueName: toQueueName(queueKey),
27952
+ settings: config2 ? {
27953
+ maxBatchSize: config2.maxBatchSize,
27954
+ maxRetries: config2.maxRetries,
27955
+ maxBatchTimeout: config2.maxBatchTimeout,
27956
+ maxConcurrency: config2.maxConcurrency,
27957
+ retryDelay: config2.retryDelay
27958
+ } : undefined,
27959
+ deadLetterQueue
27960
+ };
27961
+ });
27962
+ }
27963
+ const hasBindings = workerBindings.d1?.length || workerBindings.kv?.length || workerBindings.r2?.length || workerBindings.queues?.length;
27964
+ return {
27965
+ ...hasBindings && { bindings: workerBindings },
27966
+ ...schema2 && { schema: schema2 }
27967
+ };
27968
+ }
27969
+ static countDeadLetterQueues(bindings) {
27970
+ return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
27301
27971
  }
27302
27972
  async cleanupOrphanD1Databases(cf, d1Names, slug, gameId) {
27303
27973
  const hasAnyPriorDeployment = await this.deps.db.query.gameDeployments.findFirst({
27304
- where: eq(gameDeployments.gameId, gameId),
27974
+ where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.target, "game")),
27305
27975
  columns: { id: true }
27306
27976
  });
27307
27977
  if (hasAnyPriorDeployment) {
@@ -27336,33 +28006,7 @@ class DeployService {
27336
28006
  }
27337
28007
  await this.deps.db.update(games).set(updates).where(eq(games.id, gameId));
27338
28008
  }
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({
28009
+ async finalizeGameDeployment({
27366
28010
  game: game2,
27367
28011
  slug,
27368
28012
  deploymentId,
@@ -27374,10 +28018,17 @@ class DeployService {
27374
28018
  const { hasBackend, hasFrontend, hasMetadata } = flags2;
27375
28019
  const db2 = this.deps.db;
27376
28020
  const codeHash = hasBackend ? await generateDeploymentHash(request.code) : null;
27377
- await this.saveDeployment(game2.id, result.deploymentId, result.url, codeHash, result.resources);
28021
+ await this.saveDeployment({
28022
+ gameId: game2.id,
28023
+ deploymentId: result.deploymentId,
28024
+ url: result.url,
28025
+ codeHash,
28026
+ resources: result.resources,
28027
+ target: "game"
28028
+ });
27378
28029
  if (hasBackend) {
27379
28030
  await withSpan("deploy.configure_worker_secrets", async () => {
27380
- await this.ensureApiKeyOnWorker(user, slug, result.deploymentId);
28031
+ await this.ensureWorkerApiKeyOnWorker(user, DeployService.gameWorkerKeySpec(slug), result.deploymentId);
27381
28032
  await this.ensureQueueIngressSecretOnWorker(slug, result.deploymentId);
27382
28033
  });
27383
28034
  }
@@ -27410,169 +28061,232 @@ class DeployService {
27410
28061
  }
27411
28062
  return updatedGame;
27412
28063
  }
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
28064
+ static gameWorkerKeySpec(slug) {
28065
+ return {
28066
+ keyName: getGameWorkerApiKeyName(slug),
28067
+ permissions: { games: [`read:${slug}`, `write:${slug}`] },
28068
+ lookup: "owner-and-name"
27448
28069
  };
27449
- yield {
27450
- type: "status",
27451
- data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
28070
+ }
28071
+ static dashboardWorkerKeySpec(slug) {
28072
+ return {
28073
+ keyName: getDashboardWorkerApiKeyName(slug),
28074
+ permissions: {
28075
+ games: [`read:${slug}`],
28076
+ [DASHBOARD_PERMISSION_NAMESPACE]: [dashboardCredentialScope(slug)]
28077
+ },
28078
+ rateLimit: { maxRequests: 300, timeWindowMs: 60000 },
28079
+ lookup: "name",
28080
+ isCurrent: (permissionsJson) => DeployService.hasDashboardCredentialScope(permissionsJson, slug)
27452
28081
  };
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);
28082
+ }
28083
+ static hasDashboardCredentialScope(permissionsJson, slug) {
28084
+ if (!permissionsJson) {
28085
+ return false;
27469
28086
  }
27470
- let result;
27471
28087
  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"));
28088
+ const parsed = JSON.parse(permissionsJson);
28089
+ return dashboardCredentialSlugs(parsed).includes(slug);
28090
+ } catch {
28091
+ return false;
28092
+ }
28093
+ }
28094
+ async ensureWorkerApiKeyOnWorker(user, spec, deploymentId, existingSecrets) {
28095
+ const cf = this.getCloudflare();
28096
+ const existingKey = await this.deps.db.query.apikey.findFirst({
28097
+ where: spec.lookup === "owner-and-name" ? and(eq(apikey.userId, user.id), eq(apikey.name, spec.keyName)) : eq(apikey.name, spec.keyName),
28098
+ columns: { id: true, permissions: true }
28099
+ });
28100
+ let apiKey;
28101
+ if (!existingKey) {
28102
+ apiKey = await this.createApiKey(user, spec);
28103
+ } else {
28104
+ const current = spec.isCurrent?.(existingKey.permissions) ?? true;
28105
+ const workerHasKey = existingSecrets ? existingSecrets.includes("PLAYCADEMY_API_KEY") : await this.workerHasSecret(deploymentId, "PLAYCADEMY_API_KEY");
28106
+ if (current && workerHasKey) {
28107
+ setAttribute("app.deploy.api_key_outcome", "already_set");
28108
+ return;
27484
28109
  }
28110
+ apiKey = await this.regenerateApiKey(user, existingKey.id, spec);
27485
28111
  }
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
28112
+ await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
28113
+ }
28114
+ async createApiKey(user, spec) {
28115
+ const { key: apiKey } = await this.deps.createAuthApiKey({
28116
+ userId: user.id,
28117
+ name: spec.keyName,
28118
+ expiresIn: null,
28119
+ permissions: spec.permissions,
28120
+ ...spec.rateLimit ? {
28121
+ rateLimitEnabled: true,
28122
+ rateLimitMax: spec.rateLimit.maxRequests,
28123
+ rateLimitTimeWindow: spec.rateLimit.timeWindowMs
28124
+ } : { rateLimitEnabled: false }
27495
28125
  });
27496
- yield { type: "complete", data: updatedGame };
28126
+ setAttribute("app.deploy.api_key_outcome", "created");
28127
+ return apiKey;
27497
28128
  }
27498
- mapBindingsToOptions(deploymentId, bindings, schema2) {
27499
- if (!bindings && !schema2) {
28129
+ async regenerateApiKey(user, existingKeyId, spec) {
28130
+ setAttribute("app.deploy.api_key_outcome", "regenerated");
28131
+ try {
28132
+ await this.deps.deleteAuthApiKey(existingKeyId);
28133
+ } catch (error) {
28134
+ addEvent("deploy.api_key_revoke_failed", {
28135
+ "exception.type": errorType(error),
28136
+ "app.error.message": errorMessage(error),
28137
+ "app.deploy.old_key_id": existingKeyId
28138
+ });
28139
+ }
28140
+ return this.createApiKey(user, spec);
28141
+ }
28142
+ async listWorkerSecretNames(deploymentId) {
28143
+ try {
28144
+ return await this.getCloudflare().listSecrets(deploymentId);
28145
+ } catch (error) {
28146
+ addEvent("deploy.worker_secrets_check_failed", {
28147
+ "exception.type": errorType(error),
28148
+ "app.error.message": errorMessage(error)
28149
+ });
28150
+ return null;
28151
+ }
28152
+ }
28153
+ async workerHasSecret(deploymentId, secretName) {
28154
+ const workerSecrets = await this.listWorkerSecretNames(deploymentId);
28155
+ return Boolean(workerSecrets?.includes(secretName));
28156
+ }
28157
+ async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
28158
+ const secret = this.deps.config.queueIngressSecret;
28159
+ if (!secret) {
27500
28160
  return;
27501
28161
  }
27502
- const workerBindings = {};
27503
- if (bindings?.database) {
27504
- workerBindings.d1 = [deploymentId];
28162
+ const cf = this.getCloudflare();
28163
+ await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
28164
+ }
28165
+ async ensureDashboardSessionSecret(deploymentId, existingSecrets, hasPriorDeployment) {
28166
+ if (existingSecrets === null && hasPriorDeployment) {
28167
+ setAttribute("app.deploy.session_secret_outcome", "check_failed_kept");
28168
+ return;
27505
28169
  }
27506
- if (bindings?.keyValue) {
27507
- workerBindings.kv = [deploymentId];
28170
+ if (existingSecrets?.includes("DASHBOARD_SESSION_SECRET")) {
28171
+ setAttribute("app.deploy.session_secret_outcome", "already_set");
28172
+ return;
27508
28173
  }
27509
- if (bindings?.bucket) {
27510
- workerBindings.r2 = [deploymentId];
28174
+ const secret = await generateSecureRandomString(64);
28175
+ await this.getCloudflare().setSecrets(deploymentId, { DASHBOARD_SESSION_SECRET: secret });
28176
+ setAttribute("app.deploy.session_secret_outcome", "created");
28177
+ }
28178
+ async rotateDashboardSessionSecret(slug, user) {
28179
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
28180
+ const deployment = await this.deps.db.query.gameDeployments.findFirst({
28181
+ where: activeDeploymentWhere(game2.id, "dashboard"),
28182
+ columns: { deploymentId: true }
28183
+ });
28184
+ if (!deployment) {
28185
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28186
+ throw new NotFoundError("Dashboard deployment", slug);
27511
28187
  }
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
- };
28188
+ const secret = await generateSecureRandomString(64);
28189
+ await this.getCloudflare().setSecrets(deployment.deploymentId, {
28190
+ DASHBOARD_SESSION_SECRET: secret
28191
+ });
28192
+ setAttribute("app.deploy.session_secret_outcome", "rotated");
28193
+ }
28194
+ async removeDashboard(slug, user) {
28195
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
28196
+ const db2 = this.deps.db;
28197
+ const deployment = await db2.query.gameDeployments.findFirst({
28198
+ where: activeDeploymentWhere(game2.id, "dashboard"),
28199
+ columns: { deploymentId: true, provider: true, resources: true }
28200
+ });
28201
+ if (!deployment) {
28202
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28203
+ throw new NotFoundError("Dashboard deployment", slug);
28204
+ }
28205
+ const assetBuckets = deployment.resources?.r2?.filter((bucket) => bucket.kind === "assets") ?? [];
28206
+ await this.getCloudflare().delete(deployment.deploymentId, {
28207
+ deleteBindings: assetBuckets.length > 0,
28208
+ resources: assetBuckets.length > 0 ? { r2: assetBuckets } : undefined
28209
+ });
28210
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28211
+ await db2.delete(gameDeployments).where(and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "dashboard")));
28212
+ setAttribute("app.deploy.dashboard_removed", true);
28213
+ await withSpan("deploy.send_alert", () => this.deps.alerts.notifyDashboardRemoval({
28214
+ slug,
28215
+ displayName: game2.displayName,
28216
+ developer: { id: user.id, email: user.email }
28217
+ })).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "dashboard_removal" }));
28218
+ }
28219
+ async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
28220
+ const frontendZip = await withSpan("deploy.fetch_temporary_files", () => uploadDeps.getObjectAsByteArray(uploadToken));
28221
+ if (!frontendZip || frontendZip.length === 0) {
28222
+ throw new ValidationError("Uploaded file is empty or not found");
28223
+ }
28224
+ setAttribute("app.deploy.asset_upload_size", frontendZip.length);
28225
+ const os2 = await import("os");
28226
+ const path = await import("path");
28227
+ const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
28228
+ const assetsPath = path.join(tempDir, "dist");
28229
+ await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
28230
+ uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
28231
+ return { frontendZip, tempDir, assetsPath };
28232
+ }
28233
+ async resolvePlatformBaseUrl() {
28234
+ if (!this.deps.config.isLocal) {
28235
+ return this.deps.config.baseUrl;
28236
+ }
28237
+ try {
28238
+ return await getTunnelUrl(this.deps.config.baseUrl);
28239
+ } catch (error) {
28240
+ setAttributes({
28241
+ "app.deploy.tunnel_available": false,
28242
+ "app.deploy.failure_kind": "local_tunnel_unavailable"
27535
28243
  });
28244
+ throw new ValidationError(errorMessage(error));
27536
28245
  }
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
28246
  }
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" }));
28247
+ async deployToCloudflare(args2) {
28248
+ const cf = this.getCloudflare();
28249
+ try {
28250
+ return await withSpan("deploy.cloudflare", () => cf.deploy(args2.deploymentId, args2.code, args2.env, args2.options));
28251
+ } finally {
28252
+ if (args2.tempDir) {
28253
+ const fs2 = await import("fs/promises");
28254
+ fs2.rm(args2.tempDir, { recursive: true, force: true }).catch(catchAttrs("deploy.temp_cleanup"));
28255
+ }
28256
+ }
27550
28257
  }
27551
- async saveDeployment(gameId, deploymentId, url, codeHash, resources) {
28258
+ async saveDeployment(record) {
27552
28259
  const db2 = this.deps.db;
27553
28260
  await db2.transaction(async (tx) => {
27554
- await tx.update(gameDeployments).set({ isActive: false }).where(eq(gameDeployments.gameId, gameId));
28261
+ await tx.update(gameDeployments).set({ isActive: false }).where(and(eq(gameDeployments.gameId, record.gameId), eq(gameDeployments.target, record.target)));
27555
28262
  await tx.insert(gameDeployments).values({
27556
- gameId,
27557
- deploymentId,
28263
+ gameId: record.gameId,
28264
+ deploymentId: record.deploymentId,
27558
28265
  provider: "cloudflare",
27559
- url,
27560
- codeHash,
27561
- resources,
28266
+ target: record.target,
28267
+ url: record.url,
28268
+ codeHash: record.codeHash,
28269
+ resources: record.resources,
27562
28270
  isActive: true
27563
28271
  });
27564
28272
  });
27565
28273
  }
28274
+ async notifyDeploymentFailure(failure) {
28275
+ await this.deps.alerts.notifyDeploymentFailure(failure).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
28276
+ }
27566
28277
  }
27567
28278
  var init_deploy_service = __esm(() => {
27568
28279
  init_drizzle_orm();
27569
28280
  init_playcademy();
27570
28281
  init_src();
28282
+ init_helpers_index();
27571
28283
  init_tables_index();
27572
28284
  init_spans();
27573
28285
  init_tunnel();
27574
28286
  init_errors();
28287
+ init_dashboard_util();
27575
28288
  init_deployment_util();
28289
+ init_worker_keys_util();
27576
28290
  });
27577
28291
 
27578
28292
  // ../api-core/src/services/developer.service.ts
@@ -27817,20 +28531,6 @@ var init_game_member_service = __esm(() => {
27817
28531
  init_spans();
27818
28532
  init_errors();
27819
28533
  });
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
28534
 
27835
28535
  // ../utils/src/fns.ts
27836
28536
  function sleep(ms) {
@@ -28274,6 +28974,7 @@ var init_game_service = __esm(() => {
28274
28974
  init_errors();
28275
28975
  init_deployment_util();
28276
28976
  init_timeback_util();
28977
+ init_worker_keys_util();
28277
28978
  inFlightManifestFetches = new Map;
28278
28979
  GameService = class GameService {
28279
28980
  deps;
@@ -28460,7 +29161,7 @@ var init_game_service = __esm(() => {
28460
29161
  GameService.recordGameShape(game2);
28461
29162
  return game2;
28462
29163
  }
28463
- async getBySlug(slug, caller) {
29164
+ async getBySlug(slug, caller, options) {
28464
29165
  const db2 = this.deps.db;
28465
29166
  const game2 = await db2.query.games.findFirst({
28466
29167
  where: eq(games.slug, slug)
@@ -28468,7 +29169,9 @@ var init_game_service = __esm(() => {
28468
29169
  if (!game2) {
28469
29170
  throw new NotFoundError("Game", slug);
28470
29171
  }
28471
- await this.enforceVisibility(game2, caller, slug);
29172
+ if (!options?.scopeAuthorized) {
29173
+ await this.enforceVisibility(game2, caller, slug);
29174
+ }
28472
29175
  GameService.recordGameShape(game2);
28473
29176
  return game2;
28474
29177
  }
@@ -28645,6 +29348,9 @@ var init_game_service = __esm(() => {
28645
29348
  await this.validateDeveloperAccess(user, gameId);
28646
29349
  } else {
28647
29350
  this.validateDeveloperStatus(user);
29351
+ if (slug.endsWith(DASHBOARD_WORKER_SUFFIX)) {
29352
+ throw new ValidationError(`Game slugs may not end with '${DASHBOARD_WORKER_SUFFIX}' — it is reserved for dashboard workers`);
29353
+ }
28648
29354
  }
28649
29355
  const gameDataForDb = {
28650
29356
  displayName: data.displayName,
@@ -28771,15 +29477,21 @@ var init_game_service = __esm(() => {
28771
29477
  if (!gameToDelete?.slug) {
28772
29478
  throw new NotFoundError("Game", gameId);
28773
29479
  }
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));
29480
+ const [activeDeployment, dashboardDeployment, customHostnames] = await Promise.all([
29481
+ db2.query.gameDeployments.findFirst({
29482
+ where: activeDeploymentWhere(gameId, "game"),
29483
+ columns: { deploymentId: true, provider: true, resources: true }
29484
+ }),
29485
+ db2.query.gameDeployments.findFirst({
29486
+ where: activeDeploymentWhere(gameId, "dashboard"),
29487
+ columns: { deploymentId: true, provider: true, resources: true }
29488
+ }),
29489
+ db2.select({
29490
+ hostname: gameCustomHostnames.hostname,
29491
+ cloudflareId: gameCustomHostnames.cloudflareId,
29492
+ environment: gameCustomHostnames.environment
29493
+ }).from(gameCustomHostnames).where(eq(gameCustomHostnames.gameId, gameId))
29494
+ ]);
28783
29495
  const result = await db2.delete(games).where(eq(games.id, gameId)).returning({ id: games.id });
28784
29496
  if (result.length === 0) {
28785
29497
  throw new NotFoundError("Game", gameId);
@@ -28823,6 +29535,22 @@ var init_game_service = __esm(() => {
28823
29535
  setAttribute("app.game.api_key_cleanup_error", errorMessage(error));
28824
29536
  }
28825
29537
  }
29538
+ if (dashboardDeployment?.provider === "cloudflare" && this.deps.cloudflare) {
29539
+ const dashboardAssetBuckets = dashboardDeployment.resources?.r2?.filter((bucket) => bucket.kind === "assets") ?? [];
29540
+ try {
29541
+ await this.deps.cloudflare.delete(dashboardDeployment.deploymentId, {
29542
+ deleteBindings: dashboardAssetBuckets.length > 0,
29543
+ resources: dashboardAssetBuckets.length > 0 ? { r2: dashboardAssetBuckets } : undefined
29544
+ });
29545
+ setAttribute("app.game.dashboard_cleanup", "succeeded");
29546
+ } catch (error) {
29547
+ setAttributes({
29548
+ "app.game.dashboard_cleanup": "failed",
29549
+ "app.game.dashboard_cleanup_error": errorMessage(error)
29550
+ });
29551
+ }
29552
+ }
29553
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, gameToDelete.slug);
28826
29554
  const corsOrigin = GameService.safeOrigin(gameToDelete);
28827
29555
  if (this.deps.corsKvs && corsOrigin) {
28828
29556
  setAttribute("app.cors_kvs.origin", corsOrigin);
@@ -28946,7 +29674,7 @@ class LogsService {
28946
29674
  }
28947
29675
  async generateToken(user, slug, sstStage) {
28948
29676
  await this.deps.validateDeveloperAccessBySlug(user, slug);
28949
- const workerId = getDeploymentId(slug, sstStage);
29677
+ const workerId = getGameDeploymentId(slug, sstStage);
28950
29678
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
28951
29679
  setAttribute("app.logs.worker_id", workerId);
28952
29680
  return { token, workerId };
@@ -28966,7 +29694,8 @@ function createGameServices(deps) {
28966
29694
  cache,
28967
29695
  cloudflare: cloudflare2,
28968
29696
  corsKvs,
28969
- deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2)
29697
+ deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2),
29698
+ deleteApiKeysByName: auth2.deleteApiKeysByName.bind(auth2)
28970
29699
  });
28971
29700
  const gameMember = new GameMemberService({
28972
29701
  db: db2,
@@ -28979,6 +29708,7 @@ function createGameServices(deps) {
28979
29708
  cloudflare: cloudflare2,
28980
29709
  createAuthApiKey: auth2.createApiKey.bind(auth2),
28981
29710
  deleteAuthApiKey: auth2.deleteApiKey.bind(auth2),
29711
+ deleteApiKeysByName: auth2.deleteApiKeysByName.bind(auth2),
28982
29712
  alerts,
28983
29713
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
28984
29714
  });
@@ -28988,14 +29718,20 @@ function createGameServices(deps) {
28988
29718
  storage,
28989
29719
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
28990
29720
  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)
29721
+ notifyDeploymentFailure: (failure) => deploy.notifyDeploymentFailure(failure)
28992
29722
  });
28993
29723
  const logs = new LogsService({
28994
29724
  mintLogStreamToken: auth2.mintLogStreamToken.bind(auth2),
28995
29725
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
28996
29726
  });
29727
+ const dashboard2 = new DashboardService({
29728
+ db: db2,
29729
+ hashPassword: auth2.hashPassword.bind(auth2),
29730
+ verifyPassword: auth2.verifyPassword.bind(auth2),
29731
+ validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
29732
+ });
28997
29733
  return {
28998
- services: { game: game2, gameMember, developer, deploy, deployJobs, logs },
29734
+ services: { game: game2, gameMember, developer, deploy, deployJobs, logs, dashboard: dashboard2 },
28999
29735
  validators: {
29000
29736
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
29001
29737
  validateDeveloperAccess: (user, gameId) => game2.validateDeveloperAccess(user, gameId),
@@ -29005,6 +29741,7 @@ function createGameServices(deps) {
29005
29741
  };
29006
29742
  }
29007
29743
  var init_game2 = __esm(() => {
29744
+ init_dashboard_service();
29008
29745
  init_deploy_job_service();
29009
29746
  init_deploy_service();
29010
29747
  init_developer_service();
@@ -29189,12 +29926,25 @@ class AlertsService {
29189
29926
  embed.setFooter("Playcademy Developer Platform").setTimestamp();
29190
29927
  await this.sendAlert(discord, embed.build());
29191
29928
  }
29929
+ async notifyDashboardDeployment(dashboard2) {
29930
+ const discord = this.recordAlert("dashboard_deployment");
29931
+ if (!discord) {
29932
+ return;
29933
+ }
29934
+ 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);
29935
+ if (dashboard2.developer) {
29936
+ embed.addField("Developer", dashboard2.developer.email || dashboard2.developer.id, true);
29937
+ }
29938
+ embed.setFooter("Playcademy Developer Platform").setTimestamp();
29939
+ await this.sendAlert(discord, embed.build());
29940
+ }
29192
29941
  async notifyDeploymentFailure(failure) {
29193
29942
  const discord = this.recordAlert("deployment_failure");
29194
29943
  if (!discord) {
29195
29944
  return;
29196
29945
  }
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);
29946
+ const [title, subject] = failure.target === "dashboard" ? ["Dashboard Deployment Failed", "Dashboard deployment"] : ["❌ Deployment Failed", "Deployment"];
29947
+ 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
29948
  if (failure.developer) {
29199
29949
  embed.addField("Developer", failure.developer.email || failure.developer.id, true);
29200
29950
  }
@@ -29209,6 +29959,14 @@ class AlertsService {
29209
29959
  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
29960
  await this.sendAlert(discord, embed);
29211
29961
  }
29962
+ async notifyDashboardRemoval(dashboard2) {
29963
+ const discord = this.recordAlert("dashboard_removal");
29964
+ if (!discord) {
29965
+ return;
29966
+ }
29967
+ 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();
29968
+ await this.sendAlert(discord, embed);
29969
+ }
29212
29970
  async notifySeedFailure(failure) {
29213
29971
  const discord = this.recordAlert("seed_failure");
29214
29972
  if (!discord) {
@@ -29374,6 +30132,7 @@ class KVBackupService {
29374
30132
  async getTargets(gameSlug) {
29375
30133
  const conditions2 = [
29376
30134
  eq(gameDeployments.isActive, true),
30135
+ eq(gameDeployments.target, "game"),
29377
30136
  eq(gameDeployments.provider, "cloudflare")
29378
30137
  ];
29379
30138
  if (gameSlug) {
@@ -29600,7 +30359,7 @@ class BucketService {
29600
30359
  return extensionIndex > 0 ? fileName.slice(extensionIndex + 1).toLowerCase() : "(none)";
29601
30360
  }
29602
30361
  getBucketName(slug) {
29603
- return getDeploymentId(slug, this.deps.sstStage);
30362
+ return getGameDeploymentId(slug, this.deps.sstStage);
29604
30363
  }
29605
30364
  async listFiles(slug, user, prefix) {
29606
30365
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
@@ -29689,6 +30448,32 @@ class DatabaseService {
29689
30448
  }
29690
30449
  return d1;
29691
30450
  }
30451
+ async syncDashboardD1Binding(gameId, d1ResourceName, databaseId) {
30452
+ const dashboardDeployment = await this.deps.db.query.gameDeployments.findFirst({
30453
+ where: activeDeploymentWhere(gameId, "dashboard"),
30454
+ columns: { id: true, deploymentId: true, resources: true }
30455
+ });
30456
+ if (!dashboardDeployment || !this.deps.cloudflare) {
30457
+ return;
30458
+ }
30459
+ try {
30460
+ await this.deps.cloudflare.updateD1Binding(dashboardDeployment.deploymentId, databaseId);
30461
+ if (dashboardDeployment.resources?.d1?.length) {
30462
+ const updatedResources = {
30463
+ ...dashboardDeployment.resources,
30464
+ d1: dashboardDeployment.resources.d1.map((dbResource) => dbResource.name === d1ResourceName ? { ...dbResource, id: databaseId } : dbResource)
30465
+ };
30466
+ await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, dashboardDeployment.id));
30467
+ }
30468
+ setAttribute("app.database.dashboard_binding", "updated");
30469
+ } catch (error) {
30470
+ addEvent("database.dashboard_binding_sync_failed", {
30471
+ "exception.type": errorType(error),
30472
+ "app.error.message": errorMessage(error),
30473
+ "app.deploy.deployment_id": dashboardDeployment.deploymentId
30474
+ });
30475
+ }
30476
+ }
29692
30477
  async reset(slug, user, schema2) {
29693
30478
  setAttributes({
29694
30479
  "app.database.operation": "reset",
@@ -29697,7 +30482,7 @@ class DatabaseService {
29697
30482
  });
29698
30483
  const d1 = this.getD1();
29699
30484
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
29700
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
30485
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
29701
30486
  try {
29702
30487
  const databaseId = await d1.reset(deploymentId);
29703
30488
  setAttribute("app.database.id", databaseId);
@@ -29713,7 +30498,7 @@ class DatabaseService {
29713
30498
  setAttribute("app.database.binding", "skipped_no_provider");
29714
30499
  }
29715
30500
  const activeDeployment = await this.deps.db.query.gameDeployments.findFirst({
29716
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
30501
+ where: activeDeploymentWhere(game2.id),
29717
30502
  columns: { id: true, resources: true }
29718
30503
  });
29719
30504
  setAttribute("app.database.active_deployment_found", Boolean(activeDeployment));
@@ -29724,6 +30509,7 @@ class DatabaseService {
29724
30509
  };
29725
30510
  await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, activeDeployment.id));
29726
30511
  }
30512
+ await this.syncDashboardD1Binding(game2.id, deploymentId, databaseId);
29727
30513
  setAttributes({
29728
30514
  "app.database.deployment_id": deploymentId,
29729
30515
  "app.database.schema_pushed": schemaPushed
@@ -29747,6 +30533,7 @@ class DatabaseService {
29747
30533
  }
29748
30534
  var init_database_service = __esm(() => {
29749
30535
  init_drizzle_orm();
30536
+ init_helpers_index();
29750
30537
  init_tables_index();
29751
30538
  init_spans();
29752
30539
  init_errors();
@@ -29990,7 +30777,7 @@ class KVService {
29990
30777
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
29991
30778
  const db2 = this.deps.db;
29992
30779
  const deployment = await db2.query.gameDeployments.findFirst({
29993
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
30780
+ where: activeDeploymentWhere(game2.id),
29994
30781
  columns: { resources: true }
29995
30782
  });
29996
30783
  if (!deployment?.resources?.kv?.length) {
@@ -30111,8 +30898,7 @@ class KVService {
30111
30898
  }
30112
30899
  }
30113
30900
  var init_kv_service = __esm(() => {
30114
- init_drizzle_orm();
30115
- init_tables_index();
30901
+ init_helpers_index();
30116
30902
  init_spans();
30117
30903
  init_errors();
30118
30904
  });
@@ -30129,13 +30915,13 @@ class SecretsService {
30129
30915
  }
30130
30916
  return this.deps.cloudflare;
30131
30917
  }
30132
- getDeploymentId(slug) {
30133
- return getDeploymentId(slug, this.deps.config.sstStage);
30918
+ getGameDeploymentId(slug) {
30919
+ return getGameDeploymentId(slug, this.deps.config.sstStage);
30134
30920
  }
30135
30921
  async listKeys(slug, user) {
30136
30922
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30137
30923
  const cf = this.getCloudflare();
30138
- const deploymentId = this.getDeploymentId(slug);
30924
+ const deploymentId = this.getGameDeploymentId(slug);
30139
30925
  try {
30140
30926
  const allKeys = await cf.listSecrets(deploymentId);
30141
30927
  const userKeys = allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
@@ -30161,7 +30947,7 @@ class SecretsService {
30161
30947
  async setSecrets(slug, newSecrets, user) {
30162
30948
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30163
30949
  const cf = this.getCloudflare();
30164
- const deploymentId = this.getDeploymentId(slug);
30950
+ const deploymentId = this.getGameDeploymentId(slug);
30165
30951
  const secretKeys = Object.keys(newSecrets);
30166
30952
  if (secretKeys.length === 0) {
30167
30953
  throw new ValidationError("At least one secret must be provided");
@@ -30214,7 +31000,7 @@ class SecretsService {
30214
31000
  }
30215
31001
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30216
31002
  const cf = this.getCloudflare();
30217
- const deploymentId = this.getDeploymentId(slug);
31003
+ const deploymentId = this.getGameDeploymentId(slug);
30218
31004
  try {
30219
31005
  const prefixedKey = `${SECRETS_PREFIX}${key}`;
30220
31006
  const existingKeys = await cf.listSecrets(deploymentId);
@@ -30251,27 +31037,7 @@ var init_secrets_service = __esm(() => {
30251
31037
  init_deployment_util();
30252
31038
  INTERNAL_SECRET_KEYS = ["PLAYCADEMY_API_KEY", "GAME_ID", "PLAYCADEMY_BASE_URL"];
30253
31039
  });
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
31040
+ // ../edge-play/src/game/setup.ts
30275
31041
  function prefixSecrets(secrets) {
30276
31042
  const prefixed = {};
30277
31043
  for (const [key, value] of Object.entries(secrets)) {
@@ -30281,7 +31047,6 @@ function prefixSecrets(secrets) {
30281
31047
  }
30282
31048
  var init_setup2 = __esm(() => {
30283
31049
  init_src();
30284
- init_constants3();
30285
31050
  });
30286
31051
 
30287
31052
  // ../api-core/src/services/seed.service.ts
@@ -30316,7 +31081,7 @@ class SeedService {
30316
31081
  async seed(slug, code, user, secrets) {
30317
31082
  const cf = this.getCloudflare();
30318
31083
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
30319
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
31084
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
30320
31085
  const uniqueSuffix = Date.now().toString(36);
30321
31086
  const seedDeploymentId = `seed-${deploymentId}-${uniqueSuffix}`;
30322
31087
  setAttributes({
@@ -30807,7 +31572,7 @@ var init_emoji = __esm(() => {
30807
31572
  });
30808
31573
 
30809
31574
  // ../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;
31575
+ 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
31576
  var init_schemas2 = __esm(() => {
30812
31577
  init_drizzle_zod();
30813
31578
  init_esm();
@@ -30918,6 +31683,20 @@ var init_schemas2 = __esm(() => {
30918
31683
  UpdateGameMemberRoleSchema = exports_external.object({
30919
31684
  role: exports_external.enum(gameMemberRoleEnum.enumValues)
30920
31685
  }).strict();
31686
+ AddGameDashboardUserSchema = exports_external.object({
31687
+ email: exports_external.string().email().max(255),
31688
+ displayName: exports_external.string().min(1).max(255).optional(),
31689
+ role: exports_external.enum(gameDashboardUserRoleEnum.enumValues).optional().default(DASHBOARD_USER_ROLES.VIEWER)
31690
+ }).strict();
31691
+ VerifyGameDashboardLoginSchema = exports_external.object({
31692
+ email: exports_external.string().email().max(255),
31693
+ password: exports_external.string().min(1).max(1024)
31694
+ }).strict();
31695
+ AcceptGameDashboardInviteSchema = exports_external.object({
31696
+ token: exports_external.string().min(1).max(255),
31697
+ password: exports_external.string().min(8, "Password must be at least 8 characters").max(1024, "Password must be at most 1024 characters")
31698
+ }).strict();
31699
+ AcceptGameDashboardResetSchema = AcceptGameDashboardInviteSchema;
30921
31700
  ALLOWED_UPLOAD_EXTENSIONS = [".zip", ".js"];
30922
31701
  InitiateUploadSchema = exports_external.object({
30923
31702
  fileName: exports_external.string().min(1).refine((name2) => ALLOWED_UPLOAD_EXTENSIONS.some((ext) => name2.endsWith(ext)), {
@@ -30952,6 +31731,7 @@ var init_schemas2 = __esm(() => {
30952
31731
  }))
30953
31732
  });
30954
31733
  DeployRequestSchema = exports_external.object({
31734
+ target: exports_external.enum(deploymentTargetEnum.enumValues).optional().default("game"),
30955
31735
  uploadToken: exports_external.string().optional(),
30956
31736
  code: exports_external.string().optional(),
30957
31737
  codeUploadToken: exports_external.string().optional(),
@@ -30987,6 +31767,9 @@ var init_schemas2 = __esm(() => {
30987
31767
  }).refine((data) => !(data.code && data.codeUploadToken), {
30988
31768
  message: "Specify either code or codeUploadToken, not both",
30989
31769
  path: ["codeUploadToken"]
31770
+ }).refine((data) => !(data.target === "dashboard" && (data.schema || data.bindings)), {
31771
+ message: "Dashboard deployments cannot include schema or bindings — they attach to the game deployment’s existing resources",
31772
+ path: ["target"]
30990
31773
  });
30991
31774
  });
30992
31775
 
@@ -74418,6 +75201,17 @@ function isValidUUID(value) {
74418
75201
  }
74419
75202
  return UUID_REGEX.test(value);
74420
75203
  }
75204
+ async function deterministicUUID(input) {
75205
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
75206
+ const bytes = new Uint8Array(digest).slice(0, 16);
75207
+ bytes[6] = bytes[6] & 15 | 80;
75208
+ bytes[8] = bytes[8] & 63 | 128;
75209
+ const hex3 = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
75210
+ return `${hex3.slice(0, 8)}-${hex3.slice(8, 12)}-${hex3.slice(12, 16)}-${hex3.slice(16, 20)}-${hex3.slice(20)}`;
75211
+ }
75212
+ async function localGameId(slug) {
75213
+ return deterministicUUID(`playcademy:local-game:${slug}`);
75214
+ }
74421
75215
  var UUID_REGEX;
74422
75216
  var init_uuid2 = __esm(() => {
74423
75217
  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 +77613,7 @@ function deriveTimebackCourseLevelFromGrade(grade) {
76819
77613
  return TIMEBACK_COURSE_DEFAULTS.level.high;
76820
77614
  }
76821
77615
  var ONEROSTER_STATUS2, SCORE_STATUS2, CACHE_DEFAULTS3, RESOURCE_DEFAULTS3;
76822
- var init_constants4 = __esm(() => {
77616
+ var init_constants3 = __esm(() => {
76823
77617
  init_src();
76824
77618
  ONEROSTER_STATUS2 = {
76825
77619
  active: "active",
@@ -77793,7 +78587,7 @@ function qtiIdCandidates(parentLineItem, resource) {
77793
78587
  }
77794
78588
  var GRADE_LEVEL_TEST_TYPES, naturalTitleSort;
77795
78589
  var init_timeback_grade_level_results_util = __esm(() => {
77796
- init_constants4();
78590
+ init_constants3();
77797
78591
  GRADE_LEVEL_TEST_TYPES = [
77798
78592
  "placement",
77799
78593
  "test out",
@@ -77988,7 +78782,7 @@ async function upsertMasteryCompletionEntry(params) {
77988
78782
  }
77989
78783
  var init_timeback_mastery_completion_util = __esm(async () => {
77990
78784
  init_spans();
77991
- init_constants4();
78785
+ init_constants3();
77992
78786
  init_utils6();
77993
78787
  await init_errors8();
77994
78788
  });
@@ -78684,7 +79478,7 @@ class TimebackAdminService {
78684
79478
  columns: { slug: true }
78685
79479
  }),
78686
79480
  this.deps.db.query.gameDeployments.findFirst({
78687
- where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.isActive, true)),
79481
+ where: activeDeploymentWhere(gameId),
78688
79482
  columns: { url: true }
78689
79483
  })
78690
79484
  ]);
@@ -79765,7 +80559,7 @@ var init_timeback_admin_service = __esm(async () => {
79765
80559
  init_schemas_index();
79766
80560
  init_tables_index();
79767
80561
  init_spans();
79768
- init_constants4();
80562
+ init_constants3();
79769
80563
  init_types2();
79770
80564
  init_utils6();
79771
80565
  init_src4();
@@ -80240,7 +81034,7 @@ function timebackConfigMatchesCreateIntegrationRequest(config4, request) {
80240
81034
  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
81035
  }
80242
81036
  var init_timeback_create_integration_util = __esm(() => {
80243
- init_constants4();
81037
+ init_constants3();
80244
81038
  init_types2();
80245
81039
  init_timeback_util();
80246
81040
  });
@@ -83086,6 +83880,19 @@ function createSandboxAuthProvider() {
83086
83880
  }
83087
83881
  return null;
83088
83882
  },
83883
+ async deleteApiKeysByName(name3) {
83884
+ const deleted = [];
83885
+ for (const [id, key] of sandboxApiKeys.entries()) {
83886
+ if (key.name === name3) {
83887
+ sandboxApiKeys.delete(id);
83888
+ deleted.push(id);
83889
+ }
83890
+ }
83891
+ if (deleted.length > 0) {
83892
+ log.debug("[SandboxAuthProvider] Deleted API keys by name", { name: name3, deleted });
83893
+ }
83894
+ return deleted;
83895
+ },
83089
83896
  async mintGameToken(gameId, userId) {
83090
83897
  const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
83091
83898
  const exp = Date.now() + 15 * 60 * 1000;
@@ -83164,6 +83971,12 @@ function createSandboxAuthProvider() {
83164
83971
  } catch {
83165
83972
  return null;
83166
83973
  }
83974
+ },
83975
+ async hashPassword(password) {
83976
+ return `sandbox:${btoa(password)}`;
83977
+ },
83978
+ async verifyPassword(data) {
83979
+ return data.hash === `sandbox:${btoa(data.password)}`;
83167
83980
  }
83168
83981
  };
83169
83982
  }
@@ -83448,7 +84261,7 @@ var init_http_exception = () => {};
83448
84261
 
83449
84262
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/request/constants.js
83450
84263
  var GET_MATCH_RESULT;
83451
- var init_constants5 = __esm(() => {
84264
+ var init_constants4 = __esm(() => {
83452
84265
  GET_MATCH_RESULT = Symbol();
83453
84266
  });
83454
84267
 
@@ -83712,7 +84525,7 @@ var init_url = __esm(() => {
83712
84525
  var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_), HonoRequest;
83713
84526
  var init_request = __esm(() => {
83714
84527
  init_http_exception();
83715
- init_constants5();
84528
+ init_constants4();
83716
84529
  init_body();
83717
84530
  init_url();
83718
84531
  HonoRequest = class {
@@ -84042,7 +84855,7 @@ var init_router = __esm(() => {
84042
84855
 
84043
84856
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/utils/constants.js
84044
84857
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
84045
- var init_constants6 = () => {};
84858
+ var init_constants5 = () => {};
84046
84859
 
84047
84860
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/hono-base.js
84048
84861
  var notFoundHandler = (c) => {
@@ -84264,7 +85077,7 @@ var init_hono_base = __esm(() => {
84264
85077
  init_compose();
84265
85078
  init_context2();
84266
85079
  init_router();
84267
- init_constants6();
85080
+ init_constants5();
84268
85081
  init_url();
84269
85082
  });
84270
85083
 
@@ -92934,7 +93747,7 @@ var require_node3 = __commonJS((exports) => {
92934
93747
  exports2.isAbsolute = function(aPath) {
92935
93748
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
92936
93749
  };
92937
- function relative(aRoot, aPath) {
93750
+ function relative2(aRoot, aPath) {
92938
93751
  if (aRoot === "") {
92939
93752
  aRoot = ".";
92940
93753
  }
@@ -92953,7 +93766,7 @@ var require_node3 = __commonJS((exports) => {
92953
93766
  }
92954
93767
  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
92955
93768
  }
92956
- exports2.relative = relative;
93769
+ exports2.relative = relative2;
92957
93770
  var supportsNullProto = function() {
92958
93771
  var obj = Object.create(null);
92959
93772
  return !("__proto__" in obj);
@@ -94984,7 +95797,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
94984
95797
  });
94985
95798
  };
94986
95799
  }
94987
- var readFile = (fp) => new Promise((resolve2, reject) => {
95800
+ var readFile2 = (fp) => new Promise((resolve2, reject) => {
94988
95801
  _fs.default.readFile(fp, "utf8", (err2, data) => {
94989
95802
  if (err2)
94990
95803
  return reject(err2);
@@ -95182,7 +95995,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
95182
95995
  data: _this3.packageJsonCache.get(filepath)[options.packageKey]
95183
95996
  };
95184
95997
  }
95185
- const data = _this3.options.parseJSON(yield readFile(filepath));
95998
+ const data = _this3.options.parseJSON(yield readFile2(filepath));
95186
95999
  return {
95187
96000
  path: filepath,
95188
96001
  data
@@ -95190,7 +96003,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
95190
96003
  }
95191
96004
  return {
95192
96005
  path: filepath,
95193
- data: yield readFile(filepath)
96006
+ data: yield readFile2(filepath)
95194
96007
  };
95195
96008
  }
95196
96009
  return {};
@@ -100235,7 +101048,7 @@ var __create2, __defProp2, __getOwnPropDesc, __getOwnPropNames2, __getProtoOf2,
100235
101048
  __defProp2(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
100236
101049
  }
100237
101050
  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) => {
101051
+ }, __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
101052
  const matchers = filters.map((it3) => {
100240
101053
  return new Minimatch(it3);
100241
101054
  });
@@ -119281,8 +120094,8 @@ ${BREAKPOINT}ALTER TABLE ${tableNameWithSchema} ADD CONSTRAINT "${statement.newC
119281
120094
  win32: { sep: "\\" },
119282
120095
  posix: { sep: "/" }
119283
120096
  };
119284
- sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
119285
- minimatch.sep = sep;
120097
+ sep2 = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
120098
+ minimatch.sep = sep2;
119286
120099
  GLOBSTAR = Symbol("globstar **");
119287
120100
  minimatch.GLOBSTAR = GLOBSTAR;
119288
120101
  plTypes = {
@@ -138184,7 +138997,7 @@ async function seedCoreGames(db2) {
138184
138997
  }
138185
138998
  async function seedCurrentProjectGame(db2, project) {
138186
138999
  const now2 = new Date;
138187
- const desiredGameId = project.gameId?.trim() || undefined;
139000
+ const desiredGameId = project.gameId?.trim() || await localGameId(project.slug);
138188
139001
  try {
138189
139002
  const existingGame = await db2.query.games.findFirst({
138190
139003
  where: (row, operators) => operators.eq(row.slug, project.slug)
@@ -138200,7 +139013,7 @@ async function seedCurrentProjectGame(db2, project) {
138200
139013
  }
138201
139014
  }
138202
139015
  const gameRecord = {
138203
- id: desiredGameId ?? crypto.randomUUID(),
139016
+ id: desiredGameId,
138204
139017
  slug: project.slug,
138205
139018
  displayName: project.displayName,
138206
139019
  version: project.version,
@@ -138235,6 +139048,7 @@ var init_games = __esm(() => {
138235
139048
  init_drizzle_orm();
138236
139049
  init_src();
138237
139050
  init_tables_index();
139051
+ init_uuid2();
138238
139052
  init_constants();
138239
139053
  init_logging();
138240
139054
  init_timeback5();
@@ -138820,19 +139634,50 @@ var init_types6 = () => {};
138820
139634
  function hasGameManagementAccess(user) {
138821
139635
  return user.role === "admin" || user.role === "teacher" || user.role === "developer" && user.developerStatus === "approved";
138822
139636
  }
139637
+ function isDashboardWorkerKey(key) {
139638
+ return Boolean(key?.permissions?.[DASHBOARD_PERMISSION_NAMESPACE]);
139639
+ }
139640
+ function workerKeyGrantsGameRead(key, slug2) {
139641
+ if (!isDashboardWorkerKey(key) && !isGameWorkerKey(key)) {
139642
+ return false;
139643
+ }
139644
+ const games2 = key?.permissions?.games;
139645
+ return Boolean(games2?.includes(`read:${slug2}`) || games2?.includes(`write:${slug2}`));
139646
+ }
139647
+ function isGameWorkerKey(key) {
139648
+ return Boolean(key?.name?.startsWith(GAME_WORKER_KEY_PREFIX));
139649
+ }
139650
+ function deniedWorkerKeyRequest(key, method, pathname) {
139651
+ if (isDashboardWorkerKey(key) && !isAllowedDashboardWorkerRequest(method, pathname, key.permissions)) {
139652
+ return DASHBOARD_WORKER_KEY_RESTRICTED;
139653
+ }
139654
+ return null;
139655
+ }
139656
+ function rejectDashboardWorkerKey(ctx) {
139657
+ if (isDashboardWorkerKey(ctx.apiKey)) {
139658
+ throw ApiError.forbidden(DASHBOARD_WORKER_KEY_RESTRICTED);
139659
+ }
139660
+ }
139661
+ function assertAuthenticatedRequest(ctx) {
139662
+ if (!isAuthenticated(ctx)) {
139663
+ throw ApiError.unauthorized("Valid session or bearer token required");
139664
+ }
139665
+ if (ctx.apiKey) {
139666
+ const denied = deniedWorkerKeyRequest(ctx.apiKey, ctx.request.method, ctx.url.pathname);
139667
+ if (denied) {
139668
+ throw ApiError.forbidden(denied);
139669
+ }
139670
+ }
139671
+ }
138823
139672
  function requireAuth(handler) {
138824
139673
  return async (ctx) => {
138825
- if (!isAuthenticated(ctx)) {
138826
- throw ApiError.unauthorized("Valid session or bearer token required");
138827
- }
139674
+ assertAuthenticatedRequest(ctx);
138828
139675
  return handler(ctx);
138829
139676
  };
138830
139677
  }
138831
139678
  function requireNonAnonymous(handler) {
138832
139679
  return async (ctx) => {
138833
- if (!isAuthenticated(ctx)) {
138834
- throw ApiError.unauthorized("Valid session or bearer token required");
138835
- }
139680
+ assertAuthenticatedRequest(ctx);
138836
139681
  if (ctx.user.isAnonymous) {
138837
139682
  throw ApiError.forbidden("This operation is not available for demo/anonymous users");
138838
139683
  }
@@ -138841,9 +139686,7 @@ function requireNonAnonymous(handler) {
138841
139686
  }
138842
139687
  function requireAnonymous(handler) {
138843
139688
  return async (ctx) => {
138844
- if (!isAuthenticated(ctx)) {
138845
- throw ApiError.unauthorized("Valid session or bearer token required");
138846
- }
139689
+ assertAuthenticatedRequest(ctx);
138847
139690
  if (!ctx.user.isAnonymous) {
138848
139691
  throw ApiError.forbidden("This operation is only available for demo/anonymous users");
138849
139692
  }
@@ -138852,9 +139695,8 @@ function requireAnonymous(handler) {
138852
139695
  }
138853
139696
  function requireAdmin(handler) {
138854
139697
  return async (ctx) => {
138855
- if (!isAuthenticated(ctx)) {
138856
- throw ApiError.unauthorized("Valid session or bearer token required");
138857
- }
139698
+ assertAuthenticatedRequest(ctx);
139699
+ rejectDashboardWorkerKey(ctx);
138858
139700
  if (ctx.user.role !== "admin") {
138859
139701
  throw ApiError.forbidden("Admin access required");
138860
139702
  }
@@ -138863,9 +139705,8 @@ function requireAdmin(handler) {
138863
139705
  }
138864
139706
  function requireDeveloper(handler) {
138865
139707
  return async (ctx) => {
138866
- if (!isAuthenticated(ctx)) {
138867
- throw ApiError.unauthorized("Valid session or bearer token required");
138868
- }
139708
+ assertAuthenticatedRequest(ctx);
139709
+ rejectDashboardWorkerKey(ctx);
138869
139710
  const isAdmin = ctx.user.role === "admin";
138870
139711
  const isApprovedDev = ctx.user.role === "developer" && ctx.user.developerStatus === "approved";
138871
139712
  if (!isAdmin && !isApprovedDev) {
@@ -138874,20 +139715,30 @@ function requireDeveloper(handler) {
138874
139715
  return handler(ctx);
138875
139716
  };
138876
139717
  }
139718
+ function requireHumanDeveloper(handler) {
139719
+ return requireDeveloper(async (ctx) => {
139720
+ if (isDashboardWorkerKey(ctx.apiKey) || isGameWorkerKey(ctx.apiKey)) {
139721
+ throw ApiError.forbidden(WORKER_KEY_HUMAN_ONLY);
139722
+ }
139723
+ return handler(ctx);
139724
+ });
139725
+ }
138877
139726
  function requireGameManagementAccess(handler) {
138878
139727
  return async (ctx) => {
138879
- if (!isAuthenticated(ctx)) {
138880
- throw ApiError.unauthorized("Valid session or bearer token required");
138881
- }
139728
+ assertAuthenticatedRequest(ctx);
139729
+ rejectDashboardWorkerKey(ctx);
138882
139730
  if (!hasGameManagementAccess(ctx.user)) {
138883
139731
  throw ApiError.forbidden("Game management access required");
138884
139732
  }
138885
139733
  return handler(ctx);
138886
139734
  };
138887
139735
  }
139736
+ var WORKER_KEY_HUMAN_ONLY = "This operation requires your own credential — platform worker keys are refused";
138888
139737
  var init_auth_util = __esm(() => {
138889
139738
  init_errors();
138890
139739
  init_types6();
139740
+ init_dashboard_util();
139741
+ init_deployment_util();
138891
139742
  });
138892
139743
 
138893
139744
  // ../api-core/src/utils/controller.util.ts
@@ -139031,6 +139882,21 @@ function requireGameId(gameId) {
139031
139882
  }
139032
139883
  return gameId;
139033
139884
  }
139885
+ function requireSlug(slug2) {
139886
+ if (!slug2) {
139887
+ throw ApiError.badRequest("Missing game slug");
139888
+ }
139889
+ return slug2;
139890
+ }
139891
+ function requireUserId(userId) {
139892
+ if (!userId) {
139893
+ throw ApiError.badRequest("Missing user ID");
139894
+ }
139895
+ if (!isValidUUID(userId)) {
139896
+ throw ApiError.unprocessableEntity("userId must be a valid UUID format");
139897
+ }
139898
+ return userId;
139899
+ }
139034
139900
  var init_params_util = __esm(() => {
139035
139901
  init_src4();
139036
139902
  init_errors();
@@ -139079,6 +139945,7 @@ var init_validation_util = __esm(() => {
139079
139945
  // ../api-core/src/utils/index.ts
139080
139946
  var init_utils11 = __esm(() => {
139081
139947
  init_auth_util();
139948
+ init_dashboard_util();
139082
139949
  init_deployment_util();
139083
139950
  init_leaderboard_util();
139084
139951
  init_lti_util();
@@ -139171,6 +140038,83 @@ var init_bucket_controller = __esm(() => {
139171
140038
  });
139172
140039
  });
139173
140040
 
140041
+ // ../api-core/src/controllers/dashboard.controller.ts
140042
+ function requireDashboardWorkerKey(ctx, slug2) {
140043
+ const scopes = ctx.apiKey?.permissions?.[DASHBOARD_PERMISSION_NAMESPACE];
140044
+ if (!scopes?.includes(dashboardCredentialScope(slug2))) {
140045
+ throw ApiError.forbidden("This endpoint requires the dashboard worker key for this game");
140046
+ }
140047
+ }
140048
+ var listUsers, addUser, issueRecoveryLink, removeUser, remove, verifyLogin, acceptInvite, revokeSessions, acceptReset, dashboard2;
140049
+ var init_dashboard_controller = __esm(() => {
140050
+ init_schemas_index();
140051
+ init_errors();
140052
+ init_utils11();
140053
+ listUsers = requireHumanDeveloper(async (ctx) => {
140054
+ const slug2 = requireSlug(ctx.params.slug);
140055
+ const users2 = await ctx.services.dashboard.listUsers(slug2, ctx.user);
140056
+ return { users: users2 };
140057
+ });
140058
+ addUser = requireHumanDeveloper(async (ctx) => {
140059
+ const slug2 = requireSlug(ctx.params.slug);
140060
+ const body2 = await parseRequestBody(ctx.request, AddGameDashboardUserSchema);
140061
+ return ctx.services.dashboard.addUser(slug2, ctx.user, body2);
140062
+ });
140063
+ issueRecoveryLink = requireHumanDeveloper(async (ctx) => {
140064
+ const slug2 = requireSlug(ctx.params.slug);
140065
+ const userId = requireUserId(ctx.params.userId);
140066
+ return ctx.services.dashboard.issueRecoveryLink(slug2, ctx.user, userId);
140067
+ });
140068
+ removeUser = requireHumanDeveloper(async (ctx) => {
140069
+ const slug2 = requireSlug(ctx.params.slug);
140070
+ const userId = requireUserId(ctx.params.userId);
140071
+ await ctx.services.dashboard.removeUser(slug2, ctx.user, userId);
140072
+ return { success: true };
140073
+ });
140074
+ remove = requireHumanDeveloper(async (ctx) => {
140075
+ const slug2 = requireSlug(ctx.params.slug);
140076
+ await ctx.services.deploy.removeDashboard(slug2, ctx.user);
140077
+ return { success: true };
140078
+ });
140079
+ verifyLogin = requireAuth(async (ctx) => {
140080
+ const slug2 = requireSlug(ctx.params.slug);
140081
+ requireDashboardWorkerKey(ctx, slug2);
140082
+ const body2 = await parseRequestBody(ctx.request, VerifyGameDashboardLoginSchema);
140083
+ const user = await ctx.services.dashboard.verifyLogin(slug2, body2);
140084
+ return { user };
140085
+ });
140086
+ acceptInvite = requireAuth(async (ctx) => {
140087
+ const slug2 = requireSlug(ctx.params.slug);
140088
+ requireDashboardWorkerKey(ctx, slug2);
140089
+ const body2 = await parseRequestBody(ctx.request, AcceptGameDashboardInviteSchema);
140090
+ const user = await ctx.services.dashboard.acceptInvite(slug2, body2);
140091
+ return { user };
140092
+ });
140093
+ revokeSessions = requireHumanDeveloper(async (ctx) => {
140094
+ const slug2 = requireSlug(ctx.params.slug);
140095
+ await ctx.services.deploy.rotateDashboardSessionSecret(slug2, ctx.user);
140096
+ return { success: true };
140097
+ });
140098
+ acceptReset = requireAuth(async (ctx) => {
140099
+ const slug2 = requireSlug(ctx.params.slug);
140100
+ requireDashboardWorkerKey(ctx, slug2);
140101
+ const body2 = await parseRequestBody(ctx.request, AcceptGameDashboardResetSchema);
140102
+ const user = await ctx.services.dashboard.acceptReset(slug2, body2);
140103
+ return { user };
140104
+ });
140105
+ dashboard2 = defineControllerNames("dashboard", {
140106
+ listUsers,
140107
+ addUser,
140108
+ removeUser,
140109
+ remove,
140110
+ issueRecoveryLink,
140111
+ revokeSessions,
140112
+ verifyLogin,
140113
+ acceptInvite,
140114
+ acceptReset
140115
+ });
140116
+ });
140117
+
139174
140118
  // ../api-core/src/controllers/database.controller.ts
139175
140119
  var reset, database;
139176
140120
  var init_database_controller = __esm(() => {
@@ -139265,7 +140209,7 @@ var init_developer_controller = __esm(() => {
139265
140209
  });
139266
140210
 
139267
140211
  // ../api-core/src/controllers/domain.controller.ts
139268
- var add, list, getStatus2, remove, domains2;
140212
+ var add, list, getStatus2, remove2, domains2;
139269
140213
  var init_domain_controller = __esm(() => {
139270
140214
  init_esm();
139271
140215
  init_schemas_index();
@@ -139314,7 +140258,7 @@ var init_domain_controller = __esm(() => {
139314
140258
  const environment = getPlatformEnvironment(ctx.config);
139315
140259
  return ctx.services.domain.getStatus(slug2, hostname4, environment, ctx.user, refresh);
139316
140260
  });
139317
- remove = requireDeveloper(async (ctx) => {
140261
+ remove2 = requireDeveloper(async (ctx) => {
139318
140262
  const slug2 = ctx.params.slug;
139319
140263
  const hostname4 = ctx.params.hostname;
139320
140264
  if (!slug2) {
@@ -139330,12 +140274,12 @@ var init_domain_controller = __esm(() => {
139330
140274
  add,
139331
140275
  list,
139332
140276
  getStatus: getStatus2,
139333
- remove
140277
+ remove: remove2
139334
140278
  });
139335
140279
  });
139336
140280
 
139337
140281
  // ../api-core/src/controllers/game-member.controller.ts
139338
- function requireUserId(userId) {
140282
+ function requireUserId2(userId) {
139339
140283
  if (!userId) {
139340
140284
  throw ApiError.badRequest("Missing user ID");
139341
140285
  }
@@ -139357,13 +140301,13 @@ var init_game_member_controller = __esm(() => {
139357
140301
  });
139358
140302
  updateMemberRole = requireNonAnonymous(async (ctx) => {
139359
140303
  const gameId = requireGameId(ctx.params.gameId);
139360
- const userId = requireUserId(ctx.params.userId);
140304
+ const userId = requireUserId2(ctx.params.userId);
139361
140305
  const body2 = await parseRequestBody(ctx.request, UpdateGameMemberRoleSchema);
139362
140306
  return ctx.services.gameMember.updateRole(gameId, userId, ctx.user, body2);
139363
140307
  });
139364
140308
  removeMember = requireNonAnonymous(async (ctx) => {
139365
140309
  const gameId = requireGameId(ctx.params.gameId);
139366
- const userId = requireUserId(ctx.params.userId);
140310
+ const userId = requireUserId2(ctx.params.userId);
139367
140311
  await ctx.services.gameMember.remove(gameId, userId, ctx.user);
139368
140312
  });
139369
140313
  searchUsersForMember = requireNonAnonymous(async (ctx) => {
@@ -139381,12 +140325,13 @@ var init_game_member_controller = __esm(() => {
139381
140325
  });
139382
140326
 
139383
140327
  // ../api-core/src/controllers/game.controller.ts
139384
- var list2, listAccessible, getSubjects, getTimebackSummaries, getById, getBySlug, getManifest, upsertBySlug, patchGameMetadata, remove2, games2;
140328
+ var list2, listAccessible, getSubjects, getTimebackSummaries, getById, getBySlug, getManifest, upsertBySlug, patchGameMetadata, remove3, games2;
139385
140329
  var init_game_controller = __esm(() => {
139386
140330
  init_esm();
139387
140331
  init_schemas_index();
139388
140332
  init_errors();
139389
140333
  init_utils11();
140334
+ init_auth_util();
139390
140335
  list2 = requireNonAnonymous(async (ctx) => ctx.services.game.list(ctx.user));
139391
140336
  listAccessible = requireNonAnonymous(async (ctx) => ctx.services.game.listAccessible(ctx.user));
139392
140337
  getSubjects = requireNonAnonymous(async (ctx) => ctx.services.game.getSubjects());
@@ -139400,7 +140345,9 @@ var init_game_controller = __esm(() => {
139400
140345
  if (!slug2) {
139401
140346
  throw ApiError.badRequest("Missing game slug");
139402
140347
  }
139403
- return ctx.services.game.getBySlug(slug2, ctx.user);
140348
+ return ctx.services.game.getBySlug(slug2, ctx.user, {
140349
+ scopeAuthorized: workerKeyGrantsGameRead(ctx.apiKey, slug2)
140350
+ });
139404
140351
  });
139405
140352
  getManifest = requireNonAnonymous(async (ctx) => {
139406
140353
  const gameId = requireGameId(ctx.params.gameId);
@@ -139430,7 +140377,7 @@ var init_game_controller = __esm(() => {
139430
140377
  const body2 = await parseRequestBody(ctx.request, PatchGameMetadataSchema);
139431
140378
  return ctx.services.game.updateMetadata(gameId, body2, ctx.user);
139432
140379
  });
139433
- remove2 = requireNonAnonymous(async (ctx) => {
140380
+ remove3 = requireNonAnonymous(async (ctx) => {
139434
140381
  const gameId = requireGameId(ctx.params.gameId);
139435
140382
  await ctx.services.game.delete(gameId, ctx.user);
139436
140383
  });
@@ -139444,7 +140391,7 @@ var init_game_controller = __esm(() => {
139444
140391
  getBySlug,
139445
140392
  upsertBySlug,
139446
140393
  patchGameMetadata,
139447
- remove: remove2
140394
+ remove: remove3
139448
140395
  });
139449
140396
  });
139450
140397
 
@@ -139819,7 +140766,7 @@ var init_session_controller = __esm(() => {
139819
140766
  let baseUrl;
139820
140767
  if (ctx.config.isLocal) {
139821
140768
  try {
139822
- baseUrl = await getTunnelUrl();
140769
+ baseUrl = await getTunnelUrl(ctx.config.baseUrl);
139823
140770
  } catch {}
139824
140771
  }
139825
140772
  return { token, exp, baseUrl };
@@ -140598,6 +141545,7 @@ var init_verify_controller = __esm(() => {
140598
141545
  // ../api-core/src/controllers/index.ts
140599
141546
  var init_controllers = __esm(() => {
140600
141547
  init_bucket_controller();
141548
+ init_dashboard_controller();
140601
141549
  init_database_controller();
140602
141550
  init_deploy_controller();
140603
141551
  init_developer_controller();
@@ -143746,7 +144694,7 @@ import { resolve as resolve3 } from "node:path";
143746
144694
 
143747
144695
  // ../utils/src/file-loader.ts
143748
144696
  import { existsSync as existsSync3, readdirSync, statSync } from "fs";
143749
- import { readFile } from "fs/promises";
144697
+ import { readFile as readFile2 } from "fs/promises";
143750
144698
  import { dirname as dirname2, parse as parse9, resolve as resolve2 } from "path";
143751
144699
  function findFilePath(filename, startDir, maxLevels = 3) {
143752
144700
  const filenames = Array.isArray(filename) ? filename : [filename];
@@ -143815,7 +144763,7 @@ async function loadFile(filename, options = {}) {
143815
144763
  return null;
143816
144764
  }
143817
144765
  try {
143818
- let content = await readFile(fileResult.path, "utf8");
144766
+ let content = await readFile2(fileResult.path, "utf8");
143819
144767
  if (parseJson) {
143820
144768
  if (stripComments) {
143821
144769
  content = stripJsonComments(content);
@@ -144044,6 +144992,9 @@ program2.name("playcademy-sandbox").description("Local development server for Pl
144044
144992
  process.on("SIGTERM", cleanup);
144045
144993
  } catch (error89) {
144046
144994
  console.error(`❌ Failed to start sandbox: ${error89}`);
144995
+ if (error89 instanceof Error && error89.message.includes("already in use")) {
144996
+ console.error("Stop the other server or specify a different port with --port <number>.");
144997
+ }
144047
144998
  process.exit(1);
144048
144999
  }
144049
145000
  });