@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/server.js CHANGED
@@ -203,6 +203,20 @@ var init_auth = __esm(() => {
203
203
  };
204
204
  });
205
205
 
206
+ // ../constants/src/dashboard.ts
207
+ var DASHBOARD_USER_ROLES, DASHBOARD_WORKER_ROUTES, DASHBOARD_WORKER_SUFFIX = "-dash";
208
+ var init_dashboard = __esm(() => {
209
+ DASHBOARD_USER_ROLES = {
210
+ ADMIN: "admin",
211
+ VIEWER: "viewer"
212
+ };
213
+ DASHBOARD_WORKER_ROUTES = {
214
+ verifySession: "/sessions",
215
+ acceptInvite: "/invites/accept",
216
+ acceptReset: "/resets/accept"
217
+ };
218
+ });
219
+
206
220
  // ../constants/src/typescript.ts
207
221
  var init_typescript = () => {};
208
222
 
@@ -244,17 +258,8 @@ var init_platform = __esm(() => {
244
258
  var PLATFORM_TIMEZONE = "America/New_York";
245
259
 
246
260
  // ../constants/src/timeback.ts
247
- 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;
261
+ 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;
248
262
  var init_timeback2 = __esm(() => {
249
- TIMEBACK_ROUTES = {
250
- END_ACTIVITY: "/integrations/timeback/end-activity",
251
- GET_XP: "/integrations/timeback/xp",
252
- GET_MASTERY: "/integrations/timeback/mastery",
253
- GET_HIGHEST_GRADE_MASTERED: "/integrations/timeback/highest-grade-mastered",
254
- HEARTBEAT: "/integrations/timeback/heartbeat",
255
- ADVANCE_COURSE: "/integrations/timeback/advance-course",
256
- UNENROLL_COURSE: "/integrations/timeback/unenroll-course"
257
- };
258
263
  TIMEBACK_COURSE_DEFAULTS = {
259
264
  gradingScheme: "STANDARD",
260
265
  level: {
@@ -305,7 +310,7 @@ var init_timeback2 = __esm(() => {
305
310
  });
306
311
 
307
312
  // ../constants/src/cloudflare.ts
308
- var WORKER_NAMING, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
313
+ var WORKER_NAMING, MAX_WORKER_NAME_LENGTH = 63, SECRETS_PREFIX = "secrets_", CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
309
314
  var init_cloudflare = __esm(() => {
310
315
  WORKER_NAMING = {
311
316
  STAGING_PREFIX: "staging-",
@@ -317,6 +322,7 @@ var init_cloudflare = __esm(() => {
317
322
  // ../constants/src/index.ts
318
323
  var init_src = __esm(() => {
319
324
  init_auth();
325
+ init_dashboard();
320
326
  init_typescript();
321
327
  init_domains();
322
328
  init_env_vars();
@@ -1056,8 +1062,7 @@ async function waitForPort(port, timeoutMs = 5000) {
1056
1062
  const start2 = Date.now();
1057
1063
  while (await isPortInUse(port)) {
1058
1064
  if (Date.now() - start2 > timeoutMs) {
1059
- throw new Error(`Port ${port} is already in use.
1060
- ` + `Stop the other server or specify a different port with --port <number>.`);
1065
+ throw new Error(`Port ${port} is already in use`);
1061
1066
  }
1062
1067
  await new Promise((resolve) => setTimeout(resolve, 100));
1063
1068
  }
@@ -1066,8 +1071,7 @@ async function requirePortAvailable(port, timeoutMs = 100) {
1066
1071
  const start2 = Date.now();
1067
1072
  while (await isPortInUse(port)) {
1068
1073
  if (Date.now() - start2 > timeoutMs) {
1069
- throw new Error(`Port ${port} is already in use.
1070
- ` + `Stop the other server or specify a different port with --port <number>.`);
1074
+ throw new Error(`Port ${port} is already in use`);
1071
1075
  }
1072
1076
  await new Promise((resolve) => setTimeout(resolve, 50));
1073
1077
  }
@@ -1079,7 +1083,7 @@ var package_default;
1079
1083
  var init_package = __esm(() => {
1080
1084
  package_default = {
1081
1085
  name: "@playcademy/sandbox",
1082
- version: "0.6.1-beta.4",
1086
+ version: "0.6.1-beta.6",
1083
1087
  description: "Local development server for Playcademy game development",
1084
1088
  type: "module",
1085
1089
  exports: {
@@ -1148,7 +1152,7 @@ var init_package = __esm(() => {
1148
1152
  });
1149
1153
 
1150
1154
  // ../api-core/src/errors/domain.error.ts
1151
- var DomainError, BadRequestError, AccessDeniedError, NotFoundError, ConflictError, AlreadyExistsError, ValidationError, InternalError, ServiceUnavailableError, TimeoutError;
1155
+ var DomainError, BadRequestError, UnauthorizedError, AccessDeniedError, NotFoundError, ConflictError, AlreadyExistsError, ValidationError, RateLimitError, InternalError, ServiceUnavailableError, TimeoutError;
1152
1156
  var init_domain_error = __esm(() => {
1153
1157
  DomainError = class DomainError extends Error {
1154
1158
  code;
@@ -1166,6 +1170,12 @@ var init_domain_error = __esm(() => {
1166
1170
  this.name = "BadRequestError";
1167
1171
  }
1168
1172
  };
1173
+ UnauthorizedError = class UnauthorizedError extends DomainError {
1174
+ constructor(message = "Unauthorized", details) {
1175
+ super("UNAUTHORIZED", message, details);
1176
+ this.name = "UnauthorizedError";
1177
+ }
1178
+ };
1169
1179
  AccessDeniedError = class AccessDeniedError extends DomainError {
1170
1180
  constructor(message = "Access denied", details) {
1171
1181
  super("ACCESS_DENIED", message, details);
@@ -1198,6 +1208,12 @@ var init_domain_error = __esm(() => {
1198
1208
  this.name = "ValidationError";
1199
1209
  }
1200
1210
  };
1211
+ RateLimitError = class RateLimitError extends DomainError {
1212
+ constructor(message = "Rate limit exceeded", details) {
1213
+ super("RATE_LIMITED", message, details);
1214
+ this.name = "RateLimitError";
1215
+ }
1216
+ };
1201
1217
  InternalError = class InternalError extends DomainError {
1202
1218
  constructor(message = "Internal error", details) {
1203
1219
  super("INTERNAL", message, details);
@@ -1556,12 +1572,24 @@ data: ${JSON.stringify(data)}
1556
1572
  return sseEncoder.encode(payload);
1557
1573
  }
1558
1574
  function createContextBuilder(options) {
1559
- const { db, config: config2, providers, services, cloudflare: cloudflare2, timeback: timeback2, resolveUser, resolveGameId } = options;
1575
+ const {
1576
+ db,
1577
+ config: config2,
1578
+ providers,
1579
+ services,
1580
+ cloudflare: cloudflare2,
1581
+ timeback: timeback2,
1582
+ resolveUser,
1583
+ resolveGameId,
1584
+ resolveApiKey
1585
+ } = options;
1560
1586
  return async (c) => {
1561
1587
  const user = resolveUser ? await resolveUser(c) : null;
1562
1588
  const gameId = resolveGameId ? await resolveGameId(c) : undefined;
1589
+ const apiKey = resolveApiKey ? await resolveApiKey(c) : undefined;
1563
1590
  return {
1564
1591
  user,
1592
+ apiKey,
1565
1593
  gameId,
1566
1594
  params: c.req.param(),
1567
1595
  url: new URL(c.req.url),
@@ -5660,6 +5688,14 @@ var init_schema = __esm(() => {
5660
5688
  platformServiceJwt: platformServiceJwtConfigSchema.optional(),
5661
5689
  uploadBucket: exports_external.string().optional(),
5662
5690
  queueIngressSecret: exports_external.string().optional()
5691
+ }).superRefine((config2, ctx) => {
5692
+ if (config2.isLocal && !config2.baseUrl) {
5693
+ ctx.addIssue({
5694
+ code: exports_external.ZodIssueCode.custom,
5695
+ path: ["baseUrl"],
5696
+ message: "baseUrl is required when isLocal is true"
5697
+ });
5698
+ }
5663
5699
  });
5664
5700
  });
5665
5701
 
@@ -11180,7 +11216,7 @@ var init_table4 = __esm(() => {
11180
11216
  });
11181
11217
 
11182
11218
  // ../data/src/domains/game/table.ts
11183
- var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, deploymentProviderEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
11219
+ var gamePlatformEnum, gameTypeEnum, gameVisibilityEnum, games, gameMemberRoleEnum, gameMembers, gameMembersRelations, gameDashboardUserRoleEnum, gameDashboardUsers, gameDashboardUsersRelations, deploymentProviderEnum, deploymentTargetEnum, deployJobStatusEnum, gameDeployments, gameDeployJobs, customHostnameStatusEnum, customHostnameSslStatusEnum, customHostnameEnvironmentEnum, gameCustomHostnames;
11184
11220
  var init_table5 = __esm(() => {
11185
11221
  init_drizzle_orm();
11186
11222
  init_pg_core();
@@ -11224,7 +11260,37 @@ var init_table5 = __esm(() => {
11224
11260
  references: [users.id]
11225
11261
  })
11226
11262
  }));
11263
+ gameDashboardUserRoleEnum = pgEnum("game_dashboard_user_role", [
11264
+ DASHBOARD_USER_ROLES.ADMIN,
11265
+ DASHBOARD_USER_ROLES.VIEWER
11266
+ ]);
11267
+ gameDashboardUsers = pgTable("game_dashboard_users", {
11268
+ id: uuid("id").primaryKey().defaultRandom(),
11269
+ gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
11270
+ email: text("email").notNull(),
11271
+ displayName: text("display_name"),
11272
+ role: gameDashboardUserRoleEnum("role").notNull().default(DASHBOARD_USER_ROLES.VIEWER),
11273
+ passwordHash: text("password_hash"),
11274
+ inviteTokenHash: text("invite_token_hash"),
11275
+ inviteExpiresAt: timestamp("invite_expires_at", { withTimezone: true }),
11276
+ resetTokenHash: text("reset_token_hash"),
11277
+ resetExpiresAt: timestamp("reset_expires_at", { withTimezone: true }),
11278
+ failedLoginAttempts: integer("failed_login_attempts").notNull().default(0),
11279
+ lastFailedLoginAt: timestamp("last_failed_login_at", { withTimezone: true }),
11280
+ platformUserId: text("platform_user_id").references(() => users.id, {
11281
+ onDelete: "set null"
11282
+ }),
11283
+ createdAt: timestamp("created_at", { withTimezone: true }).notNull().defaultNow(),
11284
+ updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
11285
+ }, (table3) => [uniqueIndex("game_dashboard_users_game_email_idx").on(table3.gameId, table3.email)]);
11286
+ gameDashboardUsersRelations = relations(gameDashboardUsers, ({ one }) => ({
11287
+ game: one(games, {
11288
+ fields: [gameDashboardUsers.gameId],
11289
+ references: [games.id]
11290
+ })
11291
+ }));
11227
11292
  deploymentProviderEnum = pgEnum("deployment_provider", ["cloudflare", "aws"]);
11293
+ deploymentTargetEnum = pgEnum("deployment_target", ["game", "dashboard"]);
11228
11294
  deployJobStatusEnum = pgEnum("deploy_job_status", [
11229
11295
  "pending",
11230
11296
  "running",
@@ -11236,6 +11302,7 @@ var init_table5 = __esm(() => {
11236
11302
  gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
11237
11303
  deploymentId: text("deployment_id").notNull(),
11238
11304
  provider: deploymentProviderEnum("provider").notNull(),
11305
+ target: deploymentTargetEnum("target").notNull().default("game"),
11239
11306
  url: text("url").notNull(),
11240
11307
  codeHash: text("code_hash"),
11241
11308
  isActive: boolean("is_active").notNull().default(false),
@@ -11423,8 +11490,12 @@ __export(exports_tables_index, {
11423
11490
  gameMemberRoleEnum: () => gameMemberRoleEnum,
11424
11491
  gameDeployments: () => gameDeployments,
11425
11492
  gameDeployJobs: () => gameDeployJobs,
11493
+ gameDashboardUsersRelations: () => gameDashboardUsersRelations,
11494
+ gameDashboardUsers: () => gameDashboardUsers,
11495
+ gameDashboardUserRoleEnum: () => gameDashboardUserRoleEnum,
11426
11496
  gameCustomHostnames: () => gameCustomHostnames,
11427
11497
  developerStatusEnum: () => developerStatusEnum,
11498
+ deploymentTargetEnum: () => deploymentTargetEnum,
11428
11499
  deploymentProviderEnum: () => deploymentProviderEnum,
11429
11500
  deployJobStatusEnum: () => deployJobStatusEnum,
11430
11501
  customHostnameStatusEnum: () => customHostnameStatusEnum,
@@ -12928,6 +12999,10 @@ function errorMessage(err2) {
12928
12999
  function errorType(err2) {
12929
13000
  return err2 instanceof Error ? err2.constructor.name : typeof err2;
12930
13001
  }
13002
+ function isUniqueViolation(err2) {
13003
+ return typeof err2 === "object" && err2 !== null && err2.code === PG_UNIQUE_VIOLATION;
13004
+ }
13005
+ var PG_UNIQUE_VIOLATION = "23505";
12931
13006
 
12932
13007
  // ../otel/src/spans.ts
12933
13008
  function truncate(value) {
@@ -13006,6 +13081,310 @@ var init_spans = __esm(() => {
13006
13081
  ROOT_SPAN_KEY = import_api2.createContextKey("playcademy.root_span");
13007
13082
  });
13008
13083
 
13084
+ // ../utils/src/random.ts
13085
+ async function generateSecureRandomString(length) {
13086
+ const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
13087
+ const randomValues = new Uint8Array(length);
13088
+ crypto.getRandomValues(randomValues);
13089
+ return [...randomValues].map((byte) => charset[byte % charset.length]).join("");
13090
+ }
13091
+
13092
+ // ../api-core/src/utils/hash.util.ts
13093
+ async function sha256Hex(input) {
13094
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
13095
+ return [...new Uint8Array(digest)].map((b) => b.toString(16).padStart(2, "0")).join("");
13096
+ }
13097
+
13098
+ // ../api-core/src/utils/dashboard.util.ts
13099
+ function dashboardCredentialScope(slug) {
13100
+ return `${CREDENTIAL_SCOPE_PREFIX}${slug}`;
13101
+ }
13102
+ function dashboardCredentialSlugs(permissions) {
13103
+ 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);
13104
+ }
13105
+ function isAllowedDashboardWorkerRequest(method, pathname, permissions) {
13106
+ const path = pathname.replace(/\/+$/, "");
13107
+ return dashboardCredentialSlugs(permissions).some((slug) => {
13108
+ if (method === "GET") {
13109
+ return path === `/api/games/${slug}`;
13110
+ }
13111
+ if (method === "POST") {
13112
+ return Object.values(DASHBOARD_WORKER_ROUTES).some((subpath) => path === `/api/games/${slug}/dashboard${subpath}`);
13113
+ }
13114
+ return false;
13115
+ });
13116
+ }
13117
+ function hashDashboardToken(token) {
13118
+ return sha256Hex(token);
13119
+ }
13120
+ function loginBackoffMs(failedAttempts) {
13121
+ if (failedAttempts < LOGIN_BACKOFF_THRESHOLD) {
13122
+ return 0;
13123
+ }
13124
+ return Math.min(LOGIN_BACKOFF_CAP_MS, LOGIN_BACKOFF_BASE_MS * 2 ** (failedAttempts - LOGIN_BACKOFF_THRESHOLD));
13125
+ }
13126
+ 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;
13127
+ var init_dashboard_util = __esm(() => {
13128
+ init_src();
13129
+ LOGIN_BACKOFF_CAP_MS = 5 * 60 * 1000;
13130
+ });
13131
+
13132
+ // ../api-core/src/services/dashboard.service.ts
13133
+ function alreadyExists(gameId) {
13134
+ return new AlreadyExistsError("A dashboard user with this email already exists", { gameId });
13135
+ }
13136
+ var DashboardService;
13137
+ var init_dashboard_service = __esm(() => {
13138
+ init_drizzle_orm();
13139
+ init_tables_index();
13140
+ init_spans();
13141
+ init_errors();
13142
+ init_dashboard_util();
13143
+ DashboardService = class DashboardService {
13144
+ deps;
13145
+ static INVITE_TOKEN_LENGTH = 48;
13146
+ static INVITE_TTL_MS = 72 * 60 * 60 * 1000;
13147
+ static RESET_TTL_MS = 24 * 60 * 60 * 1000;
13148
+ constructor(deps) {
13149
+ this.deps = deps;
13150
+ }
13151
+ async listUsers(slug, user) {
13152
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13153
+ const rows = await this.deps.db.select({
13154
+ id: gameDashboardUsers.id,
13155
+ gameId: gameDashboardUsers.gameId,
13156
+ email: gameDashboardUsers.email,
13157
+ displayName: gameDashboardUsers.displayName,
13158
+ role: gameDashboardUsers.role,
13159
+ platformUserId: gameDashboardUsers.platformUserId,
13160
+ createdAt: gameDashboardUsers.createdAt,
13161
+ updatedAt: gameDashboardUsers.updatedAt,
13162
+ pending: sql`(${gameDashboardUsers.passwordHash} IS NULL)`
13163
+ }).from(gameDashboardUsers).where(eq(gameDashboardUsers.gameId, game2.id)).orderBy(asc(gameDashboardUsers.createdAt));
13164
+ setAttributes({
13165
+ "app.dashboard.operation": "list_users",
13166
+ "app.dashboard.result_count": rows.length
13167
+ });
13168
+ return rows;
13169
+ }
13170
+ async addUser(slug, user, input) {
13171
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13172
+ const email = input.email.toLowerCase();
13173
+ setAttributes({
13174
+ "app.dashboard.operation": "add_user",
13175
+ "app.dashboard.user_role": input.role
13176
+ });
13177
+ const existing = await this.findByEmail(game2.id, email);
13178
+ if (existing) {
13179
+ throw alreadyExists(game2.id);
13180
+ }
13181
+ const inviteToken = await generateSecureRandomString(DashboardService.INVITE_TOKEN_LENGTH);
13182
+ let created;
13183
+ try {
13184
+ [created] = await this.deps.db.insert(gameDashboardUsers).values({
13185
+ gameId: game2.id,
13186
+ email,
13187
+ displayName: input.displayName,
13188
+ role: input.role,
13189
+ inviteTokenHash: await hashDashboardToken(inviteToken),
13190
+ inviteExpiresAt: new Date(Date.now() + DashboardService.INVITE_TTL_MS),
13191
+ platformUserId: email === user.email.toLowerCase() ? user.id : undefined
13192
+ }).returning();
13193
+ } catch (error) {
13194
+ if (isUniqueViolation(error)) {
13195
+ throw alreadyExists(game2.id);
13196
+ }
13197
+ throw error;
13198
+ }
13199
+ if (!created) {
13200
+ throw new ValidationError("Failed to create dashboard user");
13201
+ }
13202
+ return { user: DashboardService.toSummary(created), token: inviteToken, kind: "invite" };
13203
+ }
13204
+ async removeUser(slug, user, dashboardUserId) {
13205
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13206
+ setAttributes({
13207
+ "app.dashboard.operation": "remove_user",
13208
+ "app.dashboard.user_id": dashboardUserId
13209
+ });
13210
+ const [deleted] = await this.deps.db.delete(gameDashboardUsers).where(and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.id, dashboardUserId))).returning({ id: gameDashboardUsers.id });
13211
+ if (!deleted) {
13212
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13213
+ }
13214
+ }
13215
+ async verifyLogin(slug, input) {
13216
+ const game2 = await this.getGameForRuntime(slug);
13217
+ setAttributes({ "app.dashboard.operation": "verify_login" });
13218
+ const dashboardUser = await this.findByEmail(game2.id, input.email.toLowerCase());
13219
+ if (!dashboardUser?.passwordHash) {
13220
+ throw new UnauthorizedError("Invalid credentials");
13221
+ }
13222
+ if (dashboardUser.lastFailedLoginAt) {
13223
+ const retryAt = dashboardUser.lastFailedLoginAt.getTime() + loginBackoffMs(dashboardUser.failedLoginAttempts);
13224
+ if (retryAt > Date.now()) {
13225
+ addEvent("dashboard.login_throttled", {
13226
+ "app.dashboard.failed_attempts": dashboardUser.failedLoginAttempts
13227
+ });
13228
+ throw new RateLimitError("Too many failed attempts — try again shortly", {
13229
+ retryAfterSeconds: Math.ceil((retryAt - Date.now()) / 1000)
13230
+ });
13231
+ }
13232
+ }
13233
+ const valid = await this.deps.verifyPassword({
13234
+ hash: dashboardUser.passwordHash,
13235
+ password: input.password
13236
+ });
13237
+ if (!valid) {
13238
+ await this.deps.db.update(gameDashboardUsers).set({
13239
+ failedLoginAttempts: sql`${gameDashboardUsers.failedLoginAttempts} + 1`,
13240
+ lastFailedLoginAt: new Date
13241
+ }).where(eq(gameDashboardUsers.id, dashboardUser.id));
13242
+ throw new UnauthorizedError("Invalid credentials");
13243
+ }
13244
+ if (dashboardUser.failedLoginAttempts > 0) {
13245
+ await this.deps.db.update(gameDashboardUsers).set({ failedLoginAttempts: 0, lastFailedLoginAt: null }).where(eq(gameDashboardUsers.id, dashboardUser.id));
13246
+ }
13247
+ return DashboardService.toSummary(dashboardUser);
13248
+ }
13249
+ async acceptInvite(slug, input) {
13250
+ const game2 = await this.getGameForRuntime(slug);
13251
+ setAttributes({ "app.dashboard.operation": "accept_invite" });
13252
+ const tokenHash = await hashDashboardToken(input.token);
13253
+ const invited = await this.deps.db.query.gameDashboardUsers.findFirst({
13254
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.inviteTokenHash, tokenHash))
13255
+ });
13256
+ if (!invited) {
13257
+ throw new UnauthorizedError("Invalid or already-used invite token");
13258
+ }
13259
+ if (invited.inviteExpiresAt && invited.inviteExpiresAt.getTime() < Date.now()) {
13260
+ throw new UnauthorizedError("This invite has expired — ask for a new one");
13261
+ }
13262
+ const passwordHash = await this.deps.hashPassword(input.password);
13263
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({
13264
+ passwordHash,
13265
+ inviteTokenHash: null,
13266
+ inviteExpiresAt: null,
13267
+ updatedAt: new Date
13268
+ }).where(and(eq(gameDashboardUsers.id, invited.id), eq(gameDashboardUsers.inviteTokenHash, tokenHash), isNull(gameDashboardUsers.passwordHash))).returning();
13269
+ if (!updated) {
13270
+ throw new UnauthorizedError("Invalid or already-used invite token");
13271
+ }
13272
+ return DashboardService.toSummary(updated);
13273
+ }
13274
+ async acceptReset(slug, input) {
13275
+ const game2 = await this.getGameForRuntime(slug);
13276
+ setAttributes({ "app.dashboard.operation": "accept_reset" });
13277
+ const tokenHash = await hashDashboardToken(input.token);
13278
+ const found = await this.deps.db.query.gameDashboardUsers.findFirst({
13279
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.resetTokenHash, tokenHash))
13280
+ });
13281
+ if (!found) {
13282
+ throw new UnauthorizedError("Invalid or already-used reset token");
13283
+ }
13284
+ if (found.resetExpiresAt && found.resetExpiresAt.getTime() < Date.now()) {
13285
+ throw new UnauthorizedError("This reset link has expired — ask for a new one");
13286
+ }
13287
+ const passwordHash = await this.deps.hashPassword(input.password);
13288
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({
13289
+ passwordHash,
13290
+ resetTokenHash: null,
13291
+ resetExpiresAt: null,
13292
+ inviteTokenHash: null,
13293
+ inviteExpiresAt: null,
13294
+ failedLoginAttempts: 0,
13295
+ lastFailedLoginAt: null,
13296
+ updatedAt: new Date
13297
+ }).where(and(eq(gameDashboardUsers.id, found.id), eq(gameDashboardUsers.resetTokenHash, tokenHash))).returning();
13298
+ if (!updated) {
13299
+ throw new UnauthorizedError("Invalid or already-used reset token");
13300
+ }
13301
+ return DashboardService.toSummary(updated);
13302
+ }
13303
+ async issueRecoveryLink(slug, user, dashboardUserId) {
13304
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
13305
+ const existing = await this.deps.db.query.gameDashboardUsers.findFirst({
13306
+ where: and(eq(gameDashboardUsers.gameId, game2.id), eq(gameDashboardUsers.id, dashboardUserId))
13307
+ });
13308
+ if (!existing) {
13309
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13310
+ }
13311
+ const kind = existing.passwordHash ? "reset" : "invite";
13312
+ setAttributes({
13313
+ "app.dashboard.operation": "issue_recovery_link",
13314
+ "app.dashboard.recovery_kind": kind
13315
+ });
13316
+ const token = await generateSecureRandomString(DashboardService.INVITE_TOKEN_LENGTH);
13317
+ const tokenHash = await hashDashboardToken(token);
13318
+ const fields = kind === "invite" ? {
13319
+ inviteTokenHash: tokenHash,
13320
+ inviteExpiresAt: new Date(Date.now() + DashboardService.INVITE_TTL_MS)
13321
+ } : {
13322
+ resetTokenHash: tokenHash,
13323
+ resetExpiresAt: new Date(Date.now() + DashboardService.RESET_TTL_MS)
13324
+ };
13325
+ const [updated] = await this.deps.db.update(gameDashboardUsers).set({ ...fields, updatedAt: new Date }).where(eq(gameDashboardUsers.id, existing.id)).returning();
13326
+ if (!updated) {
13327
+ throw new NotFoundError("Dashboard user", dashboardUserId);
13328
+ }
13329
+ return { user: DashboardService.toSummary(updated), token, kind };
13330
+ }
13331
+ async getGameForRuntime(slug) {
13332
+ const game2 = await this.deps.db.query.games.findFirst({
13333
+ where: eq(games.slug, slug),
13334
+ columns: { id: true }
13335
+ });
13336
+ if (!game2) {
13337
+ throw new NotFoundError("Game", slug);
13338
+ }
13339
+ return game2;
13340
+ }
13341
+ async findByEmail(gameId, email) {
13342
+ const row = await this.deps.db.query.gameDashboardUsers.findFirst({
13343
+ where: and(eq(gameDashboardUsers.gameId, gameId), eq(gameDashboardUsers.email, email))
13344
+ });
13345
+ return row ?? null;
13346
+ }
13347
+ static toSummary(row) {
13348
+ return {
13349
+ id: row.id,
13350
+ gameId: row.gameId,
13351
+ email: row.email,
13352
+ displayName: row.displayName,
13353
+ role: row.role,
13354
+ platformUserId: row.platformUserId,
13355
+ createdAt: row.createdAt,
13356
+ updatedAt: row.updatedAt,
13357
+ pending: !row.passwordHash
13358
+ };
13359
+ }
13360
+ };
13361
+ });
13362
+
13363
+ // ../data/src/domains/game/helpers.ts
13364
+ function activeDeploymentWhere(gameId, target = "game") {
13365
+ return and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.target, target), eq(gameDeployments.isActive, true));
13366
+ }
13367
+ var init_helpers = __esm(() => {
13368
+ init_drizzle_orm();
13369
+ init_table5();
13370
+ });
13371
+
13372
+ // ../data/src/domains/timeback/helpers.ts
13373
+ function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
13374
+ return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
13375
+ }
13376
+ var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active", DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
13377
+ var init_helpers2 = __esm(() => {
13378
+ init_drizzle_orm();
13379
+ init_table7();
13380
+ });
13381
+
13382
+ // ../data/src/helpers.index.ts
13383
+ var init_helpers_index = __esm(() => {
13384
+ init_helpers();
13385
+ init_helpers2();
13386
+ });
13387
+
13009
13388
  // ../../node_modules/.bun/process-nextick-args@2.0.1/node_modules/process-nextick-args/index.js
13010
13389
  var require_process_nextick_args = __commonJS((exports, module2) => {
13011
13390
  if (typeof process === "undefined" || !process.version || process.version.indexOf("v0.") === 0 || process.version.indexOf("v1.") === 0 && process.version.indexOf("v1.8.") !== 0) {
@@ -22086,8 +22465,8 @@ var require_lib3 = __commonJS((exports, module2) => {
22086
22465
  });
22087
22466
 
22088
22467
  // ../utils/src/zip.ts
22089
- import { mkdir, writeFile } from "fs/promises";
22090
- import { join as join2 } from "path";
22468
+ import { mkdir, readdir, readFile, writeFile } from "fs/promises";
22469
+ import { join as join2, relative, sep } from "path";
22091
22470
  async function extractZipToDirectory(zipBuffer, targetDir) {
22092
22471
  try {
22093
22472
  const zip = await import_jszip.default.loadAsync(zipBuffer);
@@ -22113,9 +22492,10 @@ async function extractZipToDirectory(zipBuffer, targetDir) {
22113
22492
  fileCount: Object.keys(zip.files).length
22114
22493
  });
22115
22494
  } catch (error) {
22116
- const errorMessage2 = error instanceof Error ? error.message : String(error);
22117
22495
  log.error("[utils/zip] Failed to extract ZIP", { targetDir, error });
22118
- throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage2}`, { cause: error });
22496
+ throw new Error(`Failed to extract ZIP to ${targetDir}: ${errorMessage(error)}`, {
22497
+ cause: error
22498
+ });
22119
22499
  }
22120
22500
  }
22121
22501
  var import_jszip;
@@ -22385,6 +22765,7 @@ class DeployJobService {
22385
22765
  jobId,
22386
22766
  leaseId,
22387
22767
  leaseLost,
22768
+ target,
22388
22769
  game: game2,
22389
22770
  user,
22390
22771
  error
@@ -22414,9 +22795,12 @@ class DeployJobService {
22414
22795
  }
22415
22796
  }
22416
22797
  if (!effectiveLeaseLost) {
22417
- await this.deps.notifyDeploymentFailure(game2.slug, game2.displayName, message, {
22418
- id: user.id,
22419
- email: user.email
22798
+ await this.deps.notifyDeploymentFailure({
22799
+ slug: game2.slug,
22800
+ displayName: game2.displayName,
22801
+ error: message,
22802
+ target,
22803
+ developer: { id: user.id, email: user.email }
22420
22804
  });
22421
22805
  }
22422
22806
  await this.deleteCodeBundle(jobId);
@@ -22518,7 +22902,7 @@ class DeployJobService {
22518
22902
  }
22519
22903
  assertLease();
22520
22904
  const activeDeployment = await this.deps.db.query.gameDeployments.findFirst({
22521
- where: and(eq(gameDeployments.gameId, job.gameId), eq(gameDeployments.isActive, true)),
22905
+ where: activeDeploymentWhere(job.gameId, request.target ?? "game"),
22522
22906
  columns: {
22523
22907
  deploymentId: true,
22524
22908
  url: true
@@ -22541,7 +22925,15 @@ class DeployJobService {
22541
22925
  } catch (error) {
22542
22926
  clearInterval(heartbeat);
22543
22927
  setAttribute("app.deploy_job.run_duration_ms", DeployJobService.elapsedMsSince(new Date(runStartedAt)));
22544
- await this.handleRunError({ jobId, leaseId, leaseLost, game: game2, user, error });
22928
+ await this.handleRunError({
22929
+ jobId,
22930
+ leaseId,
22931
+ leaseLost,
22932
+ target: request.target ?? "game",
22933
+ game: game2,
22934
+ user,
22935
+ error
22936
+ });
22545
22937
  throw error;
22546
22938
  }
22547
22939
  }
@@ -22560,6 +22952,7 @@ async function forceMarkDeployJobFailed(db2, jobId, error) {
22560
22952
  var STATUS_MAP2, DEPLOY_JOB_LEASE_MS, DEPLOY_JOB_HEARTBEAT_MS, DEPLOY_JOB_CODE_BUNDLE_PREFIX = "deploy-job-code";
22561
22953
  var init_deploy_job_service = __esm(() => {
22562
22954
  init_drizzle_orm();
22955
+ init_helpers_index();
22563
22956
  init_tables_index();
22564
22957
  init_spans();
22565
22958
  init_zip();
@@ -24703,7 +25096,7 @@ var init_bindings = __esm(() => {
24703
25096
  });
24704
25097
 
24705
25098
  // ../cloudflare/src/playcademy/provider/helpers.ts
24706
- var init_helpers = __esm(() => {
25099
+ var init_helpers3 = __esm(() => {
24707
25100
  init_mime();
24708
25101
  });
24709
25102
 
@@ -24715,7 +25108,7 @@ var init_provider = __esm(() => {
24715
25108
  init_constants2();
24716
25109
  init_assets_config();
24717
25110
  init_bindings();
24718
- init_helpers();
25111
+ init_helpers3();
24719
25112
  });
24720
25113
  // ../cloudflare/src/playcademy/provider/index.ts
24721
25114
  var init_provider2 = __esm(() => {
@@ -27134,26 +27527,70 @@ var init_playcademy = __esm(() => {
27134
27527
  });
27135
27528
 
27136
27529
  // ../utils/src/tunnel.ts
27137
- async function getTunnelUrl() {
27138
- let response;
27530
+ function servicePort(service) {
27531
+ if (!service) {
27532
+ return null;
27533
+ }
27139
27534
  try {
27140
- response = await fetch(`${METRICS_BASE}/config`);
27535
+ const url = new URL(service);
27536
+ return url.port || (url.protocol === "https:" ? "443" : "80");
27141
27537
  } catch {
27142
- throw new Error("Local tunnel is not running. Start it with `bun dev` or `bun scripts/infra/tunnel.ts`.");
27538
+ return null;
27143
27539
  }
27144
- if (!response.ok) {
27145
- throw new Error(`Tunnel metrics endpoint returned ${response.status}`);
27540
+ }
27541
+ async function readTunnelIngress(port) {
27542
+ try {
27543
+ const response = await fetch(`http://127.0.0.1:${port}/config`, {
27544
+ signal: AbortSignal.timeout(1000)
27545
+ });
27546
+ if (!response.ok) {
27547
+ return null;
27548
+ }
27549
+ const data = await response.json();
27550
+ return data.config?.ingress ?? [];
27551
+ } catch {
27552
+ return null;
27553
+ }
27554
+ }
27555
+ function matchingHostname(rules, expectedPort) {
27556
+ for (const rule of rules ?? []) {
27557
+ if (rule.hostname && servicePort(rule.service) === expectedPort) {
27558
+ return rule.hostname;
27559
+ }
27560
+ }
27561
+ return null;
27562
+ }
27563
+ async function getTunnelUrl(platformBaseUrl) {
27564
+ const expectedPort = servicePort(platformBaseUrl);
27565
+ if (!expectedPort) {
27566
+ throw new Error(`Cannot resolve a tunnel for unparseable platform URL '${platformBaseUrl}'`);
27567
+ }
27568
+ const cachedPort = matchedPorts.get(expectedPort);
27569
+ if (cachedPort !== undefined) {
27570
+ const hostname = matchingHostname(await readTunnelIngress(cachedPort), expectedPort);
27571
+ if (hostname) {
27572
+ return `https://${hostname}`;
27573
+ }
27574
+ matchedPorts.delete(expectedPort);
27575
+ }
27576
+ const ports = Array.from({ length: TUNNEL_METRICS_PORT_RANGE }, (_, offset) => TUNNEL_METRICS_PORT + offset);
27577
+ const results = await Promise.all(ports.map(async (port) => ({ port, rules: await readTunnelIngress(port) })));
27578
+ for (const { port, rules } of results) {
27579
+ const hostname = matchingHostname(rules, expectedPort);
27580
+ if (hostname) {
27581
+ matchedPorts.set(expectedPort, port);
27582
+ return `https://${hostname}`;
27583
+ }
27146
27584
  }
27147
- const data = await response.json();
27148
- const hostname = data.config.ingress.find((r) => r.hostname)?.hostname;
27149
- if (!hostname) {
27150
- throw new Error("Tunnel is running but no hostname found in ingress config");
27585
+ const foreign = results.flatMap(({ rules }) => (rules ?? []).filter((rule) => rule.hostname).map((rule) => `${rule.hostname} -> ${rule.service}`));
27586
+ if (foreign.length > 0) {
27587
+ throw new Error(`A local tunnel is running, but none route to the platform at ${platformBaseUrl} (found: ${foreign.join(", ")}). ${TUNNEL_START_HINT}`);
27151
27588
  }
27152
- return `https://${hostname}`;
27589
+ throw new Error(`Local tunnel is not running. ${TUNNEL_START_HINT}`);
27153
27590
  }
27154
- var TUNNEL_METRICS_PORT = 20241, METRICS_BASE;
27591
+ 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;
27155
27592
  var init_tunnel = __esm(() => {
27156
- METRICS_BASE = `http://127.0.0.1:${TUNNEL_METRICS_PORT}`;
27593
+ matchedPorts = new Map;
27157
27594
  });
27158
27595
 
27159
27596
  // ../utils/src/stages.ts
@@ -27166,7 +27603,7 @@ var init_stages = __esm(() => {
27166
27603
  });
27167
27604
 
27168
27605
  // ../api-core/src/utils/deployment.util.ts
27169
- function getDeploymentId(gameSlug, sstStage) {
27606
+ function getGameDeploymentId(gameSlug, sstStage) {
27170
27607
  if (sstStage === "production") {
27171
27608
  return gameSlug;
27172
27609
  }
@@ -27175,45 +27612,57 @@ function getDeploymentId(gameSlug, sstStage) {
27175
27612
  }
27176
27613
  return `${WORKER_NAMING.LOCAL_PREFIX}${sstStage}-${gameSlug}`;
27177
27614
  }
27615
+ function getDashboardDeploymentId(gameSlug, sstStage) {
27616
+ return `${getGameDeploymentId(gameSlug, sstStage)}${DASHBOARD_WORKER_SUFFIX}`;
27617
+ }
27178
27618
  function getGameWorkerApiKeyName(slug) {
27179
- return `game-worker-${slug}`.substring(0, 32);
27619
+ return `${GAME_WORKER_KEY_PREFIX}${slug}`.substring(0, 32);
27620
+ }
27621
+ function getDashboardWorkerApiKeyName(slug) {
27622
+ return `${DASHBOARD_WORKER_KEY_PREFIX}${slug}`;
27180
27623
  }
27181
27624
  function toBindingName(queueKey) {
27182
27625
  return `${queueKey.replace(/-/g, "_").toUpperCase()}_QUEUE`;
27183
27626
  }
27184
- async function generateDeploymentHash(code) {
27185
- const encoder = new TextEncoder;
27186
- const data = encoder.encode(code);
27187
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
27188
- const hashArray = [...new Uint8Array(hashBuffer)];
27189
- return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
27627
+ function generateDeploymentHash(code) {
27628
+ return sha256Hex(code);
27190
27629
  }
27630
+ var GAME_WORKER_KEY_PREFIX = "game-worker-", DASHBOARD_WORKER_KEY_PREFIX = "dash-worker-";
27191
27631
  var init_deployment_util = __esm(() => {
27192
27632
  init_src();
27193
27633
  init_stages();
27194
27634
  });
27195
27635
 
27636
+ // ../api-core/src/utils/worker-keys.util.ts
27637
+ async function sweepDashboardWorkerKeys(deleteApiKeysByName, slug) {
27638
+ try {
27639
+ await deleteApiKeysByName(getDashboardWorkerApiKeyName(slug));
27640
+ } catch (error) {
27641
+ addEvent("dashboard.worker_key_sweep_failed", {
27642
+ "exception.type": errorType(error),
27643
+ "app.error.message": errorMessage(error)
27644
+ });
27645
+ }
27646
+ }
27647
+ var init_worker_keys_util = __esm(() => {
27648
+ init_spans();
27649
+ init_deployment_util();
27650
+ });
27651
+
27196
27652
  // ../api-core/src/services/deploy.service.ts
27653
+ function readDashboardTheme(config2) {
27654
+ const theme = config2?.dashboard?.theme;
27655
+ return {
27656
+ ...typeof theme?.primary === "string" && { primary: theme.primary },
27657
+ ...typeof theme?.secondary === "string" && { secondary: theme.secondary }
27658
+ };
27659
+ }
27660
+
27197
27661
  class DeployService {
27198
27662
  deps;
27199
27663
  constructor(deps) {
27200
27664
  this.deps = deps;
27201
27665
  }
27202
- static classifyDeployType(flags2) {
27203
- if (flags2.hasBackend && flags2.hasFrontend) {
27204
- return "full_stack";
27205
- }
27206
- if (flags2.hasFrontend) {
27207
- return "frontend_only";
27208
- }
27209
- if (flags2.hasBackend) {
27210
- return "backend_only";
27211
- }
27212
- return "metadata_only";
27213
- }
27214
- static countDeadLetterQueues(bindings) {
27215
- return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
27216
- }
27217
27666
  getCloudflare() {
27218
27667
  const cf = this.deps.cloudflare;
27219
27668
  if (!cf) {
@@ -27221,86 +27670,307 @@ class DeployService {
27221
27670
  }
27222
27671
  return cf;
27223
27672
  }
27224
- async createApiKey(user, slug, keyName) {
27225
- const { key: apiKey } = await this.deps.createAuthApiKey({
27226
- userId: user.id,
27227
- name: keyName,
27228
- expiresIn: null,
27229
- permissions: {
27230
- games: [`read:${slug}`, `write:${slug}`]
27231
- },
27232
- rateLimitEnabled: false
27233
- });
27234
- setAttribute("app.deploy.api_key_outcome", "created");
27235
- return apiKey;
27236
- }
27237
- async regenerateApiKey(user, slug, existingKeyId, keyName) {
27238
- if (existingKeyId) {
27239
- setAttribute("app.deploy.api_key_outcome", "regenerated");
27240
- try {
27241
- await this.deps.deleteAuthApiKey(existingKeyId);
27242
- } catch (error) {
27243
- addEvent("deploy.api_key_revoke_failed", {
27244
- "exception.type": errorType(error),
27245
- "app.error.message": errorMessage(error),
27246
- "app.deploy.old_key_id": existingKeyId
27247
- });
27673
+ async* deploy(slug, request, user, uploadDeps, extractZip) {
27674
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
27675
+ const context2 = { slug, request, user, game: game2, uploadDeps, extractZip };
27676
+ switch (request.target ?? "game") {
27677
+ case "dashboard": {
27678
+ yield* this.deployDashboard(context2);
27679
+ break;
27680
+ }
27681
+ case "game": {
27682
+ yield* this.deployGame(context2);
27683
+ break;
27684
+ }
27685
+ default: {
27686
+ throw new ValidationError("Invalid deployment target");
27248
27687
  }
27249
27688
  }
27250
- return this.createApiKey(user, slug, keyName);
27251
27689
  }
27252
- async ensureApiKeyOnWorker(user, slug, deploymentId) {
27690
+ async* deployGame(context2) {
27691
+ const { slug, request, user, game: game2, uploadDeps, extractZip } = context2;
27253
27692
  const cf = this.getCloudflare();
27254
- const keyName = getGameWorkerApiKeyName(slug);
27255
- const existingKey = await this.deps.db.query.apikey.findFirst({
27256
- where: and(eq(apikey.userId, user.id), eq(apikey.name, keyName)),
27693
+ const db2 = this.deps.db;
27694
+ const flags2 = this.validateGameRequest(request);
27695
+ const { hasBackend, hasFrontend } = flags2;
27696
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
27697
+ let frontendAssetsPath;
27698
+ let tempDir;
27699
+ if (hasFrontend) {
27700
+ if (!uploadDeps || !extractZip) {
27701
+ throw new ValidationError("Upload dependencies not configured for frontend deployment");
27702
+ }
27703
+ yield { type: "status", data: { message: "Fetching temporary files" } };
27704
+ const extracted = await this.fetchAndExtractFrontendAssets(slug, game2.id, request.uploadToken, uploadDeps, extractZip);
27705
+ tempDir = extracted.tempDir;
27706
+ frontendAssetsPath = extracted.assetsPath;
27707
+ yield { type: "status", data: { message: "Extracting assets" } };
27708
+ }
27709
+ const platformBaseUrl = await this.resolvePlatformBaseUrl();
27710
+ const env = {
27711
+ GAME_ID: game2.id,
27712
+ PLAYCADEMY_BASE_URL: platformBaseUrl,
27713
+ PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
27714
+ };
27715
+ yield {
27716
+ type: "status",
27717
+ data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
27718
+ };
27719
+ const keepAssets = hasBackend && !hasFrontend;
27720
+ const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings, request.schema);
27721
+ const bindings = deploymentOptions?.bindings;
27722
+ setAttributes({
27723
+ "app.deploy.has_d1": Boolean(bindings?.d1?.length),
27724
+ "app.deploy.has_kv": Boolean(bindings?.kv?.length),
27725
+ "app.deploy.has_r2": Boolean(bindings?.r2?.length),
27726
+ "app.deploy.queue_count": bindings?.queues?.length ?? 0,
27727
+ "app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
27728
+ });
27729
+ const activeDeployment = await db2.query.gameDeployments.findFirst({
27730
+ where: activeDeploymentWhere(game2.id, "game"),
27731
+ columns: { resources: true }
27732
+ });
27733
+ if (deploymentOptions?.bindings?.d1?.length) {
27734
+ await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
27735
+ }
27736
+ const result = await this.deployToCloudflare({
27737
+ deploymentId,
27738
+ code: request.code,
27739
+ env,
27740
+ tempDir,
27741
+ options: {
27742
+ ...deploymentOptions,
27743
+ compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27744
+ compatibilityFlags: request.compatibilityFlags,
27745
+ existingResources: activeDeployment?.resources ?? undefined,
27746
+ assetsPath: frontendAssetsPath,
27747
+ keepAssets
27748
+ }
27749
+ });
27750
+ yield { type: "status", data: { message: "Finalizing deployment" } };
27751
+ const updatedGame = await this.finalizeGameDeployment({
27752
+ game: game2,
27753
+ slug,
27754
+ deploymentId,
27755
+ result,
27756
+ request,
27757
+ user,
27758
+ flags: flags2
27759
+ });
27760
+ yield { type: "complete", data: updatedGame };
27761
+ }
27762
+ async* deployDashboard(context2) {
27763
+ const { slug, request, user, game: game2 } = context2;
27764
+ const db2 = this.deps.db;
27765
+ const contract = this.validateDashboardRequest(request, context2.uploadDeps, context2.extractZip);
27766
+ const deploymentId = getDashboardDeploymentId(slug, this.deps.config.sstStage);
27767
+ if (deploymentId.length > MAX_WORKER_NAME_LENGTH) {
27768
+ throw new ValidationError(`Dashboard worker name '${deploymentId}' exceeds Cloudflare's ${MAX_WORKER_NAME_LENGTH}-character limit — shorten the game slug`);
27769
+ }
27770
+ const collidingGame = await db2.query.games.findFirst({
27771
+ where: eq(games.slug, `${slug}${DASHBOARD_WORKER_SUFFIX}`),
27257
27772
  columns: { id: true }
27258
27773
  });
27259
- let apiKey;
27260
- if (!existingKey) {
27261
- apiKey = await this.createApiKey(user, slug, keyName);
27262
- } else {
27263
- try {
27264
- const workerSecrets = await cf.listSecrets(deploymentId);
27265
- if (workerSecrets.includes("PLAYCADEMY_API_KEY")) {
27266
- setAttribute("app.deploy.api_key_outcome", "already_set");
27267
- return;
27268
- }
27269
- } catch (error) {
27270
- addEvent("deploy.worker_secrets_check_failed", {
27271
- "exception.type": errorType(error),
27272
- "app.error.message": errorMessage(error)
27273
- });
27774
+ if (collidingGame) {
27775
+ throw new ValidationError(`Cannot deploy this dashboard: an existing game's slug is '${slug}${DASHBOARD_WORKER_SUFFIX}', which owns the '${deploymentId}' worker name`);
27776
+ }
27777
+ const gameDeployment = await db2.query.gameDeployments.findFirst({
27778
+ where: activeDeploymentWhere(game2.id, "game"),
27779
+ columns: { resources: true }
27780
+ });
27781
+ if (!gameDeployment) {
27782
+ throw new ValidationError("Deploy the game before its dashboard — the dashboard attaches to the game deployment");
27783
+ }
27784
+ setAttributes({
27785
+ "app.deploy.type": "dashboard",
27786
+ "app.deploy.deployment_id": deploymentId,
27787
+ "app.deploy.code_size": contract.code.length,
27788
+ "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27789
+ "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0
27790
+ });
27791
+ yield { type: "status", data: { message: "Fetching temporary files" } };
27792
+ const [{ tempDir, assetsPath }, platformBaseUrl, dashboardActive] = await Promise.all([
27793
+ this.fetchAndExtractFrontendAssets(slug, game2.id, contract.uploadToken, contract.uploadDeps, contract.extractZip),
27794
+ this.resolvePlatformBaseUrl(),
27795
+ db2.query.gameDeployments.findFirst({
27796
+ where: activeDeploymentWhere(game2.id, "dashboard"),
27797
+ columns: { resources: true }
27798
+ })
27799
+ ]);
27800
+ yield { type: "status", data: { message: "Extracting assets" } };
27801
+ const theme = readDashboardTheme(request.config);
27802
+ const env = {
27803
+ GAME_ID: game2.id,
27804
+ GAME_SLUG: slug,
27805
+ GAME_NAME: game2.displayName,
27806
+ ...game2.metadata?.emoji && { GAME_EMOJI: game2.metadata.emoji },
27807
+ ...theme.primary && { DASHBOARD_THEME_PRIMARY: theme.primary },
27808
+ ...theme.secondary && { DASHBOARD_THEME_SECONDARY: theme.secondary },
27809
+ PLAYCADEMY_BASE_URL: platformBaseUrl,
27810
+ PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
27811
+ };
27812
+ const gameResources = gameDeployment.resources;
27813
+ const bindings = {
27814
+ d1: gameResources?.d1?.map((database) => database.name) ?? [],
27815
+ kv: gameResources?.kv?.map((namespace) => namespace.name) ?? [],
27816
+ r2: gameResources?.r2?.filter((bucket) => bucket.kind !== "assets").map((bucket) => bucket.name) ?? []
27817
+ };
27818
+ const existingResources = {
27819
+ ...dashboardActive?.resources,
27820
+ d1: gameResources?.d1,
27821
+ kv: gameResources?.kv,
27822
+ r2: gameResources?.r2
27823
+ };
27824
+ yield { type: "status", data: { message: "Deploying dashboard" } };
27825
+ const result = await this.deployToCloudflare({
27826
+ deploymentId,
27827
+ code: contract.code,
27828
+ env,
27829
+ tempDir,
27830
+ options: {
27831
+ bindings,
27832
+ compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27833
+ compatibilityFlags: request.compatibilityFlags,
27834
+ existingResources,
27835
+ assetsPath,
27836
+ keepAssets: false,
27837
+ assetsConfig: { run_worker_first: true }
27274
27838
  }
27275
- apiKey = await this.regenerateApiKey(user, slug, existingKey.id, keyName);
27839
+ });
27840
+ yield { type: "status", data: { message: "Finalizing deployment" } };
27841
+ await withSpan("deploy.configure_worker_secrets", async () => {
27842
+ const existingSecrets = await this.listWorkerSecretNames(result.deploymentId);
27843
+ await this.ensureWorkerApiKeyOnWorker(user, DeployService.dashboardWorkerKeySpec(slug), result.deploymentId, existingSecrets);
27844
+ await this.ensureDashboardSessionSecret(result.deploymentId, existingSecrets, Boolean(dashboardActive));
27845
+ });
27846
+ const codeHash = await generateDeploymentHash(contract.code);
27847
+ await this.saveDeployment({
27848
+ gameId: game2.id,
27849
+ deploymentId: result.deploymentId,
27850
+ url: result.url,
27851
+ codeHash,
27852
+ resources: result.resources,
27853
+ target: "dashboard"
27854
+ });
27855
+ setAttribute("app.deploy.code_hash", codeHash);
27856
+ try {
27857
+ await withSpan("deploy.send_alert", () => this.deps.alerts.notifyDashboardDeployment({
27858
+ slug,
27859
+ displayName: game2.displayName,
27860
+ url: result.url,
27861
+ developer: { id: user.id, email: user.email }
27862
+ }));
27863
+ } catch (error) {
27864
+ setAttribute("app.alerts.delivery", "failed");
27865
+ setAttribute("app.alerts.delivery_error", errorMessage(error));
27866
+ setAttribute("app.alerts.type", "dashboard_deployment");
27276
27867
  }
27277
- await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
27868
+ yield { type: "complete", data: game2 };
27278
27869
  }
27279
- async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
27280
- const secret = this.deps.config.queueIngressSecret;
27281
- if (!secret) {
27282
- return;
27870
+ validateGameRequest(request) {
27871
+ const hasBackend = Boolean(request.code);
27872
+ const hasFrontend = Boolean(request.uploadToken);
27873
+ const hasMetadata = Boolean(request.metadata);
27874
+ setAttributes({
27875
+ "app.deploy.has_backend": hasBackend,
27876
+ "app.deploy.has_frontend": hasFrontend,
27877
+ "app.deploy.has_metadata": hasMetadata,
27878
+ "app.deploy.type": DeployService.classifyGameDeployType({
27879
+ hasBackend,
27880
+ hasFrontend,
27881
+ hasMetadata
27882
+ }),
27883
+ "app.deploy.code_size": request.code?.length ?? 0,
27884
+ "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27885
+ "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
27886
+ "app.deploy.has_schema": Boolean(request.schema)
27887
+ });
27888
+ if (!hasBackend && !hasFrontend && !hasMetadata) {
27889
+ throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
27283
27890
  }
27284
- const cf = this.getCloudflare();
27285
- await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
27891
+ if (!this.deps.config.baseUrl) {
27892
+ throw new ValidationError("baseUrl is not configured");
27893
+ }
27894
+ return { hasBackend, hasFrontend, hasMetadata };
27286
27895
  }
27287
- async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
27288
- const frontendZip = await withSpan("deploy.fetch_temporary_files", () => uploadDeps.getObjectAsByteArray(uploadToken));
27289
- if (!frontendZip || frontendZip.length === 0) {
27290
- throw new ValidationError("Uploaded file is empty or not found");
27896
+ validateDashboardRequest(request, uploadDeps, extractZip) {
27897
+ if (!request.code || !request.uploadToken) {
27898
+ throw new ValidationError("Dashboard deployments require both worker code and an asset upload");
27291
27899
  }
27292
- setAttribute("app.deploy.asset_upload_size", frontendZip.length);
27293
- const os2 = await import("os");
27294
- const path = await import("path");
27295
- const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
27296
- const assetsPath = path.join(tempDir, "dist");
27297
- await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
27298
- uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
27299
- return { frontendZip, tempDir, assetsPath };
27900
+ if (request.metadata) {
27901
+ throw new ValidationError("Dashboard deployments cannot update game metadata");
27902
+ }
27903
+ if (!uploadDeps || !extractZip) {
27904
+ throw new ValidationError("Upload dependencies not configured for frontend deployment");
27905
+ }
27906
+ if (!this.deps.config.baseUrl) {
27907
+ throw new ValidationError("baseUrl is not configured");
27908
+ }
27909
+ return { code: request.code, uploadToken: request.uploadToken, uploadDeps, extractZip };
27910
+ }
27911
+ static classifyGameDeployType(flags2) {
27912
+ if (flags2.hasBackend && flags2.hasFrontend) {
27913
+ return "full_stack";
27914
+ }
27915
+ if (flags2.hasFrontend) {
27916
+ return "frontend_only";
27917
+ }
27918
+ if (flags2.hasBackend) {
27919
+ return "backend_only";
27920
+ }
27921
+ return "metadata_only";
27922
+ }
27923
+ mapGameBindingsToOptions(deploymentId, bindings, schema2) {
27924
+ if (!bindings && !schema2) {
27925
+ return;
27926
+ }
27927
+ const workerBindings = {};
27928
+ if (bindings?.database) {
27929
+ workerBindings.d1 = [deploymentId];
27930
+ }
27931
+ if (bindings?.keyValue) {
27932
+ workerBindings.kv = [deploymentId];
27933
+ }
27934
+ if (bindings?.bucket) {
27935
+ workerBindings.r2 = [deploymentId];
27936
+ }
27937
+ if (bindings?.queues) {
27938
+ let toQueueName = function(queueKey) {
27939
+ return `${QUEUE_NAME_PREFIX}-${deploymentId}--${queueKey}`;
27940
+ };
27941
+ const queueNameByKey = new Map;
27942
+ for (const queueKey of Object.keys(bindings.queues)) {
27943
+ queueNameByKey.set(queueKey, toQueueName(queueKey));
27944
+ }
27945
+ workerBindings.queues = Object.entries(bindings.queues).map(([queueKey, queueConfig]) => {
27946
+ const config2 = queueConfig === true ? undefined : queueConfig;
27947
+ const deadLetterQueue = config2?.deadLetterQueue && queueNameByKey.get(config2.deadLetterQueue) ? queueNameByKey.get(config2.deadLetterQueue) : undefined;
27948
+ return {
27949
+ bindingName: toBindingName(queueKey),
27950
+ queueName: toQueueName(queueKey),
27951
+ settings: config2 ? {
27952
+ maxBatchSize: config2.maxBatchSize,
27953
+ maxRetries: config2.maxRetries,
27954
+ maxBatchTimeout: config2.maxBatchTimeout,
27955
+ maxConcurrency: config2.maxConcurrency,
27956
+ retryDelay: config2.retryDelay
27957
+ } : undefined,
27958
+ deadLetterQueue
27959
+ };
27960
+ });
27961
+ }
27962
+ const hasBindings = workerBindings.d1?.length || workerBindings.kv?.length || workerBindings.r2?.length || workerBindings.queues?.length;
27963
+ return {
27964
+ ...hasBindings && { bindings: workerBindings },
27965
+ ...schema2 && { schema: schema2 }
27966
+ };
27967
+ }
27968
+ static countDeadLetterQueues(bindings) {
27969
+ return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
27300
27970
  }
27301
27971
  async cleanupOrphanD1Databases(cf, d1Names, slug, gameId) {
27302
27972
  const hasAnyPriorDeployment = await this.deps.db.query.gameDeployments.findFirst({
27303
- where: eq(gameDeployments.gameId, gameId),
27973
+ where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.target, "game")),
27304
27974
  columns: { id: true }
27305
27975
  });
27306
27976
  if (hasAnyPriorDeployment) {
@@ -27335,33 +28005,7 @@ class DeployService {
27335
28005
  }
27336
28006
  await this.deps.db.update(games).set(updates).where(eq(games.id, gameId));
27337
28007
  }
27338
- validateDeployRequest(request) {
27339
- const hasBackend = Boolean(request.code);
27340
- const hasFrontend = Boolean(request.uploadToken);
27341
- const hasMetadata = Boolean(request.metadata);
27342
- setAttributes({
27343
- "app.deploy.has_backend": hasBackend,
27344
- "app.deploy.has_frontend": hasFrontend,
27345
- "app.deploy.has_metadata": hasMetadata,
27346
- "app.deploy.type": DeployService.classifyDeployType({
27347
- hasBackend,
27348
- hasFrontend,
27349
- hasMetadata
27350
- }),
27351
- "app.deploy.code_size": request.code?.length ?? 0,
27352
- "app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27353
- "app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
27354
- "app.deploy.has_schema": Boolean(request.schema)
27355
- });
27356
- if (!hasBackend && !hasFrontend && !hasMetadata) {
27357
- throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
27358
- }
27359
- if (!this.deps.config.baseUrl) {
27360
- throw new ValidationError("baseUrl is not configured");
27361
- }
27362
- return { hasBackend, hasFrontend, hasMetadata };
27363
- }
27364
- async finalizeDeployment({
28008
+ async finalizeGameDeployment({
27365
28009
  game: game2,
27366
28010
  slug,
27367
28011
  deploymentId,
@@ -27373,10 +28017,17 @@ class DeployService {
27373
28017
  const { hasBackend, hasFrontend, hasMetadata } = flags2;
27374
28018
  const db2 = this.deps.db;
27375
28019
  const codeHash = hasBackend ? await generateDeploymentHash(request.code) : null;
27376
- await this.saveDeployment(game2.id, result.deploymentId, result.url, codeHash, result.resources);
28020
+ await this.saveDeployment({
28021
+ gameId: game2.id,
28022
+ deploymentId: result.deploymentId,
28023
+ url: result.url,
28024
+ codeHash,
28025
+ resources: result.resources,
28026
+ target: "game"
28027
+ });
27377
28028
  if (hasBackend) {
27378
28029
  await withSpan("deploy.configure_worker_secrets", async () => {
27379
- await this.ensureApiKeyOnWorker(user, slug, result.deploymentId);
28030
+ await this.ensureWorkerApiKeyOnWorker(user, DeployService.gameWorkerKeySpec(slug), result.deploymentId);
27380
28031
  await this.ensureQueueIngressSecretOnWorker(slug, result.deploymentId);
27381
28032
  });
27382
28033
  }
@@ -27409,169 +28060,232 @@ class DeployService {
27409
28060
  }
27410
28061
  return updatedGame;
27411
28062
  }
27412
- async* deploy(slug, request, user, uploadDeps, extractZip) {
27413
- const cf = this.getCloudflare();
27414
- const db2 = this.deps.db;
27415
- const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
27416
- const flags2 = this.validateDeployRequest(request);
27417
- const { hasBackend, hasFrontend } = flags2;
27418
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
27419
- let frontendAssetsPath;
27420
- let tempDir;
27421
- if (hasFrontend) {
27422
- if (!uploadDeps || !extractZip) {
27423
- throw new ValidationError("Upload dependencies not configured for frontend deployment");
27424
- }
27425
- yield { type: "status", data: { message: "Fetching temporary files" } };
27426
- const extracted = await this.fetchAndExtractFrontendAssets(slug, game2.id, request.uploadToken, uploadDeps, extractZip);
27427
- tempDir = extracted.tempDir;
27428
- frontendAssetsPath = extracted.assetsPath;
27429
- yield { type: "status", data: { message: "Extracting assets" } };
27430
- }
27431
- let platformBaseUrl = this.deps.config.baseUrl;
27432
- if (this.deps.config.isLocal) {
27433
- try {
27434
- platformBaseUrl = await getTunnelUrl();
27435
- } catch {
27436
- setAttributes({
27437
- "app.deploy.tunnel_available": false,
27438
- "app.deploy.failure_kind": "local_tunnel_unavailable"
27439
- });
27440
- throw new ValidationError("Local tunnel is not running. Ensure cloudflared is installed (`brew install cloudflared`) and the tunnel DevCommand started successfully.");
27441
- }
27442
- }
27443
- const env = {
27444
- GAME_ID: game2.id,
27445
- PLAYCADEMY_BASE_URL: platformBaseUrl,
27446
- PLAYCADEMY_PLATFORM_SERVICE_JWKS: this.deps.config.platformServiceJwt?.publicJwks
28063
+ static gameWorkerKeySpec(slug) {
28064
+ return {
28065
+ keyName: getGameWorkerApiKeyName(slug),
28066
+ permissions: { games: [`read:${slug}`, `write:${slug}`] },
28067
+ lookup: "owner-and-name"
27447
28068
  };
27448
- yield {
27449
- type: "status",
27450
- data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
28069
+ }
28070
+ static dashboardWorkerKeySpec(slug) {
28071
+ return {
28072
+ keyName: getDashboardWorkerApiKeyName(slug),
28073
+ permissions: {
28074
+ games: [`read:${slug}`],
28075
+ [DASHBOARD_PERMISSION_NAMESPACE]: [dashboardCredentialScope(slug)]
28076
+ },
28077
+ rateLimit: { maxRequests: 300, timeWindowMs: 60000 },
28078
+ lookup: "name",
28079
+ isCurrent: (permissionsJson) => DeployService.hasDashboardCredentialScope(permissionsJson, slug)
27451
28080
  };
27452
- const keepAssets = hasBackend && !hasFrontend;
27453
- const deploymentOptions = this.mapBindingsToOptions(deploymentId, request.bindings, request.schema);
27454
- const bindings = deploymentOptions?.bindings;
27455
- setAttributes({
27456
- "app.deploy.has_d1": Boolean(bindings?.d1?.length),
27457
- "app.deploy.has_kv": Boolean(bindings?.kv?.length),
27458
- "app.deploy.has_r2": Boolean(bindings?.r2?.length),
27459
- "app.deploy.queue_count": bindings?.queues?.length ?? 0,
27460
- "app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
27461
- });
27462
- const activeDeployment = await db2.query.gameDeployments.findFirst({
27463
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
27464
- columns: { resources: true }
27465
- });
27466
- if (deploymentOptions?.bindings?.d1?.length) {
27467
- await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
28081
+ }
28082
+ static hasDashboardCredentialScope(permissionsJson, slug) {
28083
+ if (!permissionsJson) {
28084
+ return false;
27468
28085
  }
27469
- let result;
27470
28086
  try {
27471
- result = await withSpan("deploy.cloudflare", () => cf.deploy(deploymentId, request.code, env, {
27472
- ...deploymentOptions,
27473
- compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
27474
- compatibilityFlags: request.compatibilityFlags,
27475
- existingResources: activeDeployment?.resources ?? undefined,
27476
- assetsPath: frontendAssetsPath,
27477
- keepAssets
27478
- }));
27479
- } finally {
27480
- if (tempDir) {
27481
- const fs2 = await import("fs/promises");
27482
- fs2.rm(tempDir, { recursive: true, force: true }).catch(catchAttrs("deploy.temp_cleanup"));
28087
+ const parsed = JSON.parse(permissionsJson);
28088
+ return dashboardCredentialSlugs(parsed).includes(slug);
28089
+ } catch {
28090
+ return false;
28091
+ }
28092
+ }
28093
+ async ensureWorkerApiKeyOnWorker(user, spec, deploymentId, existingSecrets) {
28094
+ const cf = this.getCloudflare();
28095
+ const existingKey = await this.deps.db.query.apikey.findFirst({
28096
+ where: spec.lookup === "owner-and-name" ? and(eq(apikey.userId, user.id), eq(apikey.name, spec.keyName)) : eq(apikey.name, spec.keyName),
28097
+ columns: { id: true, permissions: true }
28098
+ });
28099
+ let apiKey;
28100
+ if (!existingKey) {
28101
+ apiKey = await this.createApiKey(user, spec);
28102
+ } else {
28103
+ const current = spec.isCurrent?.(existingKey.permissions) ?? true;
28104
+ const workerHasKey = existingSecrets ? existingSecrets.includes("PLAYCADEMY_API_KEY") : await this.workerHasSecret(deploymentId, "PLAYCADEMY_API_KEY");
28105
+ if (current && workerHasKey) {
28106
+ setAttribute("app.deploy.api_key_outcome", "already_set");
28107
+ return;
27483
28108
  }
28109
+ apiKey = await this.regenerateApiKey(user, existingKey.id, spec);
27484
28110
  }
27485
- yield { type: "status", data: { message: "Finalizing deployment" } };
27486
- const updatedGame = await this.finalizeDeployment({
27487
- game: game2,
27488
- slug,
27489
- deploymentId,
27490
- result,
27491
- request,
27492
- user,
27493
- flags: flags2
28111
+ await cf.setSecrets(deploymentId, { PLAYCADEMY_API_KEY: apiKey });
28112
+ }
28113
+ async createApiKey(user, spec) {
28114
+ const { key: apiKey } = await this.deps.createAuthApiKey({
28115
+ userId: user.id,
28116
+ name: spec.keyName,
28117
+ expiresIn: null,
28118
+ permissions: spec.permissions,
28119
+ ...spec.rateLimit ? {
28120
+ rateLimitEnabled: true,
28121
+ rateLimitMax: spec.rateLimit.maxRequests,
28122
+ rateLimitTimeWindow: spec.rateLimit.timeWindowMs
28123
+ } : { rateLimitEnabled: false }
27494
28124
  });
27495
- yield { type: "complete", data: updatedGame };
28125
+ setAttribute("app.deploy.api_key_outcome", "created");
28126
+ return apiKey;
27496
28127
  }
27497
- mapBindingsToOptions(deploymentId, bindings, schema2) {
27498
- if (!bindings && !schema2) {
28128
+ async regenerateApiKey(user, existingKeyId, spec) {
28129
+ setAttribute("app.deploy.api_key_outcome", "regenerated");
28130
+ try {
28131
+ await this.deps.deleteAuthApiKey(existingKeyId);
28132
+ } catch (error) {
28133
+ addEvent("deploy.api_key_revoke_failed", {
28134
+ "exception.type": errorType(error),
28135
+ "app.error.message": errorMessage(error),
28136
+ "app.deploy.old_key_id": existingKeyId
28137
+ });
28138
+ }
28139
+ return this.createApiKey(user, spec);
28140
+ }
28141
+ async listWorkerSecretNames(deploymentId) {
28142
+ try {
28143
+ return await this.getCloudflare().listSecrets(deploymentId);
28144
+ } catch (error) {
28145
+ addEvent("deploy.worker_secrets_check_failed", {
28146
+ "exception.type": errorType(error),
28147
+ "app.error.message": errorMessage(error)
28148
+ });
28149
+ return null;
28150
+ }
28151
+ }
28152
+ async workerHasSecret(deploymentId, secretName) {
28153
+ const workerSecrets = await this.listWorkerSecretNames(deploymentId);
28154
+ return Boolean(workerSecrets?.includes(secretName));
28155
+ }
28156
+ async ensureQueueIngressSecretOnWorker(slug, deploymentId) {
28157
+ const secret = this.deps.config.queueIngressSecret;
28158
+ if (!secret) {
27499
28159
  return;
27500
28160
  }
27501
- const workerBindings = {};
27502
- if (bindings?.database) {
27503
- workerBindings.d1 = [deploymentId];
28161
+ const cf = this.getCloudflare();
28162
+ await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
28163
+ }
28164
+ async ensureDashboardSessionSecret(deploymentId, existingSecrets, hasPriorDeployment) {
28165
+ if (existingSecrets === null && hasPriorDeployment) {
28166
+ setAttribute("app.deploy.session_secret_outcome", "check_failed_kept");
28167
+ return;
27504
28168
  }
27505
- if (bindings?.keyValue) {
27506
- workerBindings.kv = [deploymentId];
28169
+ if (existingSecrets?.includes("DASHBOARD_SESSION_SECRET")) {
28170
+ setAttribute("app.deploy.session_secret_outcome", "already_set");
28171
+ return;
27507
28172
  }
27508
- if (bindings?.bucket) {
27509
- workerBindings.r2 = [deploymentId];
28173
+ const secret = await generateSecureRandomString(64);
28174
+ await this.getCloudflare().setSecrets(deploymentId, { DASHBOARD_SESSION_SECRET: secret });
28175
+ setAttribute("app.deploy.session_secret_outcome", "created");
28176
+ }
28177
+ async rotateDashboardSessionSecret(slug, user) {
28178
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
28179
+ const deployment = await this.deps.db.query.gameDeployments.findFirst({
28180
+ where: activeDeploymentWhere(game2.id, "dashboard"),
28181
+ columns: { deploymentId: true }
28182
+ });
28183
+ if (!deployment) {
28184
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28185
+ throw new NotFoundError("Dashboard deployment", slug);
27510
28186
  }
27511
- if (bindings?.queues) {
27512
- let toQueueName = function(queueKey) {
27513
- return `${QUEUE_NAME_PREFIX}-${deploymentId}--${queueKey}`;
27514
- };
27515
- const queueNameByKey = new Map;
27516
- for (const queueKey of Object.keys(bindings.queues)) {
27517
- queueNameByKey.set(queueKey, toQueueName(queueKey));
27518
- }
27519
- workerBindings.queues = Object.entries(bindings.queues).map(([queueKey, queueConfig]) => {
27520
- const config2 = queueConfig === true ? undefined : queueConfig;
27521
- const deadLetterQueue = config2?.deadLetterQueue && queueNameByKey.get(config2.deadLetterQueue) ? queueNameByKey.get(config2.deadLetterQueue) : undefined;
27522
- return {
27523
- bindingName: toBindingName(queueKey),
27524
- queueName: toQueueName(queueKey),
27525
- settings: config2 ? {
27526
- maxBatchSize: config2.maxBatchSize,
27527
- maxRetries: config2.maxRetries,
27528
- maxBatchTimeout: config2.maxBatchTimeout,
27529
- maxConcurrency: config2.maxConcurrency,
27530
- retryDelay: config2.retryDelay
27531
- } : undefined,
27532
- deadLetterQueue
27533
- };
28187
+ const secret = await generateSecureRandomString(64);
28188
+ await this.getCloudflare().setSecrets(deployment.deploymentId, {
28189
+ DASHBOARD_SESSION_SECRET: secret
28190
+ });
28191
+ setAttribute("app.deploy.session_secret_outcome", "rotated");
28192
+ }
28193
+ async removeDashboard(slug, user) {
28194
+ const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
28195
+ const db2 = this.deps.db;
28196
+ const deployment = await db2.query.gameDeployments.findFirst({
28197
+ where: activeDeploymentWhere(game2.id, "dashboard"),
28198
+ columns: { deploymentId: true, provider: true, resources: true }
28199
+ });
28200
+ if (!deployment) {
28201
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28202
+ throw new NotFoundError("Dashboard deployment", slug);
28203
+ }
28204
+ const assetBuckets = deployment.resources?.r2?.filter((bucket) => bucket.kind === "assets") ?? [];
28205
+ await this.getCloudflare().delete(deployment.deploymentId, {
28206
+ deleteBindings: assetBuckets.length > 0,
28207
+ resources: assetBuckets.length > 0 ? { r2: assetBuckets } : undefined
28208
+ });
28209
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, slug);
28210
+ await db2.delete(gameDeployments).where(and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "dashboard")));
28211
+ setAttribute("app.deploy.dashboard_removed", true);
28212
+ await withSpan("deploy.send_alert", () => this.deps.alerts.notifyDashboardRemoval({
28213
+ slug,
28214
+ displayName: game2.displayName,
28215
+ developer: { id: user.id, email: user.email }
28216
+ })).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "dashboard_removal" }));
28217
+ }
28218
+ async fetchAndExtractFrontendAssets(slug, gameId, uploadToken, uploadDeps, extractZip) {
28219
+ const frontendZip = await withSpan("deploy.fetch_temporary_files", () => uploadDeps.getObjectAsByteArray(uploadToken));
28220
+ if (!frontendZip || frontendZip.length === 0) {
28221
+ throw new ValidationError("Uploaded file is empty or not found");
28222
+ }
28223
+ setAttribute("app.deploy.asset_upload_size", frontendZip.length);
28224
+ const os2 = await import("os");
28225
+ const path = await import("path");
28226
+ const tempDir = path.join(os2.tmpdir(), `playcademy-deploy-${gameId}-${Date.now()}`);
28227
+ const assetsPath = path.join(tempDir, "dist");
28228
+ await withSpan("deploy.extract_assets", () => extractZip(frontendZip, assetsPath));
28229
+ uploadDeps.deleteObject(uploadToken).catch(catchAttrs("deploy.temp_cleanup"));
28230
+ return { frontendZip, tempDir, assetsPath };
28231
+ }
28232
+ async resolvePlatformBaseUrl() {
28233
+ if (!this.deps.config.isLocal) {
28234
+ return this.deps.config.baseUrl;
28235
+ }
28236
+ try {
28237
+ return await getTunnelUrl(this.deps.config.baseUrl);
28238
+ } catch (error) {
28239
+ setAttributes({
28240
+ "app.deploy.tunnel_available": false,
28241
+ "app.deploy.failure_kind": "local_tunnel_unavailable"
27534
28242
  });
28243
+ throw new ValidationError(errorMessage(error));
27535
28244
  }
27536
- const hasBindings = workerBindings.d1?.length || workerBindings.kv?.length || workerBindings.r2?.length || workerBindings.queues?.length;
27537
- return {
27538
- ...hasBindings && { bindings: workerBindings },
27539
- ...schema2 && { schema: schema2 }
27540
- };
27541
28245
  }
27542
- async notifyDeploymentFailure(slug, displayName, error, developer) {
27543
- await this.deps.alerts.notifyDeploymentFailure({
27544
- slug,
27545
- displayName,
27546
- error,
27547
- developer
27548
- }).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
28246
+ async deployToCloudflare(args2) {
28247
+ const cf = this.getCloudflare();
28248
+ try {
28249
+ return await withSpan("deploy.cloudflare", () => cf.deploy(args2.deploymentId, args2.code, args2.env, args2.options));
28250
+ } finally {
28251
+ if (args2.tempDir) {
28252
+ const fs2 = await import("fs/promises");
28253
+ fs2.rm(args2.tempDir, { recursive: true, force: true }).catch(catchAttrs("deploy.temp_cleanup"));
28254
+ }
28255
+ }
27549
28256
  }
27550
- async saveDeployment(gameId, deploymentId, url, codeHash, resources) {
28257
+ async saveDeployment(record) {
27551
28258
  const db2 = this.deps.db;
27552
28259
  await db2.transaction(async (tx) => {
27553
- await tx.update(gameDeployments).set({ isActive: false }).where(eq(gameDeployments.gameId, gameId));
28260
+ await tx.update(gameDeployments).set({ isActive: false }).where(and(eq(gameDeployments.gameId, record.gameId), eq(gameDeployments.target, record.target)));
27554
28261
  await tx.insert(gameDeployments).values({
27555
- gameId,
27556
- deploymentId,
28262
+ gameId: record.gameId,
28263
+ deploymentId: record.deploymentId,
27557
28264
  provider: "cloudflare",
27558
- url,
27559
- codeHash,
27560
- resources,
28265
+ target: record.target,
28266
+ url: record.url,
28267
+ codeHash: record.codeHash,
28268
+ resources: record.resources,
27561
28269
  isActive: true
27562
28270
  });
27563
28271
  });
27564
28272
  }
28273
+ async notifyDeploymentFailure(failure) {
28274
+ await this.deps.alerts.notifyDeploymentFailure(failure).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
28275
+ }
27565
28276
  }
27566
28277
  var init_deploy_service = __esm(() => {
27567
28278
  init_drizzle_orm();
27568
28279
  init_playcademy();
27569
28280
  init_src();
28281
+ init_helpers_index();
27570
28282
  init_tables_index();
27571
28283
  init_spans();
27572
28284
  init_tunnel();
27573
28285
  init_errors();
28286
+ init_dashboard_util();
27574
28287
  init_deployment_util();
28288
+ init_worker_keys_util();
27575
28289
  });
27576
28290
 
27577
28291
  // ../api-core/src/services/developer.service.ts
@@ -27816,20 +28530,6 @@ var init_game_member_service = __esm(() => {
27816
28530
  init_spans();
27817
28531
  init_errors();
27818
28532
  });
27819
- // ../data/src/domains/timeback/helpers.ts
27820
- function isActiveGameTimebackIntegrationStatus(statusColumn = gameTimebackIntegrations.status) {
27821
- return sql`${statusColumn} IS DISTINCT FROM ${DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS}`;
27822
- }
27823
- var ACTIVE_GAME_TIMEBACK_INTEGRATION_STATUS = "active", DEACTIVATED_GAME_TIMEBACK_INTEGRATION_STATUS = "deactivated";
27824
- var init_helpers2 = __esm(() => {
27825
- init_drizzle_orm();
27826
- init_table7();
27827
- });
27828
-
27829
- // ../data/src/helpers.index.ts
27830
- var init_helpers_index = __esm(() => {
27831
- init_helpers2();
27832
- });
27833
28533
 
27834
28534
  // ../utils/src/fns.ts
27835
28535
  function sleep(ms) {
@@ -28273,6 +28973,7 @@ var init_game_service = __esm(() => {
28273
28973
  init_errors();
28274
28974
  init_deployment_util();
28275
28975
  init_timeback_util();
28976
+ init_worker_keys_util();
28276
28977
  inFlightManifestFetches = new Map;
28277
28978
  GameService = class GameService {
28278
28979
  deps;
@@ -28459,7 +29160,7 @@ var init_game_service = __esm(() => {
28459
29160
  GameService.recordGameShape(game2);
28460
29161
  return game2;
28461
29162
  }
28462
- async getBySlug(slug, caller) {
29163
+ async getBySlug(slug, caller, options) {
28463
29164
  const db2 = this.deps.db;
28464
29165
  const game2 = await db2.query.games.findFirst({
28465
29166
  where: eq(games.slug, slug)
@@ -28467,7 +29168,9 @@ var init_game_service = __esm(() => {
28467
29168
  if (!game2) {
28468
29169
  throw new NotFoundError("Game", slug);
28469
29170
  }
28470
- await this.enforceVisibility(game2, caller, slug);
29171
+ if (!options?.scopeAuthorized) {
29172
+ await this.enforceVisibility(game2, caller, slug);
29173
+ }
28471
29174
  GameService.recordGameShape(game2);
28472
29175
  return game2;
28473
29176
  }
@@ -28644,6 +29347,9 @@ var init_game_service = __esm(() => {
28644
29347
  await this.validateDeveloperAccess(user, gameId);
28645
29348
  } else {
28646
29349
  this.validateDeveloperStatus(user);
29350
+ if (slug.endsWith(DASHBOARD_WORKER_SUFFIX)) {
29351
+ throw new ValidationError(`Game slugs may not end with '${DASHBOARD_WORKER_SUFFIX}' — it is reserved for dashboard workers`);
29352
+ }
28647
29353
  }
28648
29354
  const gameDataForDb = {
28649
29355
  displayName: data.displayName,
@@ -28770,15 +29476,21 @@ var init_game_service = __esm(() => {
28770
29476
  if (!gameToDelete?.slug) {
28771
29477
  throw new NotFoundError("Game", gameId);
28772
29478
  }
28773
- const activeDeployment = await db2.query.gameDeployments.findFirst({
28774
- where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.isActive, true)),
28775
- columns: { deploymentId: true, provider: true, resources: true }
28776
- });
28777
- const customHostnames = await db2.select({
28778
- hostname: gameCustomHostnames.hostname,
28779
- cloudflareId: gameCustomHostnames.cloudflareId,
28780
- environment: gameCustomHostnames.environment
28781
- }).from(gameCustomHostnames).where(eq(gameCustomHostnames.gameId, gameId));
29479
+ const [activeDeployment, dashboardDeployment, customHostnames] = await Promise.all([
29480
+ db2.query.gameDeployments.findFirst({
29481
+ where: activeDeploymentWhere(gameId, "game"),
29482
+ columns: { deploymentId: true, provider: true, resources: true }
29483
+ }),
29484
+ db2.query.gameDeployments.findFirst({
29485
+ where: activeDeploymentWhere(gameId, "dashboard"),
29486
+ columns: { deploymentId: true, provider: true, resources: true }
29487
+ }),
29488
+ db2.select({
29489
+ hostname: gameCustomHostnames.hostname,
29490
+ cloudflareId: gameCustomHostnames.cloudflareId,
29491
+ environment: gameCustomHostnames.environment
29492
+ }).from(gameCustomHostnames).where(eq(gameCustomHostnames.gameId, gameId))
29493
+ ]);
28782
29494
  const result = await db2.delete(games).where(eq(games.id, gameId)).returning({ id: games.id });
28783
29495
  if (result.length === 0) {
28784
29496
  throw new NotFoundError("Game", gameId);
@@ -28822,6 +29534,22 @@ var init_game_service = __esm(() => {
28822
29534
  setAttribute("app.game.api_key_cleanup_error", errorMessage(error));
28823
29535
  }
28824
29536
  }
29537
+ if (dashboardDeployment?.provider === "cloudflare" && this.deps.cloudflare) {
29538
+ const dashboardAssetBuckets = dashboardDeployment.resources?.r2?.filter((bucket) => bucket.kind === "assets") ?? [];
29539
+ try {
29540
+ await this.deps.cloudflare.delete(dashboardDeployment.deploymentId, {
29541
+ deleteBindings: dashboardAssetBuckets.length > 0,
29542
+ resources: dashboardAssetBuckets.length > 0 ? { r2: dashboardAssetBuckets } : undefined
29543
+ });
29544
+ setAttribute("app.game.dashboard_cleanup", "succeeded");
29545
+ } catch (error) {
29546
+ setAttributes({
29547
+ "app.game.dashboard_cleanup": "failed",
29548
+ "app.game.dashboard_cleanup_error": errorMessage(error)
29549
+ });
29550
+ }
29551
+ }
29552
+ await sweepDashboardWorkerKeys(this.deps.deleteApiKeysByName, gameToDelete.slug);
28825
29553
  const corsOrigin = GameService.safeOrigin(gameToDelete);
28826
29554
  if (this.deps.corsKvs && corsOrigin) {
28827
29555
  setAttribute("app.cors_kvs.origin", corsOrigin);
@@ -28945,7 +29673,7 @@ class LogsService {
28945
29673
  }
28946
29674
  async generateToken(user, slug, sstStage) {
28947
29675
  await this.deps.validateDeveloperAccessBySlug(user, slug);
28948
- const workerId = getDeploymentId(slug, sstStage);
29676
+ const workerId = getGameDeploymentId(slug, sstStage);
28949
29677
  const token = await this.deps.mintLogStreamToken(user.id, workerId);
28950
29678
  setAttribute("app.logs.worker_id", workerId);
28951
29679
  return { token, workerId };
@@ -28965,7 +29693,8 @@ function createGameServices(deps) {
28965
29693
  cache,
28966
29694
  cloudflare: cloudflare2,
28967
29695
  corsKvs,
28968
- deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2)
29696
+ deleteApiKeyByName: auth2.deleteApiKeyByName.bind(auth2),
29697
+ deleteApiKeysByName: auth2.deleteApiKeysByName.bind(auth2)
28969
29698
  });
28970
29699
  const gameMember = new GameMemberService({
28971
29700
  db: db2,
@@ -28978,6 +29707,7 @@ function createGameServices(deps) {
28978
29707
  cloudflare: cloudflare2,
28979
29708
  createAuthApiKey: auth2.createApiKey.bind(auth2),
28980
29709
  deleteAuthApiKey: auth2.deleteApiKey.bind(auth2),
29710
+ deleteApiKeysByName: auth2.deleteApiKeysByName.bind(auth2),
28981
29711
  alerts,
28982
29712
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
28983
29713
  });
@@ -28987,14 +29717,20 @@ function createGameServices(deps) {
28987
29717
  storage,
28988
29718
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
28989
29719
  runDeploy: (slug, request, user, uploadDeps, extractZip) => deploy.deploy(slug, request, user, uploadDeps, extractZip),
28990
- notifyDeploymentFailure: (slug, displayName, error, developerInfo) => deploy.notifyDeploymentFailure(slug, displayName, error, developerInfo)
29720
+ notifyDeploymentFailure: (failure) => deploy.notifyDeploymentFailure(failure)
28991
29721
  });
28992
29722
  const logs = new LogsService({
28993
29723
  mintLogStreamToken: auth2.mintLogStreamToken.bind(auth2),
28994
29724
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
28995
29725
  });
29726
+ const dashboard2 = new DashboardService({
29727
+ db: db2,
29728
+ hashPassword: auth2.hashPassword.bind(auth2),
29729
+ verifyPassword: auth2.verifyPassword.bind(auth2),
29730
+ validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug)
29731
+ });
28996
29732
  return {
28997
- services: { game: game2, gameMember, developer, deploy, deployJobs, logs },
29733
+ services: { game: game2, gameMember, developer, deploy, deployJobs, logs, dashboard: dashboard2 },
28998
29734
  validators: {
28999
29735
  validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
29000
29736
  validateDeveloperAccess: (user, gameId) => game2.validateDeveloperAccess(user, gameId),
@@ -29004,6 +29740,7 @@ function createGameServices(deps) {
29004
29740
  };
29005
29741
  }
29006
29742
  var init_game2 = __esm(() => {
29743
+ init_dashboard_service();
29007
29744
  init_deploy_job_service();
29008
29745
  init_deploy_service();
29009
29746
  init_developer_service();
@@ -29188,12 +29925,25 @@ class AlertsService {
29188
29925
  embed.setFooter("Playcademy Developer Platform").setTimestamp();
29189
29926
  await this.sendAlert(discord, embed.build());
29190
29927
  }
29928
+ async notifyDashboardDeployment(dashboard2) {
29929
+ const discord = this.recordAlert("dashboard_deployment");
29930
+ if (!discord) {
29931
+ return;
29932
+ }
29933
+ 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);
29934
+ if (dashboard2.developer) {
29935
+ embed.addField("Developer", dashboard2.developer.email || dashboard2.developer.id, true);
29936
+ }
29937
+ embed.setFooter("Playcademy Developer Platform").setTimestamp();
29938
+ await this.sendAlert(discord, embed.build());
29939
+ }
29191
29940
  async notifyDeploymentFailure(failure) {
29192
29941
  const discord = this.recordAlert("deployment_failure");
29193
29942
  if (!discord) {
29194
29943
  return;
29195
29944
  }
29196
- 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);
29945
+ const [title, subject] = failure.target === "dashboard" ? ["Dashboard Deployment Failed", "Dashboard deployment"] : ["❌ Deployment Failed", "Deployment"];
29946
+ const embed = new DiscordEmbedBuilder().setTitle(title).setDescription(`${subject} failed for **${failure.displayName || failure.slug}** (**${this.getEnvironment()}**).`).setColor(DiscordColors.RED).addField("Slug", failure.slug, true);
29197
29947
  if (failure.developer) {
29198
29948
  embed.addField("Developer", failure.developer.email || failure.developer.id, true);
29199
29949
  }
@@ -29208,6 +29958,14 @@ class AlertsService {
29208
29958
  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();
29209
29959
  await this.sendAlert(discord, embed);
29210
29960
  }
29961
+ async notifyDashboardRemoval(dashboard2) {
29962
+ const discord = this.recordAlert("dashboard_removal");
29963
+ if (!discord) {
29964
+ return;
29965
+ }
29966
+ 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();
29967
+ await this.sendAlert(discord, embed);
29968
+ }
29211
29969
  async notifySeedFailure(failure) {
29212
29970
  const discord = this.recordAlert("seed_failure");
29213
29971
  if (!discord) {
@@ -29373,6 +30131,7 @@ class KVBackupService {
29373
30131
  async getTargets(gameSlug) {
29374
30132
  const conditions2 = [
29375
30133
  eq(gameDeployments.isActive, true),
30134
+ eq(gameDeployments.target, "game"),
29376
30135
  eq(gameDeployments.provider, "cloudflare")
29377
30136
  ];
29378
30137
  if (gameSlug) {
@@ -29599,7 +30358,7 @@ class BucketService {
29599
30358
  return extensionIndex > 0 ? fileName.slice(extensionIndex + 1).toLowerCase() : "(none)";
29600
30359
  }
29601
30360
  getBucketName(slug) {
29602
- return getDeploymentId(slug, this.deps.sstStage);
30361
+ return getGameDeploymentId(slug, this.deps.sstStage);
29603
30362
  }
29604
30363
  async listFiles(slug, user, prefix) {
29605
30364
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
@@ -29688,6 +30447,32 @@ class DatabaseService {
29688
30447
  }
29689
30448
  return d1;
29690
30449
  }
30450
+ async syncDashboardD1Binding(gameId, d1ResourceName, databaseId) {
30451
+ const dashboardDeployment = await this.deps.db.query.gameDeployments.findFirst({
30452
+ where: activeDeploymentWhere(gameId, "dashboard"),
30453
+ columns: { id: true, deploymentId: true, resources: true }
30454
+ });
30455
+ if (!dashboardDeployment || !this.deps.cloudflare) {
30456
+ return;
30457
+ }
30458
+ try {
30459
+ await this.deps.cloudflare.updateD1Binding(dashboardDeployment.deploymentId, databaseId);
30460
+ if (dashboardDeployment.resources?.d1?.length) {
30461
+ const updatedResources = {
30462
+ ...dashboardDeployment.resources,
30463
+ d1: dashboardDeployment.resources.d1.map((dbResource) => dbResource.name === d1ResourceName ? { ...dbResource, id: databaseId } : dbResource)
30464
+ };
30465
+ await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, dashboardDeployment.id));
30466
+ }
30467
+ setAttribute("app.database.dashboard_binding", "updated");
30468
+ } catch (error) {
30469
+ addEvent("database.dashboard_binding_sync_failed", {
30470
+ "exception.type": errorType(error),
30471
+ "app.error.message": errorMessage(error),
30472
+ "app.deploy.deployment_id": dashboardDeployment.deploymentId
30473
+ });
30474
+ }
30475
+ }
29691
30476
  async reset(slug, user, schema2) {
29692
30477
  setAttributes({
29693
30478
  "app.database.operation": "reset",
@@ -29696,7 +30481,7 @@ class DatabaseService {
29696
30481
  });
29697
30482
  const d1 = this.getD1();
29698
30483
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
29699
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
30484
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
29700
30485
  try {
29701
30486
  const databaseId = await d1.reset(deploymentId);
29702
30487
  setAttribute("app.database.id", databaseId);
@@ -29712,7 +30497,7 @@ class DatabaseService {
29712
30497
  setAttribute("app.database.binding", "skipped_no_provider");
29713
30498
  }
29714
30499
  const activeDeployment = await this.deps.db.query.gameDeployments.findFirst({
29715
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
30500
+ where: activeDeploymentWhere(game2.id),
29716
30501
  columns: { id: true, resources: true }
29717
30502
  });
29718
30503
  setAttribute("app.database.active_deployment_found", Boolean(activeDeployment));
@@ -29723,6 +30508,7 @@ class DatabaseService {
29723
30508
  };
29724
30509
  await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, activeDeployment.id));
29725
30510
  }
30511
+ await this.syncDashboardD1Binding(game2.id, deploymentId, databaseId);
29726
30512
  setAttributes({
29727
30513
  "app.database.deployment_id": deploymentId,
29728
30514
  "app.database.schema_pushed": schemaPushed
@@ -29746,6 +30532,7 @@ class DatabaseService {
29746
30532
  }
29747
30533
  var init_database_service = __esm(() => {
29748
30534
  init_drizzle_orm();
30535
+ init_helpers_index();
29749
30536
  init_tables_index();
29750
30537
  init_spans();
29751
30538
  init_errors();
@@ -29989,7 +30776,7 @@ class KVService {
29989
30776
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
29990
30777
  const db2 = this.deps.db;
29991
30778
  const deployment = await db2.query.gameDeployments.findFirst({
29992
- where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.isActive, true)),
30779
+ where: activeDeploymentWhere(game2.id),
29993
30780
  columns: { resources: true }
29994
30781
  });
29995
30782
  if (!deployment?.resources?.kv?.length) {
@@ -30110,8 +30897,7 @@ class KVService {
30110
30897
  }
30111
30898
  }
30112
30899
  var init_kv_service = __esm(() => {
30113
- init_drizzle_orm();
30114
- init_tables_index();
30900
+ init_helpers_index();
30115
30901
  init_spans();
30116
30902
  init_errors();
30117
30903
  });
@@ -30128,13 +30914,13 @@ class SecretsService {
30128
30914
  }
30129
30915
  return this.deps.cloudflare;
30130
30916
  }
30131
- getDeploymentId(slug) {
30132
- return getDeploymentId(slug, this.deps.config.sstStage);
30917
+ getGameDeploymentId(slug) {
30918
+ return getGameDeploymentId(slug, this.deps.config.sstStage);
30133
30919
  }
30134
30920
  async listKeys(slug, user) {
30135
30921
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30136
30922
  const cf = this.getCloudflare();
30137
- const deploymentId = this.getDeploymentId(slug);
30923
+ const deploymentId = this.getGameDeploymentId(slug);
30138
30924
  try {
30139
30925
  const allKeys = await cf.listSecrets(deploymentId);
30140
30926
  const userKeys = allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
@@ -30160,7 +30946,7 @@ class SecretsService {
30160
30946
  async setSecrets(slug, newSecrets, user) {
30161
30947
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30162
30948
  const cf = this.getCloudflare();
30163
- const deploymentId = this.getDeploymentId(slug);
30949
+ const deploymentId = this.getGameDeploymentId(slug);
30164
30950
  const secretKeys = Object.keys(newSecrets);
30165
30951
  if (secretKeys.length === 0) {
30166
30952
  throw new ValidationError("At least one secret must be provided");
@@ -30213,7 +30999,7 @@ class SecretsService {
30213
30999
  }
30214
31000
  await this.deps.validateDeveloperAccessBySlug(user, slug);
30215
31001
  const cf = this.getCloudflare();
30216
- const deploymentId = this.getDeploymentId(slug);
31002
+ const deploymentId = this.getGameDeploymentId(slug);
30217
31003
  try {
30218
31004
  const prefixedKey = `${SECRETS_PREFIX}${key}`;
30219
31005
  const existingKeys = await cf.listSecrets(deploymentId);
@@ -30250,27 +31036,7 @@ var init_secrets_service = __esm(() => {
30250
31036
  init_deployment_util();
30251
31037
  INTERNAL_SECRET_KEYS = ["PLAYCADEMY_API_KEY", "GAME_ID", "PLAYCADEMY_BASE_URL"];
30252
31038
  });
30253
-
30254
- // ../edge-play/src/constants.ts
30255
- var ASSET_ROUTE_PREFIX = "/api/assets/", ROUTES;
30256
- var init_constants3 = __esm(() => {
30257
- init_src();
30258
- ROUTES = {
30259
- INDEX: "/api",
30260
- HEALTH: "/api/health",
30261
- ASSETS: `${ASSET_ROUTE_PREFIX}*`,
30262
- TIMEBACK: {
30263
- END_ACTIVITY: `/api${TIMEBACK_ROUTES.END_ACTIVITY}`,
30264
- GET_XP: `/api${TIMEBACK_ROUTES.GET_XP}`,
30265
- GET_MASTERY: `/api${TIMEBACK_ROUTES.GET_MASTERY}`,
30266
- GET_HIGHEST_GRADE_MASTERED: `/api${TIMEBACK_ROUTES.GET_HIGHEST_GRADE_MASTERED}`,
30267
- HEARTBEAT: `/api${TIMEBACK_ROUTES.HEARTBEAT}`,
30268
- ADVANCE_COURSE: `/api${TIMEBACK_ROUTES.ADVANCE_COURSE}`,
30269
- UNENROLL_COURSE: `/api${TIMEBACK_ROUTES.UNENROLL_COURSE}`
30270
- }
30271
- };
30272
- });
30273
- // ../edge-play/src/entry/setup.ts
31039
+ // ../edge-play/src/game/setup.ts
30274
31040
  function prefixSecrets(secrets) {
30275
31041
  const prefixed = {};
30276
31042
  for (const [key, value] of Object.entries(secrets)) {
@@ -30280,7 +31046,6 @@ function prefixSecrets(secrets) {
30280
31046
  }
30281
31047
  var init_setup2 = __esm(() => {
30282
31048
  init_src();
30283
- init_constants3();
30284
31049
  });
30285
31050
 
30286
31051
  // ../api-core/src/services/seed.service.ts
@@ -30315,7 +31080,7 @@ class SeedService {
30315
31080
  async seed(slug, code, user, secrets) {
30316
31081
  const cf = this.getCloudflare();
30317
31082
  const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
30318
- const deploymentId = getDeploymentId(slug, this.deps.config.sstStage);
31083
+ const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
30319
31084
  const uniqueSuffix = Date.now().toString(36);
30320
31085
  const seedDeploymentId = `seed-${deploymentId}-${uniqueSuffix}`;
30321
31086
  setAttributes({
@@ -30806,7 +31571,7 @@ var init_emoji = __esm(() => {
30806
31571
  });
30807
31572
 
30808
31573
  // ../data/src/domains/game/schemas.ts
30809
- var HttpUrlSchema, GameEmojiSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SeedRequestSchema, SchemaInfoSchema, DatabaseResetRequestSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployRequestSchema;
31574
+ 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;
30810
31575
  var init_schemas2 = __esm(() => {
30811
31576
  init_drizzle_zod();
30812
31577
  init_esm();
@@ -30917,6 +31682,20 @@ var init_schemas2 = __esm(() => {
30917
31682
  UpdateGameMemberRoleSchema = exports_external.object({
30918
31683
  role: exports_external.enum(gameMemberRoleEnum.enumValues)
30919
31684
  }).strict();
31685
+ AddGameDashboardUserSchema = exports_external.object({
31686
+ email: exports_external.string().email().max(255),
31687
+ displayName: exports_external.string().min(1).max(255).optional(),
31688
+ role: exports_external.enum(gameDashboardUserRoleEnum.enumValues).optional().default(DASHBOARD_USER_ROLES.VIEWER)
31689
+ }).strict();
31690
+ VerifyGameDashboardLoginSchema = exports_external.object({
31691
+ email: exports_external.string().email().max(255),
31692
+ password: exports_external.string().min(1).max(1024)
31693
+ }).strict();
31694
+ AcceptGameDashboardInviteSchema = exports_external.object({
31695
+ token: exports_external.string().min(1).max(255),
31696
+ password: exports_external.string().min(8, "Password must be at least 8 characters").max(1024, "Password must be at most 1024 characters")
31697
+ }).strict();
31698
+ AcceptGameDashboardResetSchema = AcceptGameDashboardInviteSchema;
30920
31699
  ALLOWED_UPLOAD_EXTENSIONS = [".zip", ".js"];
30921
31700
  InitiateUploadSchema = exports_external.object({
30922
31701
  fileName: exports_external.string().min(1).refine((name2) => ALLOWED_UPLOAD_EXTENSIONS.some((ext) => name2.endsWith(ext)), {
@@ -30951,6 +31730,7 @@ var init_schemas2 = __esm(() => {
30951
31730
  }))
30952
31731
  });
30953
31732
  DeployRequestSchema = exports_external.object({
31733
+ target: exports_external.enum(deploymentTargetEnum.enumValues).optional().default("game"),
30954
31734
  uploadToken: exports_external.string().optional(),
30955
31735
  code: exports_external.string().optional(),
30956
31736
  codeUploadToken: exports_external.string().optional(),
@@ -30986,6 +31766,9 @@ var init_schemas2 = __esm(() => {
30986
31766
  }).refine((data) => !(data.code && data.codeUploadToken), {
30987
31767
  message: "Specify either code or codeUploadToken, not both",
30988
31768
  path: ["codeUploadToken"]
31769
+ }).refine((data) => !(data.target === "dashboard" && (data.schema || data.bindings)), {
31770
+ message: "Dashboard deployments cannot include schema or bindings — they attach to the game deployment’s existing resources",
31771
+ path: ["target"]
30989
31772
  });
30990
31773
  });
30991
31774
 
@@ -74417,6 +75200,17 @@ function isValidUUID(value) {
74417
75200
  }
74418
75201
  return UUID_REGEX.test(value);
74419
75202
  }
75203
+ async function deterministicUUID(input) {
75204
+ const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(input));
75205
+ const bytes = new Uint8Array(digest).slice(0, 16);
75206
+ bytes[6] = bytes[6] & 15 | 80;
75207
+ bytes[8] = bytes[8] & 63 | 128;
75208
+ const hex3 = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
75209
+ return `${hex3.slice(0, 8)}-${hex3.slice(8, 12)}-${hex3.slice(12, 16)}-${hex3.slice(16, 20)}-${hex3.slice(20)}`;
75210
+ }
75211
+ async function localGameId(slug) {
75212
+ return deterministicUUID(`playcademy:local-game:${slug}`);
75213
+ }
74420
75214
  var UUID_REGEX;
74421
75215
  var init_uuid2 = __esm(() => {
74422
75216
  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}$/;
@@ -76818,7 +77612,7 @@ function deriveTimebackCourseLevelFromGrade(grade) {
76818
77612
  return TIMEBACK_COURSE_DEFAULTS.level.high;
76819
77613
  }
76820
77614
  var ONEROSTER_STATUS2, SCORE_STATUS2, CACHE_DEFAULTS3, RESOURCE_DEFAULTS3;
76821
- var init_constants4 = __esm(() => {
77615
+ var init_constants3 = __esm(() => {
76822
77616
  init_src();
76823
77617
  ONEROSTER_STATUS2 = {
76824
77618
  active: "active",
@@ -77792,7 +78586,7 @@ function qtiIdCandidates(parentLineItem, resource) {
77792
78586
  }
77793
78587
  var GRADE_LEVEL_TEST_TYPES, naturalTitleSort;
77794
78588
  var init_timeback_grade_level_results_util = __esm(() => {
77795
- init_constants4();
78589
+ init_constants3();
77796
78590
  GRADE_LEVEL_TEST_TYPES = [
77797
78591
  "placement",
77798
78592
  "test out",
@@ -77987,7 +78781,7 @@ async function upsertMasteryCompletionEntry(params) {
77987
78781
  }
77988
78782
  var init_timeback_mastery_completion_util = __esm(async () => {
77989
78783
  init_spans();
77990
- init_constants4();
78784
+ init_constants3();
77991
78785
  init_utils6();
77992
78786
  await init_errors8();
77993
78787
  });
@@ -78683,7 +79477,7 @@ class TimebackAdminService {
78683
79477
  columns: { slug: true }
78684
79478
  }),
78685
79479
  this.deps.db.query.gameDeployments.findFirst({
78686
- where: and(eq(gameDeployments.gameId, gameId), eq(gameDeployments.isActive, true)),
79480
+ where: activeDeploymentWhere(gameId),
78687
79481
  columns: { url: true }
78688
79482
  })
78689
79483
  ]);
@@ -79764,7 +80558,7 @@ var init_timeback_admin_service = __esm(async () => {
79764
80558
  init_schemas_index();
79765
80559
  init_tables_index();
79766
80560
  init_spans();
79767
- init_constants4();
80561
+ init_constants3();
79768
80562
  init_types2();
79769
80563
  init_utils6();
79770
80564
  init_src4();
@@ -80239,7 +81033,7 @@ function timebackConfigMatchesCreateIntegrationRequest(config4, request) {
80239
81033
  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;
80240
81034
  }
80241
81035
  var init_timeback_create_integration_util = __esm(() => {
80242
- init_constants4();
81036
+ init_constants3();
80243
81037
  init_types2();
80244
81038
  init_timeback_util();
80245
81039
  });
@@ -83085,6 +83879,19 @@ function createSandboxAuthProvider() {
83085
83879
  }
83086
83880
  return null;
83087
83881
  },
83882
+ async deleteApiKeysByName(name3) {
83883
+ const deleted = [];
83884
+ for (const [id, key] of sandboxApiKeys.entries()) {
83885
+ if (key.name === name3) {
83886
+ sandboxApiKeys.delete(id);
83887
+ deleted.push(id);
83888
+ }
83889
+ }
83890
+ if (deleted.length > 0) {
83891
+ log.debug("[SandboxAuthProvider] Deleted API keys by name", { name: name3, deleted });
83892
+ }
83893
+ return deleted;
83894
+ },
83088
83895
  async mintGameToken(gameId, userId) {
83089
83896
  const header = btoa(JSON.stringify({ alg: "none", typ: "sandbox" }));
83090
83897
  const exp = Date.now() + 15 * 60 * 1000;
@@ -83163,6 +83970,12 @@ function createSandboxAuthProvider() {
83163
83970
  } catch {
83164
83971
  return null;
83165
83972
  }
83973
+ },
83974
+ async hashPassword(password) {
83975
+ return `sandbox:${btoa(password)}`;
83976
+ },
83977
+ async verifyPassword(data) {
83978
+ return data.hash === `sandbox:${btoa(data.password)}`;
83166
83979
  }
83167
83980
  };
83168
83981
  }
@@ -83447,7 +84260,7 @@ var init_http_exception = () => {};
83447
84260
 
83448
84261
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/request/constants.js
83449
84262
  var GET_MATCH_RESULT;
83450
- var init_constants5 = __esm(() => {
84263
+ var init_constants4 = __esm(() => {
83451
84264
  GET_MATCH_RESULT = Symbol();
83452
84265
  });
83453
84266
 
@@ -83711,7 +84524,7 @@ var init_url = __esm(() => {
83711
84524
  var tryDecodeURIComponent = (str) => tryDecode(str, decodeURIComponent_), HonoRequest;
83712
84525
  var init_request = __esm(() => {
83713
84526
  init_http_exception();
83714
- init_constants5();
84527
+ init_constants4();
83715
84528
  init_body();
83716
84529
  init_url();
83717
84530
  HonoRequest = class {
@@ -84041,7 +84854,7 @@ var init_router = __esm(() => {
84041
84854
 
84042
84855
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/utils/constants.js
84043
84856
  var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
84044
- var init_constants6 = () => {};
84857
+ var init_constants5 = () => {};
84045
84858
 
84046
84859
  // ../../node_modules/.bun/hono@4.10.7/node_modules/hono/dist/hono-base.js
84047
84860
  var notFoundHandler = (c) => {
@@ -84263,7 +85076,7 @@ var init_hono_base = __esm(() => {
84263
85076
  init_compose();
84264
85077
  init_context2();
84265
85078
  init_router();
84266
- init_constants6();
85079
+ init_constants5();
84267
85080
  init_url();
84268
85081
  });
84269
85082
 
@@ -92933,7 +93746,7 @@ var require_node3 = __commonJS((exports) => {
92933
93746
  exports2.isAbsolute = function(aPath) {
92934
93747
  return aPath.charAt(0) === "/" || urlRegexp.test(aPath);
92935
93748
  };
92936
- function relative(aRoot, aPath) {
93749
+ function relative2(aRoot, aPath) {
92937
93750
  if (aRoot === "") {
92938
93751
  aRoot = ".";
92939
93752
  }
@@ -92952,7 +93765,7 @@ var require_node3 = __commonJS((exports) => {
92952
93765
  }
92953
93766
  return Array(level + 1).join("../") + aPath.substr(aRoot.length + 1);
92954
93767
  }
92955
- exports2.relative = relative;
93768
+ exports2.relative = relative2;
92956
93769
  var supportsNullProto = function() {
92957
93770
  var obj = Object.create(null);
92958
93771
  return !("__proto__" in obj);
@@ -94983,7 +95796,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
94983
95796
  });
94984
95797
  };
94985
95798
  }
94986
- var readFile = (fp) => new Promise((resolve2, reject) => {
95799
+ var readFile2 = (fp) => new Promise((resolve2, reject) => {
94987
95800
  _fs.default.readFile(fp, "utf8", (err2, data) => {
94988
95801
  if (err2)
94989
95802
  return reject(err2);
@@ -95181,7 +95994,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
95181
95994
  data: _this3.packageJsonCache.get(filepath)[options.packageKey]
95182
95995
  };
95183
95996
  }
95184
- const data = _this3.options.parseJSON(yield readFile(filepath));
95997
+ const data = _this3.options.parseJSON(yield readFile2(filepath));
95185
95998
  return {
95186
95999
  path: filepath,
95187
96000
  data
@@ -95189,7 +96002,7 @@ If you have no idea what this means or what Pirates is, let me explain: Pirates
95189
96002
  }
95190
96003
  return {
95191
96004
  path: filepath,
95192
- data: yield readFile(filepath)
96005
+ data: yield readFile2(filepath)
95193
96006
  };
95194
96007
  }
95195
96008
  return {};
@@ -100234,7 +101047,7 @@ var __create2, __defProp2, __getOwnPropDesc, __getOwnPropNames2, __getProtoOf2,
100234
101047
  __defProp2(to, key, { get: () => from[key], enumerable: !(desc2 = __getOwnPropDesc(from, key)) || desc2.enumerable });
100235
101048
  }
100236
101049
  return to;
100237
- }, __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) => {
101050
+ }, __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) => {
100238
101051
  const matchers = filters.map((it3) => {
100239
101052
  return new Minimatch(it3);
100240
101053
  });
@@ -119280,8 +120093,8 @@ ${BREAKPOINT}ALTER TABLE ${tableNameWithSchema} ADD CONSTRAINT "${statement.newC
119280
120093
  win32: { sep: "\\" },
119281
120094
  posix: { sep: "/" }
119282
120095
  };
119283
- sep = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
119284
- minimatch.sep = sep;
120096
+ sep2 = defaultPlatform === "win32" ? path.win32.sep : path.posix.sep;
120097
+ minimatch.sep = sep2;
119285
120098
  GLOBSTAR = Symbol("globstar **");
119286
120099
  minimatch.GLOBSTAR = GLOBSTAR;
119287
120100
  plTypes = {
@@ -138183,7 +138996,7 @@ async function seedCoreGames(db2) {
138183
138996
  }
138184
138997
  async function seedCurrentProjectGame(db2, project) {
138185
138998
  const now2 = new Date;
138186
- const desiredGameId = project.gameId?.trim() || undefined;
138999
+ const desiredGameId = project.gameId?.trim() || await localGameId(project.slug);
138187
139000
  try {
138188
139001
  const existingGame = await db2.query.games.findFirst({
138189
139002
  where: (row, operators) => operators.eq(row.slug, project.slug)
@@ -138199,7 +139012,7 @@ async function seedCurrentProjectGame(db2, project) {
138199
139012
  }
138200
139013
  }
138201
139014
  const gameRecord = {
138202
- id: desiredGameId ?? crypto.randomUUID(),
139015
+ id: desiredGameId,
138203
139016
  slug: project.slug,
138204
139017
  displayName: project.displayName,
138205
139018
  version: project.version,
@@ -138234,6 +139047,7 @@ var init_games = __esm(() => {
138234
139047
  init_drizzle_orm();
138235
139048
  init_src();
138236
139049
  init_tables_index();
139050
+ init_uuid2();
138237
139051
  init_constants();
138238
139052
  init_logging();
138239
139053
  init_timeback5();
@@ -138819,19 +139633,50 @@ var init_types6 = () => {};
138819
139633
  function hasGameManagementAccess(user) {
138820
139634
  return user.role === "admin" || user.role === "teacher" || user.role === "developer" && user.developerStatus === "approved";
138821
139635
  }
139636
+ function isDashboardWorkerKey(key) {
139637
+ return Boolean(key?.permissions?.[DASHBOARD_PERMISSION_NAMESPACE]);
139638
+ }
139639
+ function workerKeyGrantsGameRead(key, slug2) {
139640
+ if (!isDashboardWorkerKey(key) && !isGameWorkerKey(key)) {
139641
+ return false;
139642
+ }
139643
+ const games2 = key?.permissions?.games;
139644
+ return Boolean(games2?.includes(`read:${slug2}`) || games2?.includes(`write:${slug2}`));
139645
+ }
139646
+ function isGameWorkerKey(key) {
139647
+ return Boolean(key?.name?.startsWith(GAME_WORKER_KEY_PREFIX));
139648
+ }
139649
+ function deniedWorkerKeyRequest(key, method, pathname) {
139650
+ if (isDashboardWorkerKey(key) && !isAllowedDashboardWorkerRequest(method, pathname, key.permissions)) {
139651
+ return DASHBOARD_WORKER_KEY_RESTRICTED;
139652
+ }
139653
+ return null;
139654
+ }
139655
+ function rejectDashboardWorkerKey(ctx) {
139656
+ if (isDashboardWorkerKey(ctx.apiKey)) {
139657
+ throw ApiError.forbidden(DASHBOARD_WORKER_KEY_RESTRICTED);
139658
+ }
139659
+ }
139660
+ function assertAuthenticatedRequest(ctx) {
139661
+ if (!isAuthenticated(ctx)) {
139662
+ throw ApiError.unauthorized("Valid session or bearer token required");
139663
+ }
139664
+ if (ctx.apiKey) {
139665
+ const denied = deniedWorkerKeyRequest(ctx.apiKey, ctx.request.method, ctx.url.pathname);
139666
+ if (denied) {
139667
+ throw ApiError.forbidden(denied);
139668
+ }
139669
+ }
139670
+ }
138822
139671
  function requireAuth(handler) {
138823
139672
  return async (ctx) => {
138824
- if (!isAuthenticated(ctx)) {
138825
- throw ApiError.unauthorized("Valid session or bearer token required");
138826
- }
139673
+ assertAuthenticatedRequest(ctx);
138827
139674
  return handler(ctx);
138828
139675
  };
138829
139676
  }
138830
139677
  function requireNonAnonymous(handler) {
138831
139678
  return async (ctx) => {
138832
- if (!isAuthenticated(ctx)) {
138833
- throw ApiError.unauthorized("Valid session or bearer token required");
138834
- }
139679
+ assertAuthenticatedRequest(ctx);
138835
139680
  if (ctx.user.isAnonymous) {
138836
139681
  throw ApiError.forbidden("This operation is not available for demo/anonymous users");
138837
139682
  }
@@ -138840,9 +139685,7 @@ function requireNonAnonymous(handler) {
138840
139685
  }
138841
139686
  function requireAnonymous(handler) {
138842
139687
  return async (ctx) => {
138843
- if (!isAuthenticated(ctx)) {
138844
- throw ApiError.unauthorized("Valid session or bearer token required");
138845
- }
139688
+ assertAuthenticatedRequest(ctx);
138846
139689
  if (!ctx.user.isAnonymous) {
138847
139690
  throw ApiError.forbidden("This operation is only available for demo/anonymous users");
138848
139691
  }
@@ -138851,9 +139694,8 @@ function requireAnonymous(handler) {
138851
139694
  }
138852
139695
  function requireAdmin(handler) {
138853
139696
  return async (ctx) => {
138854
- if (!isAuthenticated(ctx)) {
138855
- throw ApiError.unauthorized("Valid session or bearer token required");
138856
- }
139697
+ assertAuthenticatedRequest(ctx);
139698
+ rejectDashboardWorkerKey(ctx);
138857
139699
  if (ctx.user.role !== "admin") {
138858
139700
  throw ApiError.forbidden("Admin access required");
138859
139701
  }
@@ -138862,9 +139704,8 @@ function requireAdmin(handler) {
138862
139704
  }
138863
139705
  function requireDeveloper(handler) {
138864
139706
  return async (ctx) => {
138865
- if (!isAuthenticated(ctx)) {
138866
- throw ApiError.unauthorized("Valid session or bearer token required");
138867
- }
139707
+ assertAuthenticatedRequest(ctx);
139708
+ rejectDashboardWorkerKey(ctx);
138868
139709
  const isAdmin = ctx.user.role === "admin";
138869
139710
  const isApprovedDev = ctx.user.role === "developer" && ctx.user.developerStatus === "approved";
138870
139711
  if (!isAdmin && !isApprovedDev) {
@@ -138873,20 +139714,30 @@ function requireDeveloper(handler) {
138873
139714
  return handler(ctx);
138874
139715
  };
138875
139716
  }
139717
+ function requireHumanDeveloper(handler) {
139718
+ return requireDeveloper(async (ctx) => {
139719
+ if (isDashboardWorkerKey(ctx.apiKey) || isGameWorkerKey(ctx.apiKey)) {
139720
+ throw ApiError.forbidden(WORKER_KEY_HUMAN_ONLY);
139721
+ }
139722
+ return handler(ctx);
139723
+ });
139724
+ }
138876
139725
  function requireGameManagementAccess(handler) {
138877
139726
  return async (ctx) => {
138878
- if (!isAuthenticated(ctx)) {
138879
- throw ApiError.unauthorized("Valid session or bearer token required");
138880
- }
139727
+ assertAuthenticatedRequest(ctx);
139728
+ rejectDashboardWorkerKey(ctx);
138881
139729
  if (!hasGameManagementAccess(ctx.user)) {
138882
139730
  throw ApiError.forbidden("Game management access required");
138883
139731
  }
138884
139732
  return handler(ctx);
138885
139733
  };
138886
139734
  }
139735
+ var WORKER_KEY_HUMAN_ONLY = "This operation requires your own credential — platform worker keys are refused";
138887
139736
  var init_auth_util = __esm(() => {
138888
139737
  init_errors();
138889
139738
  init_types6();
139739
+ init_dashboard_util();
139740
+ init_deployment_util();
138890
139741
  });
138891
139742
 
138892
139743
  // ../api-core/src/utils/controller.util.ts
@@ -139030,6 +139881,21 @@ function requireGameId(gameId) {
139030
139881
  }
139031
139882
  return gameId;
139032
139883
  }
139884
+ function requireSlug(slug2) {
139885
+ if (!slug2) {
139886
+ throw ApiError.badRequest("Missing game slug");
139887
+ }
139888
+ return slug2;
139889
+ }
139890
+ function requireUserId(userId) {
139891
+ if (!userId) {
139892
+ throw ApiError.badRequest("Missing user ID");
139893
+ }
139894
+ if (!isValidUUID(userId)) {
139895
+ throw ApiError.unprocessableEntity("userId must be a valid UUID format");
139896
+ }
139897
+ return userId;
139898
+ }
139033
139899
  var init_params_util = __esm(() => {
139034
139900
  init_src4();
139035
139901
  init_errors();
@@ -139078,6 +139944,7 @@ var init_validation_util = __esm(() => {
139078
139944
  // ../api-core/src/utils/index.ts
139079
139945
  var init_utils11 = __esm(() => {
139080
139946
  init_auth_util();
139947
+ init_dashboard_util();
139081
139948
  init_deployment_util();
139082
139949
  init_leaderboard_util();
139083
139950
  init_lti_util();
@@ -139170,6 +140037,83 @@ var init_bucket_controller = __esm(() => {
139170
140037
  });
139171
140038
  });
139172
140039
 
140040
+ // ../api-core/src/controllers/dashboard.controller.ts
140041
+ function requireDashboardWorkerKey(ctx, slug2) {
140042
+ const scopes = ctx.apiKey?.permissions?.[DASHBOARD_PERMISSION_NAMESPACE];
140043
+ if (!scopes?.includes(dashboardCredentialScope(slug2))) {
140044
+ throw ApiError.forbidden("This endpoint requires the dashboard worker key for this game");
140045
+ }
140046
+ }
140047
+ var listUsers, addUser, issueRecoveryLink, removeUser, remove, verifyLogin, acceptInvite, revokeSessions, acceptReset, dashboard2;
140048
+ var init_dashboard_controller = __esm(() => {
140049
+ init_schemas_index();
140050
+ init_errors();
140051
+ init_utils11();
140052
+ listUsers = requireHumanDeveloper(async (ctx) => {
140053
+ const slug2 = requireSlug(ctx.params.slug);
140054
+ const users2 = await ctx.services.dashboard.listUsers(slug2, ctx.user);
140055
+ return { users: users2 };
140056
+ });
140057
+ addUser = requireHumanDeveloper(async (ctx) => {
140058
+ const slug2 = requireSlug(ctx.params.slug);
140059
+ const body2 = await parseRequestBody(ctx.request, AddGameDashboardUserSchema);
140060
+ return ctx.services.dashboard.addUser(slug2, ctx.user, body2);
140061
+ });
140062
+ issueRecoveryLink = requireHumanDeveloper(async (ctx) => {
140063
+ const slug2 = requireSlug(ctx.params.slug);
140064
+ const userId = requireUserId(ctx.params.userId);
140065
+ return ctx.services.dashboard.issueRecoveryLink(slug2, ctx.user, userId);
140066
+ });
140067
+ removeUser = requireHumanDeveloper(async (ctx) => {
140068
+ const slug2 = requireSlug(ctx.params.slug);
140069
+ const userId = requireUserId(ctx.params.userId);
140070
+ await ctx.services.dashboard.removeUser(slug2, ctx.user, userId);
140071
+ return { success: true };
140072
+ });
140073
+ remove = requireHumanDeveloper(async (ctx) => {
140074
+ const slug2 = requireSlug(ctx.params.slug);
140075
+ await ctx.services.deploy.removeDashboard(slug2, ctx.user);
140076
+ return { success: true };
140077
+ });
140078
+ verifyLogin = requireAuth(async (ctx) => {
140079
+ const slug2 = requireSlug(ctx.params.slug);
140080
+ requireDashboardWorkerKey(ctx, slug2);
140081
+ const body2 = await parseRequestBody(ctx.request, VerifyGameDashboardLoginSchema);
140082
+ const user = await ctx.services.dashboard.verifyLogin(slug2, body2);
140083
+ return { user };
140084
+ });
140085
+ acceptInvite = requireAuth(async (ctx) => {
140086
+ const slug2 = requireSlug(ctx.params.slug);
140087
+ requireDashboardWorkerKey(ctx, slug2);
140088
+ const body2 = await parseRequestBody(ctx.request, AcceptGameDashboardInviteSchema);
140089
+ const user = await ctx.services.dashboard.acceptInvite(slug2, body2);
140090
+ return { user };
140091
+ });
140092
+ revokeSessions = requireHumanDeveloper(async (ctx) => {
140093
+ const slug2 = requireSlug(ctx.params.slug);
140094
+ await ctx.services.deploy.rotateDashboardSessionSecret(slug2, ctx.user);
140095
+ return { success: true };
140096
+ });
140097
+ acceptReset = requireAuth(async (ctx) => {
140098
+ const slug2 = requireSlug(ctx.params.slug);
140099
+ requireDashboardWorkerKey(ctx, slug2);
140100
+ const body2 = await parseRequestBody(ctx.request, AcceptGameDashboardResetSchema);
140101
+ const user = await ctx.services.dashboard.acceptReset(slug2, body2);
140102
+ return { user };
140103
+ });
140104
+ dashboard2 = defineControllerNames("dashboard", {
140105
+ listUsers,
140106
+ addUser,
140107
+ removeUser,
140108
+ remove,
140109
+ issueRecoveryLink,
140110
+ revokeSessions,
140111
+ verifyLogin,
140112
+ acceptInvite,
140113
+ acceptReset
140114
+ });
140115
+ });
140116
+
139173
140117
  // ../api-core/src/controllers/database.controller.ts
139174
140118
  var reset, database;
139175
140119
  var init_database_controller = __esm(() => {
@@ -139264,7 +140208,7 @@ var init_developer_controller = __esm(() => {
139264
140208
  });
139265
140209
 
139266
140210
  // ../api-core/src/controllers/domain.controller.ts
139267
- var add, list, getStatus2, remove, domains2;
140211
+ var add, list, getStatus2, remove2, domains2;
139268
140212
  var init_domain_controller = __esm(() => {
139269
140213
  init_esm();
139270
140214
  init_schemas_index();
@@ -139313,7 +140257,7 @@ var init_domain_controller = __esm(() => {
139313
140257
  const environment = getPlatformEnvironment(ctx.config);
139314
140258
  return ctx.services.domain.getStatus(slug2, hostname4, environment, ctx.user, refresh);
139315
140259
  });
139316
- remove = requireDeveloper(async (ctx) => {
140260
+ remove2 = requireDeveloper(async (ctx) => {
139317
140261
  const slug2 = ctx.params.slug;
139318
140262
  const hostname4 = ctx.params.hostname;
139319
140263
  if (!slug2) {
@@ -139329,12 +140273,12 @@ var init_domain_controller = __esm(() => {
139329
140273
  add,
139330
140274
  list,
139331
140275
  getStatus: getStatus2,
139332
- remove
140276
+ remove: remove2
139333
140277
  });
139334
140278
  });
139335
140279
 
139336
140280
  // ../api-core/src/controllers/game-member.controller.ts
139337
- function requireUserId(userId) {
140281
+ function requireUserId2(userId) {
139338
140282
  if (!userId) {
139339
140283
  throw ApiError.badRequest("Missing user ID");
139340
140284
  }
@@ -139356,13 +140300,13 @@ var init_game_member_controller = __esm(() => {
139356
140300
  });
139357
140301
  updateMemberRole = requireNonAnonymous(async (ctx) => {
139358
140302
  const gameId = requireGameId(ctx.params.gameId);
139359
- const userId = requireUserId(ctx.params.userId);
140303
+ const userId = requireUserId2(ctx.params.userId);
139360
140304
  const body2 = await parseRequestBody(ctx.request, UpdateGameMemberRoleSchema);
139361
140305
  return ctx.services.gameMember.updateRole(gameId, userId, ctx.user, body2);
139362
140306
  });
139363
140307
  removeMember = requireNonAnonymous(async (ctx) => {
139364
140308
  const gameId = requireGameId(ctx.params.gameId);
139365
- const userId = requireUserId(ctx.params.userId);
140309
+ const userId = requireUserId2(ctx.params.userId);
139366
140310
  await ctx.services.gameMember.remove(gameId, userId, ctx.user);
139367
140311
  });
139368
140312
  searchUsersForMember = requireNonAnonymous(async (ctx) => {
@@ -139380,12 +140324,13 @@ var init_game_member_controller = __esm(() => {
139380
140324
  });
139381
140325
 
139382
140326
  // ../api-core/src/controllers/game.controller.ts
139383
- var list2, listAccessible, getSubjects, getTimebackSummaries, getById, getBySlug, getManifest, upsertBySlug, patchGameMetadata, remove2, games2;
140327
+ var list2, listAccessible, getSubjects, getTimebackSummaries, getById, getBySlug, getManifest, upsertBySlug, patchGameMetadata, remove3, games2;
139384
140328
  var init_game_controller = __esm(() => {
139385
140329
  init_esm();
139386
140330
  init_schemas_index();
139387
140331
  init_errors();
139388
140332
  init_utils11();
140333
+ init_auth_util();
139389
140334
  list2 = requireNonAnonymous(async (ctx) => ctx.services.game.list(ctx.user));
139390
140335
  listAccessible = requireNonAnonymous(async (ctx) => ctx.services.game.listAccessible(ctx.user));
139391
140336
  getSubjects = requireNonAnonymous(async (ctx) => ctx.services.game.getSubjects());
@@ -139399,7 +140344,9 @@ var init_game_controller = __esm(() => {
139399
140344
  if (!slug2) {
139400
140345
  throw ApiError.badRequest("Missing game slug");
139401
140346
  }
139402
- return ctx.services.game.getBySlug(slug2, ctx.user);
140347
+ return ctx.services.game.getBySlug(slug2, ctx.user, {
140348
+ scopeAuthorized: workerKeyGrantsGameRead(ctx.apiKey, slug2)
140349
+ });
139403
140350
  });
139404
140351
  getManifest = requireNonAnonymous(async (ctx) => {
139405
140352
  const gameId = requireGameId(ctx.params.gameId);
@@ -139429,7 +140376,7 @@ var init_game_controller = __esm(() => {
139429
140376
  const body2 = await parseRequestBody(ctx.request, PatchGameMetadataSchema);
139430
140377
  return ctx.services.game.updateMetadata(gameId, body2, ctx.user);
139431
140378
  });
139432
- remove2 = requireNonAnonymous(async (ctx) => {
140379
+ remove3 = requireNonAnonymous(async (ctx) => {
139433
140380
  const gameId = requireGameId(ctx.params.gameId);
139434
140381
  await ctx.services.game.delete(gameId, ctx.user);
139435
140382
  });
@@ -139443,7 +140390,7 @@ var init_game_controller = __esm(() => {
139443
140390
  getBySlug,
139444
140391
  upsertBySlug,
139445
140392
  patchGameMetadata,
139446
- remove: remove2
140393
+ remove: remove3
139447
140394
  });
139448
140395
  });
139449
140396
 
@@ -139818,7 +140765,7 @@ var init_session_controller = __esm(() => {
139818
140765
  let baseUrl;
139819
140766
  if (ctx.config.isLocal) {
139820
140767
  try {
139821
- baseUrl = await getTunnelUrl();
140768
+ baseUrl = await getTunnelUrl(ctx.config.baseUrl);
139822
140769
  } catch {}
139823
140770
  }
139824
140771
  return { token, exp, baseUrl };
@@ -140597,6 +141544,7 @@ var init_verify_controller = __esm(() => {
140597
141544
  // ../api-core/src/controllers/index.ts
140598
141545
  var init_controllers = __esm(() => {
140599
141546
  init_bucket_controller();
141547
+ init_dashboard_controller();
140600
141548
  init_database_controller();
140601
141549
  init_deploy_controller();
140602
141550
  init_developer_controller();