@playcademy/vite-plugin 1.1.3-beta.6 → 1.1.3-beta.7
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/index.js +2655 -184
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -24314,7 +24314,7 @@ import path2 from "node:path";
|
|
|
24314
24314
|
// package.json
|
|
24315
24315
|
var package_default = {
|
|
24316
24316
|
name: "@playcademy/vite-plugin",
|
|
24317
|
-
version: "1.1.3-beta.
|
|
24317
|
+
version: "1.1.3-beta.7",
|
|
24318
24318
|
type: "module",
|
|
24319
24319
|
exports: {
|
|
24320
24320
|
".": {
|
|
@@ -25056,6 +25056,7 @@ var WORKER_NAMING;
|
|
|
25056
25056
|
var MAX_WORKER_NAME_LENGTH = 63;
|
|
25057
25057
|
var SECRETS_PREFIX = "secrets_";
|
|
25058
25058
|
var CLOUDFLARE_COMPATIBILITY_DATE = "2025-10-11";
|
|
25059
|
+
var D1_TIME_TRAVEL_RETENTION_DAYS = 30;
|
|
25059
25060
|
var init_cloudflare = __esm(() => {
|
|
25060
25061
|
WORKER_NAMING = {
|
|
25061
25062
|
STAGING_PREFIX: "staging-",
|
|
@@ -25845,7 +25846,7 @@ var package_default2;
|
|
|
25845
25846
|
var init_package = __esm(() => {
|
|
25846
25847
|
package_default2 = {
|
|
25847
25848
|
name: "@playcademy/sandbox",
|
|
25848
|
-
version: "0.6.1-beta.
|
|
25849
|
+
version: "0.6.1-beta.7",
|
|
25849
25850
|
description: "Local development server for Playcademy game development",
|
|
25850
25851
|
type: "module",
|
|
25851
25852
|
exports: {
|
|
@@ -25912,6 +25913,16 @@ var init_package = __esm(() => {
|
|
|
25912
25913
|
}
|
|
25913
25914
|
};
|
|
25914
25915
|
});
|
|
25916
|
+
function deployErrorCode(error) {
|
|
25917
|
+
if (!(error instanceof DomainError)) {
|
|
25918
|
+
return null;
|
|
25919
|
+
}
|
|
25920
|
+
const details = error.details;
|
|
25921
|
+
if (typeof details !== "object" || details === null || !("code" in details)) {
|
|
25922
|
+
return null;
|
|
25923
|
+
}
|
|
25924
|
+
return typeof details.code === "string" ? details.code : null;
|
|
25925
|
+
}
|
|
25915
25926
|
var DomainError;
|
|
25916
25927
|
var BadRequestError;
|
|
25917
25928
|
var UnauthorizedError;
|
|
@@ -26004,6 +26015,171 @@ var init_domain_error = __esm(() => {
|
|
|
26004
26015
|
}
|
|
26005
26016
|
};
|
|
26006
26017
|
});
|
|
26018
|
+
var DEPLOY_ERROR_CODES;
|
|
26019
|
+
var REFUSAL_CODES;
|
|
26020
|
+
var DEPLOY_REFUSAL_CODES;
|
|
26021
|
+
var DELETED_ACCOUNT_LABEL = "(deleted account)";
|
|
26022
|
+
var init_game2 = __esm(() => {
|
|
26023
|
+
DEPLOY_ERROR_CODES = {
|
|
26024
|
+
deployIdPayloadMismatch: "deploy-id-payload-mismatch",
|
|
26025
|
+
stateConflict: "deployment-state-conflict",
|
|
26026
|
+
stateDrift: "deployment-state-drift",
|
|
26027
|
+
destructiveSchema: "destructive-schema-changes",
|
|
26028
|
+
upgradeRequired: "deploy-upgrade-required",
|
|
26029
|
+
checksumMismatch: "migration-checksum-mismatch",
|
|
26030
|
+
journalDivergence: "migration-journal-divergence",
|
|
26031
|
+
outOfOrder: "migration-out-of-order",
|
|
26032
|
+
migrationFailed: "migration-failed",
|
|
26033
|
+
strategyMismatch: "strategy-mismatch",
|
|
26034
|
+
pushFailed: "push-failed",
|
|
26035
|
+
pruneUnmanagedSecrets: "secrets-prune-unmanaged",
|
|
26036
|
+
baselineLedgerNotEmpty: "baseline-ledger-not-empty",
|
|
26037
|
+
baselineDatabaseEmpty: "baseline-database-empty",
|
|
26038
|
+
baselineAlreadyAdopted: "baseline-already-adopted",
|
|
26039
|
+
baselineClaimRejected: "baseline-claim-rejected",
|
|
26040
|
+
restoreUnsupported: "restore-unsupported",
|
|
26041
|
+
restoreIncomplete: "restore-incomplete",
|
|
26042
|
+
restoreBlockedByDeploy: "restore-blocked-by-deploy",
|
|
26043
|
+
restoreExpired: "restore-expired"
|
|
26044
|
+
};
|
|
26045
|
+
REFUSAL_CODES = [
|
|
26046
|
+
DEPLOY_ERROR_CODES.deployIdPayloadMismatch,
|
|
26047
|
+
DEPLOY_ERROR_CODES.stateConflict,
|
|
26048
|
+
DEPLOY_ERROR_CODES.stateDrift,
|
|
26049
|
+
DEPLOY_ERROR_CODES.destructiveSchema,
|
|
26050
|
+
DEPLOY_ERROR_CODES.upgradeRequired,
|
|
26051
|
+
DEPLOY_ERROR_CODES.checksumMismatch,
|
|
26052
|
+
DEPLOY_ERROR_CODES.journalDivergence,
|
|
26053
|
+
DEPLOY_ERROR_CODES.outOfOrder,
|
|
26054
|
+
DEPLOY_ERROR_CODES.strategyMismatch,
|
|
26055
|
+
DEPLOY_ERROR_CODES.pruneUnmanagedSecrets,
|
|
26056
|
+
DEPLOY_ERROR_CODES.baselineLedgerNotEmpty,
|
|
26057
|
+
DEPLOY_ERROR_CODES.baselineDatabaseEmpty,
|
|
26058
|
+
DEPLOY_ERROR_CODES.baselineAlreadyAdopted,
|
|
26059
|
+
DEPLOY_ERROR_CODES.baselineClaimRejected
|
|
26060
|
+
];
|
|
26061
|
+
DEPLOY_REFUSAL_CODES = new Set(REFUSAL_CODES);
|
|
26062
|
+
});
|
|
26063
|
+
var DeployIdConflictError;
|
|
26064
|
+
var DeploymentStateConflictError;
|
|
26065
|
+
var DeploymentStateDriftError;
|
|
26066
|
+
var LegacySchemaUpgradeRequiredError;
|
|
26067
|
+
var DestructiveSchemaError;
|
|
26068
|
+
var MigrationChecksumMismatchError;
|
|
26069
|
+
var MigrationJournalDivergenceError;
|
|
26070
|
+
var MigrationOrderError;
|
|
26071
|
+
var SecretsPruneUnmanagedError;
|
|
26072
|
+
var BaselineLedgerNotEmptyError;
|
|
26073
|
+
var BaselineDatabaseEmptyError;
|
|
26074
|
+
var BaselineAlreadyAdoptedError;
|
|
26075
|
+
var BaselineClaimRejectedError;
|
|
26076
|
+
var MigrationExecutionError;
|
|
26077
|
+
var PushExecutionError;
|
|
26078
|
+
var init_deploy_error = __esm(() => {
|
|
26079
|
+
init_game2();
|
|
26080
|
+
init_domain_error();
|
|
26081
|
+
DeployIdConflictError = class DeployIdConflictError2 extends ConflictError {
|
|
26082
|
+
constructor(deployId, existingJobId) {
|
|
26083
|
+
super(`Deploy '${deployId}' was already created with a different payload — ` + "generate a fresh deployId for a new deploy", { code: DEPLOY_ERROR_CODES.deployIdPayloadMismatch, deployId, existingJobId });
|
|
26084
|
+
this.name = "DeployIdConflictError";
|
|
26085
|
+
}
|
|
26086
|
+
};
|
|
26087
|
+
DeploymentStateConflictError = class DeploymentStateConflictError2 extends ConflictError {
|
|
26088
|
+
constructor(payload) {
|
|
26089
|
+
super("Deployment state conflict: the schema baseline changed since this deploy was " + "prepared — refetch the deployment state, re-diff, and retry", { code: DEPLOY_ERROR_CODES.stateConflict, ...payload });
|
|
26090
|
+
this.name = "DeploymentStateConflictError";
|
|
26091
|
+
}
|
|
26092
|
+
};
|
|
26093
|
+
DeploymentStateDriftError = class DeploymentStateDriftError2 extends ConflictError {
|
|
26094
|
+
constructor(payload) {
|
|
26095
|
+
super("Deployment state drift: the live database schema does not match the recorded " + "fingerprint (out-of-band change or partial prior apply) — repair the " + "database state before deploying", { code: DEPLOY_ERROR_CODES.stateDrift, ...payload });
|
|
26096
|
+
this.name = "DeploymentStateDriftError";
|
|
26097
|
+
}
|
|
26098
|
+
};
|
|
26099
|
+
LegacySchemaUpgradeRequiredError = class LegacySchemaUpgradeRequiredError2 extends ValidationError {
|
|
26100
|
+
constructor() {
|
|
26101
|
+
super("This game's database state is server-managed; the legacy schema payload is no " + "longer accepted — update the Playcademy CLI and redeploy", { code: DEPLOY_ERROR_CODES.upgradeRequired });
|
|
26102
|
+
this.name = "LegacySchemaUpgradeRequiredError";
|
|
26103
|
+
}
|
|
26104
|
+
};
|
|
26105
|
+
DestructiveSchemaError = class DestructiveSchemaError2 extends ValidationError {
|
|
26106
|
+
constructor(statements) {
|
|
26107
|
+
super(`Push contains ${statements.length} destructive statement(s) that can drop data — ` + "review them and re-run with --accept-data-loss to proceed", { code: DEPLOY_ERROR_CODES.destructiveSchema, statements });
|
|
26108
|
+
this.name = "DestructiveSchemaError";
|
|
26109
|
+
}
|
|
26110
|
+
};
|
|
26111
|
+
MigrationChecksumMismatchError = class MigrationChecksumMismatchError2 extends ConflictError {
|
|
26112
|
+
constructor(mismatches) {
|
|
26113
|
+
const tags = mismatches.map((mismatch) => mismatch.tag).join(", ");
|
|
26114
|
+
super(`Applied migration(s) ${tags} no longer match their recorded checksums.`, {
|
|
26115
|
+
code: DEPLOY_ERROR_CODES.checksumMismatch,
|
|
26116
|
+
mismatches
|
|
26117
|
+
});
|
|
26118
|
+
this.name = "MigrationChecksumMismatchError";
|
|
26119
|
+
}
|
|
26120
|
+
};
|
|
26121
|
+
MigrationJournalDivergenceError = class MigrationJournalDivergenceError2 extends ConflictError {
|
|
26122
|
+
constructor(tags) {
|
|
26123
|
+
super(`The database ledger records applied migration(s) missing from the deploy's ` + `journal: ${tags.join(", ")}. Restore the missing migration files (or resolve ` + "the ledger) before deploying", { code: DEPLOY_ERROR_CODES.journalDivergence, tags });
|
|
26124
|
+
this.name = "MigrationJournalDivergenceError";
|
|
26125
|
+
}
|
|
26126
|
+
};
|
|
26127
|
+
MigrationOrderError = class MigrationOrderError2 extends ValidationError {
|
|
26128
|
+
constructor(tags) {
|
|
26129
|
+
super(`Out-of-order migration(s): ${tags.join(", ")} are ordered before migrations that ` + "already applied. This usually means a branch merged with older-numbered " + "migrations — regenerate them after the applied ones and redeploy", { code: DEPLOY_ERROR_CODES.outOfOrder, tags });
|
|
26130
|
+
this.name = "MigrationOrderError";
|
|
26131
|
+
}
|
|
26132
|
+
};
|
|
26133
|
+
SecretsPruneUnmanagedError = class SecretsPruneUnmanagedError2 extends ValidationError {
|
|
26134
|
+
constructor(keys) {
|
|
26135
|
+
super(`Cannot prune secret(s) not managed by the platform: ${keys.join(", ")}. ` + "Only keys the platform previously pushed can be pruned", { code: DEPLOY_ERROR_CODES.pruneUnmanagedSecrets, keys });
|
|
26136
|
+
this.name = "SecretsPruneUnmanagedError";
|
|
26137
|
+
}
|
|
26138
|
+
};
|
|
26139
|
+
BaselineLedgerNotEmptyError = class BaselineLedgerNotEmptyError2 extends ConflictError {
|
|
26140
|
+
constructor(tags) {
|
|
26141
|
+
super(`Cannot baseline: the migration ledger already records ${tags.length} applied ` + "migration(s) — this database is already server-managed. Just deploy", { code: DEPLOY_ERROR_CODES.baselineLedgerNotEmpty, tags });
|
|
26142
|
+
this.name = "BaselineLedgerNotEmptyError";
|
|
26143
|
+
}
|
|
26144
|
+
};
|
|
26145
|
+
BaselineDatabaseEmptyError = class BaselineDatabaseEmptyError2 extends ValidationError {
|
|
26146
|
+
constructor() {
|
|
26147
|
+
super("Cannot baseline an empty database — there is no applied history to claim. " + "An empty database needs no baseline: deploy directly and the full " + "journal (or schema) applies fresh.", { code: DEPLOY_ERROR_CODES.baselineDatabaseEmpty });
|
|
26148
|
+
this.name = "BaselineDatabaseEmptyError";
|
|
26149
|
+
}
|
|
26150
|
+
};
|
|
26151
|
+
BaselineAlreadyAdoptedError = class BaselineAlreadyAdoptedError2 extends ConflictError {
|
|
26152
|
+
constructor(baselineSource) {
|
|
26153
|
+
super("Cannot baseline: deployment state already records a schema baseline" + `${baselineSource ? ` (source: ${baselineSource})` : ""} — ` + "use realign/resolve to repair drift instead", { code: DEPLOY_ERROR_CODES.baselineAlreadyAdopted, baselineSource });
|
|
26154
|
+
this.name = "BaselineAlreadyAdoptedError";
|
|
26155
|
+
}
|
|
26156
|
+
};
|
|
26157
|
+
BaselineClaimRejectedError = class BaselineClaimRejectedError2 extends ConflictError {
|
|
26158
|
+
constructor(details) {
|
|
26159
|
+
const [first] = details.contradictions;
|
|
26160
|
+
super(first ? `Cannot baseline: the claim contradicts the live database — ${first.tag} ${first.detail ?? ""}` : "Cannot baseline: the claim includes migrations that cannot be verified against the live database — " + "pass --allow-unverified to record them anyway", { code: DEPLOY_ERROR_CODES.baselineClaimRejected, ...details });
|
|
26161
|
+
this.name = "BaselineClaimRejectedError";
|
|
26162
|
+
}
|
|
26163
|
+
};
|
|
26164
|
+
MigrationExecutionError = class MigrationExecutionError2 extends ValidationError {
|
|
26165
|
+
tag;
|
|
26166
|
+
offset;
|
|
26167
|
+
constructor(input) {
|
|
26168
|
+
super(`Migration '${input.tag}' failed and rolled back: ${input.d1Message}. ` + "Fix the migration SQL and redeploy — the deploy resumes from this migration", { code: DEPLOY_ERROR_CODES.migrationFailed, ...input });
|
|
26169
|
+
this.name = "MigrationExecutionError";
|
|
26170
|
+
this.tag = input.tag;
|
|
26171
|
+
this.offset = input.offset;
|
|
26172
|
+
}
|
|
26173
|
+
};
|
|
26174
|
+
PushExecutionError = class PushExecutionError2 extends ValidationError {
|
|
26175
|
+
offset;
|
|
26176
|
+
constructor(input) {
|
|
26177
|
+
super(`Push schema changes failed and rolled back: ${input.d1Message}. ` + "Fix the schema and redeploy — nothing was applied", { code: DEPLOY_ERROR_CODES.pushFailed, ...input });
|
|
26178
|
+
this.name = "PushExecutionError";
|
|
26179
|
+
this.offset = input.offset;
|
|
26180
|
+
}
|
|
26181
|
+
};
|
|
26182
|
+
});
|
|
26007
26183
|
var STATUS_MAP;
|
|
26008
26184
|
var ApiError;
|
|
26009
26185
|
var init_api_error = __esm(() => {
|
|
@@ -26101,6 +26277,7 @@ var init_api_error = __esm(() => {
|
|
|
26101
26277
|
});
|
|
26102
26278
|
var init_errors = __esm(() => {
|
|
26103
26279
|
init_domain_error();
|
|
26280
|
+
init_deploy_error();
|
|
26104
26281
|
init_api_error();
|
|
26105
26282
|
});
|
|
26106
26283
|
function isBrowser() {
|
|
@@ -30546,7 +30723,8 @@ var init_schema = __esm(() => {
|
|
|
30546
30723
|
ltiTestMode: exports_external.boolean().default(false),
|
|
30547
30724
|
platformServiceJwt: platformServiceJwtConfigSchema.optional(),
|
|
30548
30725
|
uploadBucket: exports_external.string().optional(),
|
|
30549
|
-
queueIngressSecret: exports_external.string().optional()
|
|
30726
|
+
queueIngressSecret: exports_external.string().optional(),
|
|
30727
|
+
secretsManifestPepper: exports_external.string().optional()
|
|
30550
30728
|
}).superRefine((config2, ctx) => {
|
|
30551
30729
|
if (config2.isLocal && !config2.baseUrl) {
|
|
30552
30730
|
ctx.addIssue({
|
|
@@ -36005,6 +36183,8 @@ var deploymentTargetEnum;
|
|
|
36005
36183
|
var deployJobStatusEnum;
|
|
36006
36184
|
var gameDeployments;
|
|
36007
36185
|
var gameDeployJobs;
|
|
36186
|
+
var gameDeployEvents;
|
|
36187
|
+
var gameDeploymentState;
|
|
36008
36188
|
var customHostnameStatusEnum;
|
|
36009
36189
|
var customHostnameSslStatusEnum;
|
|
36010
36190
|
var customHostnameEnvironmentEnum;
|
|
@@ -36097,15 +36277,20 @@ var init_table5 = __esm(() => {
|
|
|
36097
36277
|
target: deploymentTargetEnum("target").notNull().default("game"),
|
|
36098
36278
|
url: text("url").notNull(),
|
|
36099
36279
|
codeHash: text("code_hash"),
|
|
36280
|
+
schemaHash: text("schema_hash"),
|
|
36281
|
+
schemaFingerprint: text("schema_fingerprint"),
|
|
36282
|
+
timeTravelBookmark: text("time_travel_bookmark"),
|
|
36283
|
+
bookmarkCapturedAt: timestamp("bookmark_captured_at", { withTimezone: true }),
|
|
36100
36284
|
isActive: boolean("is_active").notNull().default(false),
|
|
36101
36285
|
resources: jsonb("resources").$type(),
|
|
36102
36286
|
deployedAt: timestamp("deployed_at", { withTimezone: true }).notNull().defaultNow()
|
|
36103
|
-
});
|
|
36287
|
+
}, (table3) => [index("game_deployments_game_target_idx").on(table3.gameId, table3.target)]);
|
|
36104
36288
|
gameDeployJobs = pgTable("game_deploy_jobs", {
|
|
36105
36289
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
36106
36290
|
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
36107
|
-
userId: text("user_id").
|
|
36291
|
+
userId: text("user_id").references(() => users.id, { onDelete: "set null" }),
|
|
36108
36292
|
status: deployJobStatusEnum("status").notNull().default("pending"),
|
|
36293
|
+
deployId: text("deploy_id"),
|
|
36109
36294
|
request: jsonb("request").$type().notNull(),
|
|
36110
36295
|
events: jsonb("events").$type().notNull().default([]),
|
|
36111
36296
|
error: text("error"),
|
|
@@ -36119,6 +36304,27 @@ var init_table5 = __esm(() => {
|
|
|
36119
36304
|
createdAt: timestamp("created_at", { mode: "date", withTimezone: true }).notNull().defaultNow(),
|
|
36120
36305
|
startedAt: timestamp("started_at", { mode: "date", withTimezone: true }),
|
|
36121
36306
|
completedAt: timestamp("completed_at", { mode: "date", withTimezone: true })
|
|
36307
|
+
}, (table3) => [uniqueIndex("game_deploy_jobs_game_deploy_id_idx").on(table3.gameId, table3.deployId)]);
|
|
36308
|
+
gameDeployEvents = pgTable("game_deploy_events", {
|
|
36309
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
36310
|
+
gameId: uuid("game_id").notNull().references(() => games.id, { onDelete: "cascade" }),
|
|
36311
|
+
userId: text("user_id").references(() => users.id, { onDelete: "set null" }),
|
|
36312
|
+
kind: text("kind").$type().notNull(),
|
|
36313
|
+
payload: jsonb("payload").$type().notNull(),
|
|
36314
|
+
createdAt: timestamp("created_at", { mode: "date", withTimezone: true }).notNull().defaultNow()
|
|
36315
|
+
}, (table3) => [index("game_deploy_events_game_idx").on(table3.gameId, table3.createdAt)]);
|
|
36316
|
+
gameDeploymentState = pgTable("game_deployment_state", {
|
|
36317
|
+
id: uuid("id").primaryKey().defaultRandom(),
|
|
36318
|
+
gameId: uuid("game_id").notNull().unique().references(() => games.id, { onDelete: "cascade" }),
|
|
36319
|
+
schemaHash: text("schema_hash"),
|
|
36320
|
+
schemaSnapshot: jsonb("schema_snapshot"),
|
|
36321
|
+
schemaFingerprint: text("schema_fingerprint"),
|
|
36322
|
+
secretsManifest: jsonb("secrets_manifest").$type(),
|
|
36323
|
+
integrationsHash: text("integrations_hash"),
|
|
36324
|
+
buildHash: text("build_hash"),
|
|
36325
|
+
compatibilityDate: text("compatibility_date"),
|
|
36326
|
+
baselineSource: text("baseline_source").$type(),
|
|
36327
|
+
updatedAt: timestamp("updated_at", { withTimezone: true }).notNull().defaultNow()
|
|
36122
36328
|
});
|
|
36123
36329
|
customHostnameStatusEnum = pgEnum("custom_hostname_status", [
|
|
36124
36330
|
"pending",
|
|
@@ -36280,7 +36486,9 @@ __export(exports_tables_index, {
|
|
|
36280
36486
|
gameMembers: () => gameMembers,
|
|
36281
36487
|
gameMemberRoleEnum: () => gameMemberRoleEnum,
|
|
36282
36488
|
gameDeployments: () => gameDeployments,
|
|
36489
|
+
gameDeploymentState: () => gameDeploymentState,
|
|
36283
36490
|
gameDeployJobs: () => gameDeployJobs,
|
|
36491
|
+
gameDeployEvents: () => gameDeployEvents,
|
|
36284
36492
|
gameDashboardUsersRelations: () => gameDashboardUsersRelations,
|
|
36285
36493
|
gameDashboardUsers: () => gameDashboardUsers,
|
|
36286
36494
|
gameDashboardUserRoleEnum: () => gameDashboardUserRoleEnum,
|
|
@@ -47055,6 +47263,107 @@ var init_zip = __esm(() => {
|
|
|
47055
47263
|
init_src2();
|
|
47056
47264
|
import_jszip = __toESM2(require_lib3(), 1);
|
|
47057
47265
|
});
|
|
47266
|
+
function isPreviewStage(stage) {
|
|
47267
|
+
return PREVIEW_STAGE_PATTERN.test(stage);
|
|
47268
|
+
}
|
|
47269
|
+
var PREVIEW_STAGE_PATTERN;
|
|
47270
|
+
var init_stages = __esm(() => {
|
|
47271
|
+
PREVIEW_STAGE_PATTERN = /^pr-\d+$/;
|
|
47272
|
+
});
|
|
47273
|
+
function deployJobInstant() {
|
|
47274
|
+
return sql`COALESCE(${gameDeployJobs.completedAt}, ${gameDeployJobs.createdAt})`;
|
|
47275
|
+
}
|
|
47276
|
+
async function findLastSuccessfulDeploy(db2, gameId) {
|
|
47277
|
+
const job = await db2.query.gameDeployJobs.findFirst({
|
|
47278
|
+
where: and(eq(gameDeployJobs.gameId, gameId), eq(gameDeployJobs.status, "succeeded")),
|
|
47279
|
+
orderBy: desc(deployJobInstant()),
|
|
47280
|
+
columns: { userId: true, createdAt: true, completedAt: true }
|
|
47281
|
+
});
|
|
47282
|
+
if (!job) {
|
|
47283
|
+
return null;
|
|
47284
|
+
}
|
|
47285
|
+
return { userId: job.userId, at: job.completedAt ?? job.createdAt };
|
|
47286
|
+
}
|
|
47287
|
+
async function findLastSuccessfulDeployWithEmail(db2, gameId) {
|
|
47288
|
+
const lastDeploy = await findLastSuccessfulDeploy(db2, gameId);
|
|
47289
|
+
if (!lastDeploy) {
|
|
47290
|
+
return null;
|
|
47291
|
+
}
|
|
47292
|
+
const deployer = lastDeploy.userId ? await db2.query.users.findFirst({
|
|
47293
|
+
where: eq(users.id, lastDeploy.userId),
|
|
47294
|
+
columns: { email: true }
|
|
47295
|
+
}) : null;
|
|
47296
|
+
return { ...lastDeploy, email: deployer?.email ?? null };
|
|
47297
|
+
}
|
|
47298
|
+
function getGameDeploymentId(gameSlug, sstStage) {
|
|
47299
|
+
if (sstStage === "production") {
|
|
47300
|
+
return gameSlug;
|
|
47301
|
+
}
|
|
47302
|
+
if (sstStage === "dev" || isPreviewStage(sstStage)) {
|
|
47303
|
+
return `${WORKER_NAMING.STAGING_PREFIX}${gameSlug}`;
|
|
47304
|
+
}
|
|
47305
|
+
return `${WORKER_NAMING.LOCAL_PREFIX}${sstStage}-${gameSlug}`;
|
|
47306
|
+
}
|
|
47307
|
+
function getDashboardDeploymentId(gameSlug, sstStage) {
|
|
47308
|
+
return `${getGameDeploymentId(gameSlug, sstStage)}${DASHBOARD_WORKER_SUFFIX}`;
|
|
47309
|
+
}
|
|
47310
|
+
function getGameWorkerApiKeyName(slug) {
|
|
47311
|
+
return `${GAME_WORKER_KEY_PREFIX}${slug}`.substring(0, 32);
|
|
47312
|
+
}
|
|
47313
|
+
function getDashboardWorkerApiKeyName(slug) {
|
|
47314
|
+
return `${DASHBOARD_WORKER_KEY_PREFIX}${slug}`;
|
|
47315
|
+
}
|
|
47316
|
+
function toBindingName(queueKey) {
|
|
47317
|
+
return `${queueKey.replace(/-/g, "_").toUpperCase()}_QUEUE`;
|
|
47318
|
+
}
|
|
47319
|
+
function isSchemaAdopted(state) {
|
|
47320
|
+
if (!state) {
|
|
47321
|
+
return false;
|
|
47322
|
+
}
|
|
47323
|
+
return state.schemaHash !== null || state.schemaFingerprint !== null || state.schemaSnapshot !== null;
|
|
47324
|
+
}
|
|
47325
|
+
function isPushAdopted(state) {
|
|
47326
|
+
return Boolean(state?.schemaHash);
|
|
47327
|
+
}
|
|
47328
|
+
function isMigrateManaged(state) {
|
|
47329
|
+
return Boolean(state?.schemaFingerprint) && !state?.schemaHash;
|
|
47330
|
+
}
|
|
47331
|
+
function generateDeploymentHash(code) {
|
|
47332
|
+
return sha256Hex(code);
|
|
47333
|
+
}
|
|
47334
|
+
function computeDeployPayloadFingerprint(payload) {
|
|
47335
|
+
return sha256Hex(canonicalJson(payload));
|
|
47336
|
+
}
|
|
47337
|
+
function canonicalJson(value) {
|
|
47338
|
+
if (value === null || typeof value !== "object") {
|
|
47339
|
+
return JSON.stringify(value) ?? "null";
|
|
47340
|
+
}
|
|
47341
|
+
if (Array.isArray(value)) {
|
|
47342
|
+
return `[${value.map((item) => canonicalJson(item)).join(",")}]`;
|
|
47343
|
+
}
|
|
47344
|
+
const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).toSorted(([a], [b]) => compareKeys(a, b)).map(([key, entryValue]) => `${JSON.stringify(key)}:${canonicalJson(entryValue)}`);
|
|
47345
|
+
return `{${entries.join(",")}}`;
|
|
47346
|
+
}
|
|
47347
|
+
function compareKeys(a, b) {
|
|
47348
|
+
if (a < b) {
|
|
47349
|
+
return -1;
|
|
47350
|
+
}
|
|
47351
|
+
if (a > b) {
|
|
47352
|
+
return 1;
|
|
47353
|
+
}
|
|
47354
|
+
return 0;
|
|
47355
|
+
}
|
|
47356
|
+
var GAME_WORKER_KEY_PREFIX = "game-worker-";
|
|
47357
|
+
var DASHBOARD_WORKER_KEY_PREFIX = "dash-worker-";
|
|
47358
|
+
var init_deployment_util = __esm(() => {
|
|
47359
|
+
init_drizzle_orm();
|
|
47360
|
+
init_src();
|
|
47361
|
+
init_tables_index();
|
|
47362
|
+
init_stages();
|
|
47363
|
+
});
|
|
47364
|
+
function isEventDetails(value) {
|
|
47365
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
47366
|
+
}
|
|
47058
47367
|
|
|
47059
47368
|
class DeployJobService {
|
|
47060
47369
|
deps;
|
|
@@ -47068,6 +47377,9 @@ class DeployJobService {
|
|
|
47068
47377
|
if (leaseLost) {
|
|
47069
47378
|
return "lease_lost";
|
|
47070
47379
|
}
|
|
47380
|
+
if (DEPLOY_REFUSAL_CODES.has(deployErrorCode(error) ?? "")) {
|
|
47381
|
+
return "refused";
|
|
47382
|
+
}
|
|
47071
47383
|
if (error instanceof DomainError) {
|
|
47072
47384
|
return "domain_error";
|
|
47073
47385
|
}
|
|
@@ -47147,13 +47459,28 @@ class DeployJobService {
|
|
|
47147
47459
|
const bucketName = this.getUploadBucket();
|
|
47148
47460
|
this.deps.storage.deleteObject(bucketName, codeUploadToken).catch(catchAttrs("deploy_job.temp_cleanup"));
|
|
47149
47461
|
}
|
|
47150
|
-
sanitizeRequestForPersistence(request) {
|
|
47151
|
-
const sanitized = {
|
|
47462
|
+
async sanitizeRequestForPersistence(request) {
|
|
47463
|
+
const sanitized = {
|
|
47464
|
+
...request
|
|
47465
|
+
};
|
|
47152
47466
|
delete sanitized._headers;
|
|
47153
47467
|
delete sanitized.code;
|
|
47154
47468
|
delete sanitized.codeUploadToken;
|
|
47469
|
+
const codeFingerprint = await this.computeCodeFingerprint(request);
|
|
47470
|
+
if (codeFingerprint) {
|
|
47471
|
+
sanitized.codeFingerprint = codeFingerprint;
|
|
47472
|
+
}
|
|
47155
47473
|
return sanitized;
|
|
47156
47474
|
}
|
|
47475
|
+
async computeCodeFingerprint(request) {
|
|
47476
|
+
if (request.codeUploadToken) {
|
|
47477
|
+
return `upload:${request.codeUploadToken}`;
|
|
47478
|
+
}
|
|
47479
|
+
if (request.code) {
|
|
47480
|
+
return `sha256:${await generateDeploymentHash(request.code)}`;
|
|
47481
|
+
}
|
|
47482
|
+
return;
|
|
47483
|
+
}
|
|
47157
47484
|
getLeaseExpiry() {
|
|
47158
47485
|
return new Date(Date.now() + DEPLOY_JOB_LEASE_MS);
|
|
47159
47486
|
}
|
|
@@ -47186,8 +47513,36 @@ class DeployJobService {
|
|
|
47186
47513
|
heartbeatAt: null
|
|
47187
47514
|
}).where(and(eq(gameDeployJobs.id, jobId), eq(gameDeployJobs.leaseId, leaseId)));
|
|
47188
47515
|
}
|
|
47516
|
+
async findByDeployId(gameId, deployId) {
|
|
47517
|
+
const job = await this.deps.db.query.gameDeployJobs.findFirst({
|
|
47518
|
+
where: and(eq(gameDeployJobs.gameId, gameId), eq(gameDeployJobs.deployId, deployId))
|
|
47519
|
+
});
|
|
47520
|
+
return job ?? null;
|
|
47521
|
+
}
|
|
47522
|
+
async resolveIdempotentReplay(existing, request) {
|
|
47523
|
+
const [incoming, stored] = await Promise.all([
|
|
47524
|
+
this.sanitizeRequestForPersistence(request).then(computeDeployPayloadFingerprint),
|
|
47525
|
+
computeDeployPayloadFingerprint(existing.request)
|
|
47526
|
+
]);
|
|
47527
|
+
if (incoming !== stored) {
|
|
47528
|
+
setAttribute("app.deploy_job.idempotency", "payload_mismatch");
|
|
47529
|
+
throw new DeployIdConflictError(request.deployId, existing.id);
|
|
47530
|
+
}
|
|
47531
|
+
setAttributes({
|
|
47532
|
+
"app.deploy_job.idempotency": "replayed",
|
|
47533
|
+
"app.deploy_job.id": existing.id,
|
|
47534
|
+
"app.deploy_job.status": existing.status
|
|
47535
|
+
});
|
|
47536
|
+
return this.toResponse(existing);
|
|
47537
|
+
}
|
|
47189
47538
|
async create(slug, request, user) {
|
|
47190
47539
|
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
47540
|
+
if (request.deployId) {
|
|
47541
|
+
const existing = await this.findByDeployId(game2.id, request.deployId);
|
|
47542
|
+
if (existing) {
|
|
47543
|
+
return this.resolveIdempotentReplay(existing, request);
|
|
47544
|
+
}
|
|
47545
|
+
}
|
|
47191
47546
|
const jobId = crypto.randomUUID();
|
|
47192
47547
|
let codeSource = "none";
|
|
47193
47548
|
if (request.code) {
|
|
@@ -47203,7 +47558,7 @@ class DeployJobService {
|
|
|
47203
47558
|
request.code = await this.loadUploadedCode(request.codeUploadToken, game2.id);
|
|
47204
47559
|
}
|
|
47205
47560
|
setAttribute("app.deploy_job.code_bundle_size", request.code?.length ?? 0);
|
|
47206
|
-
const sanitizedRequest = this.sanitizeRequestForPersistence(request);
|
|
47561
|
+
const sanitizedRequest = await this.sanitizeRequestForPersistence(request);
|
|
47207
47562
|
if (request.code) {
|
|
47208
47563
|
await this.storeCodeBundle(jobId, request.code);
|
|
47209
47564
|
}
|
|
@@ -47213,6 +47568,7 @@ class DeployJobService {
|
|
|
47213
47568
|
id: jobId,
|
|
47214
47569
|
gameId: game2.id,
|
|
47215
47570
|
userId: user.id,
|
|
47571
|
+
deployId: request.deployId ?? null,
|
|
47216
47572
|
request: sanitizedRequest,
|
|
47217
47573
|
events: [
|
|
47218
47574
|
{
|
|
@@ -47224,6 +47580,12 @@ class DeployJobService {
|
|
|
47224
47580
|
}).returning();
|
|
47225
47581
|
} catch (error) {
|
|
47226
47582
|
await this.deleteCodeBundle(jobId);
|
|
47583
|
+
if (request.deployId) {
|
|
47584
|
+
const winner = await this.findByDeployId(game2.id, request.deployId);
|
|
47585
|
+
if (winner) {
|
|
47586
|
+
return this.resolveIdempotentReplay(winner, request);
|
|
47587
|
+
}
|
|
47588
|
+
}
|
|
47227
47589
|
throw error;
|
|
47228
47590
|
}
|
|
47229
47591
|
if (!job) {
|
|
@@ -47338,7 +47700,11 @@ class DeployJobService {
|
|
|
47338
47700
|
"app.deploy_job.error_status": errorClassification?.errorStatus
|
|
47339
47701
|
});
|
|
47340
47702
|
if (!effectiveLeaseLost) {
|
|
47341
|
-
|
|
47703
|
+
const structuredDetails = error instanceof DomainError && isEventDetails(error.details) ? error.details : undefined;
|
|
47704
|
+
await this.addStatusEvent(jobId, "Deployment failed", {
|
|
47705
|
+
error: message,
|
|
47706
|
+
...structuredDetails
|
|
47707
|
+
});
|
|
47342
47708
|
const failed = await this.markFailed(jobId, leaseId, message, errorClassification);
|
|
47343
47709
|
if (!failed) {
|
|
47344
47710
|
await this.clearLease(jobId, leaseId);
|
|
@@ -47351,18 +47717,21 @@ class DeployJobService {
|
|
|
47351
47717
|
displayName: game2.displayName,
|
|
47352
47718
|
error: message,
|
|
47353
47719
|
target,
|
|
47354
|
-
developer: { id: user.id, email: user.email }
|
|
47720
|
+
developer: { id: user.id, email: user.email },
|
|
47721
|
+
errorCode: deployErrorCode(error)
|
|
47355
47722
|
});
|
|
47356
47723
|
}
|
|
47357
47724
|
await this.deleteCodeBundle(jobId);
|
|
47358
47725
|
}
|
|
47359
47726
|
async loadJobActors(job, jobId, leaseId, onMissing) {
|
|
47360
|
-
const game2 = await
|
|
47361
|
-
|
|
47362
|
-
|
|
47363
|
-
|
|
47364
|
-
|
|
47365
|
-
|
|
47727
|
+
const [game2, user] = await Promise.all([
|
|
47728
|
+
this.deps.db.query.games.findFirst({
|
|
47729
|
+
where: eq(games.id, job.gameId)
|
|
47730
|
+
}),
|
|
47731
|
+
job.userId ? this.deps.db.query.users.findFirst({
|
|
47732
|
+
where: eq(users.id, job.userId)
|
|
47733
|
+
}) : undefined
|
|
47734
|
+
]);
|
|
47366
47735
|
if (!game2 || !user) {
|
|
47367
47736
|
const message = !game2 ? "Deploy job game no longer exists" : "Deploy job user no longer exists";
|
|
47368
47737
|
onMissing();
|
|
@@ -47448,7 +47817,8 @@ class DeployJobService {
|
|
|
47448
47817
|
for await (const step of this.deps.runDeploy(game2.slug, request, user, uploadDeps, extractZipToDirectory)) {
|
|
47449
47818
|
assertLease();
|
|
47450
47819
|
if (step.type === "status" && "message" in step.data && typeof step.data.message === "string") {
|
|
47451
|
-
|
|
47820
|
+
const details = "details" in step.data && isEventDetails(step.data.details) ? step.data.details : undefined;
|
|
47821
|
+
await this.addStatusEvent(jobId, step.data.message, details);
|
|
47452
47822
|
}
|
|
47453
47823
|
}
|
|
47454
47824
|
assertLease();
|
|
@@ -47509,8 +47879,10 @@ var init_deploy_job_service = __esm(() => {
|
|
|
47509
47879
|
init_helpers_index();
|
|
47510
47880
|
init_tables_index();
|
|
47511
47881
|
init_spans();
|
|
47882
|
+
init_game2();
|
|
47512
47883
|
init_zip();
|
|
47513
47884
|
init_errors();
|
|
47885
|
+
init_deployment_util();
|
|
47514
47886
|
STATUS_MAP2 = {
|
|
47515
47887
|
BAD_REQUEST: 400,
|
|
47516
47888
|
UNAUTHORIZED: 401,
|
|
@@ -47532,7 +47904,154 @@ var init_deploy_job_service = __esm(() => {
|
|
|
47532
47904
|
DEPLOY_JOB_LEASE_MS = 2 * 60 * 1000;
|
|
47533
47905
|
DEPLOY_JOB_HEARTBEAT_MS = 30 * 1000;
|
|
47534
47906
|
});
|
|
47535
|
-
|
|
47907
|
+
function normalizeSqlForChecksum(sql4) {
|
|
47908
|
+
const unified = sql4.replace(/\r\n/g, `
|
|
47909
|
+
`).replace(/\r/g, `
|
|
47910
|
+
`);
|
|
47911
|
+
const stripped = stripSqlComments(unified);
|
|
47912
|
+
return stripped.split(`
|
|
47913
|
+
`).map((line3) => line3.replace(/\s+$/, "")).filter((line3) => line3 !== "").join(`
|
|
47914
|
+
`);
|
|
47915
|
+
}
|
|
47916
|
+
function stripSqlComments(sql4) {
|
|
47917
|
+
let output = "";
|
|
47918
|
+
let i2 = 0;
|
|
47919
|
+
while (i2 < sql4.length) {
|
|
47920
|
+
const char3 = sql4[i2];
|
|
47921
|
+
const next = sql4[i2 + 1];
|
|
47922
|
+
if (char3 === "-" && next === "-") {
|
|
47923
|
+
i2 = skipLineComment(sql4, i2);
|
|
47924
|
+
} else if (char3 === "/" && next === "*") {
|
|
47925
|
+
output += " ";
|
|
47926
|
+
i2 = skipBlockComment(sql4, i2);
|
|
47927
|
+
} else if (char3 === "'" || char3 === '"' || char3 === "`") {
|
|
47928
|
+
const quoted = copyQuoted(sql4, i2, char3);
|
|
47929
|
+
output += quoted.text;
|
|
47930
|
+
i2 = quoted.end;
|
|
47931
|
+
} else if (char3 === "[") {
|
|
47932
|
+
const bracketed = copyBracketed(sql4, i2);
|
|
47933
|
+
output += bracketed.text;
|
|
47934
|
+
i2 = bracketed.end;
|
|
47935
|
+
} else {
|
|
47936
|
+
output += char3;
|
|
47937
|
+
i2++;
|
|
47938
|
+
}
|
|
47939
|
+
}
|
|
47940
|
+
return output;
|
|
47941
|
+
}
|
|
47942
|
+
function skipLineComment(sql4, start2) {
|
|
47943
|
+
let i2 = start2 + 2;
|
|
47944
|
+
while (i2 < sql4.length && sql4[i2] !== `
|
|
47945
|
+
`) {
|
|
47946
|
+
i2++;
|
|
47947
|
+
}
|
|
47948
|
+
return i2;
|
|
47949
|
+
}
|
|
47950
|
+
function skipBlockComment(sql4, start2) {
|
|
47951
|
+
let i2 = start2 + 2;
|
|
47952
|
+
while (i2 < sql4.length && !(sql4[i2] === "*" && sql4[i2 + 1] === "/")) {
|
|
47953
|
+
i2++;
|
|
47954
|
+
}
|
|
47955
|
+
return i2 + 2;
|
|
47956
|
+
}
|
|
47957
|
+
function copyQuoted(sql4, start2, quote) {
|
|
47958
|
+
let text3 = quote;
|
|
47959
|
+
let i2 = start2 + 1;
|
|
47960
|
+
while (i2 < sql4.length) {
|
|
47961
|
+
text3 += sql4[i2];
|
|
47962
|
+
if (sql4[i2] !== quote) {
|
|
47963
|
+
i2++;
|
|
47964
|
+
} else if (sql4[i2 + 1] === quote) {
|
|
47965
|
+
text3 += quote;
|
|
47966
|
+
i2 += 2;
|
|
47967
|
+
} else {
|
|
47968
|
+
i2++;
|
|
47969
|
+
break;
|
|
47970
|
+
}
|
|
47971
|
+
}
|
|
47972
|
+
return { text: text3, end: i2 };
|
|
47973
|
+
}
|
|
47974
|
+
function copyBracketed(sql4, start2) {
|
|
47975
|
+
let text3 = "[";
|
|
47976
|
+
let i2 = start2 + 1;
|
|
47977
|
+
while (i2 < sql4.length) {
|
|
47978
|
+
text3 += sql4[i2];
|
|
47979
|
+
i2++;
|
|
47980
|
+
if (sql4[i2 - 1] === "]") {
|
|
47981
|
+
break;
|
|
47982
|
+
}
|
|
47983
|
+
}
|
|
47984
|
+
return { text: text3, end: i2 };
|
|
47985
|
+
}
|
|
47986
|
+
function findOversizedStatement(statements) {
|
|
47987
|
+
for (const [index2, statement] of statements.entries()) {
|
|
47988
|
+
const byteLength = Buffer.byteLength(statement, "utf8");
|
|
47989
|
+
if (byteLength > D1_MAX_STATEMENT_BYTES) {
|
|
47990
|
+
return { index: index2, byteLength };
|
|
47991
|
+
}
|
|
47992
|
+
}
|
|
47993
|
+
return null;
|
|
47994
|
+
}
|
|
47995
|
+
var MIGRATION_LEDGER_TABLE = "_playcademy_migrations";
|
|
47996
|
+
var MIGRATION_CHECKSUM_ALGO = "sha256-v1";
|
|
47997
|
+
var D1_MAX_STATEMENT_BYTES;
|
|
47998
|
+
var init_schema3 = __esm(() => {
|
|
47999
|
+
D1_MAX_STATEMENT_BYTES = 100 * 1024;
|
|
48000
|
+
});
|
|
48001
|
+
function parseD1ErrorOffset(message) {
|
|
48002
|
+
const match = message.match(/at offset (\d+)/);
|
|
48003
|
+
return match ? Number(match[1]) : null;
|
|
48004
|
+
}
|
|
48005
|
+
var MIGRATION_LEDGER_DDL;
|
|
48006
|
+
var D1StatementTooLargeError;
|
|
48007
|
+
var D1BatchError;
|
|
48008
|
+
var D1MigrationError;
|
|
48009
|
+
var init_d1 = __esm(() => {
|
|
48010
|
+
init_schema3();
|
|
48011
|
+
MIGRATION_LEDGER_DDL = `CREATE TABLE IF NOT EXISTS ${MIGRATION_LEDGER_TABLE} (
|
|
48012
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
48013
|
+
tag TEXT NOT NULL UNIQUE,
|
|
48014
|
+
checksum TEXT NOT NULL,
|
|
48015
|
+
checksum_algo TEXT NOT NULL DEFAULT 'sha256-v1',
|
|
48016
|
+
deploy_id TEXT NOT NULL,
|
|
48017
|
+
applied_by TEXT,
|
|
48018
|
+
applied_at TEXT NOT NULL,
|
|
48019
|
+
source TEXT NOT NULL DEFAULT 'deploy',
|
|
48020
|
+
statements_total INTEGER
|
|
48021
|
+
)`;
|
|
48022
|
+
D1StatementTooLargeError = class D1StatementTooLargeError2 extends Error {
|
|
48023
|
+
name = "D1StatementTooLargeError";
|
|
48024
|
+
statementIndex;
|
|
48025
|
+
byteLength;
|
|
48026
|
+
constructor(tag, statementIndex, byteLength) {
|
|
48027
|
+
super(`Migration '${tag}' statement ${statementIndex} is ${byteLength} bytes — ` + `over D1's ${D1_MAX_STATEMENT_BYTES / 1024} KB per-statement limit. ` + "Split the statement or reduce its size.");
|
|
48028
|
+
this.statementIndex = statementIndex;
|
|
48029
|
+
this.byteLength = byteLength;
|
|
48030
|
+
}
|
|
48031
|
+
};
|
|
48032
|
+
D1BatchError = class D1BatchError2 extends Error {
|
|
48033
|
+
name = "D1BatchError";
|
|
48034
|
+
d1Message;
|
|
48035
|
+
offset;
|
|
48036
|
+
constructor(d1Message, cause) {
|
|
48037
|
+
super(`Failed to execute batch: ${d1Message}`, { cause });
|
|
48038
|
+
this.d1Message = d1Message;
|
|
48039
|
+
this.offset = parseD1ErrorOffset(d1Message);
|
|
48040
|
+
}
|
|
48041
|
+
};
|
|
48042
|
+
D1MigrationError = class D1MigrationError2 extends Error {
|
|
48043
|
+
name = "D1MigrationError";
|
|
48044
|
+
tag;
|
|
48045
|
+
d1Message;
|
|
48046
|
+
offset;
|
|
48047
|
+
constructor(tag, d1Message, cause) {
|
|
48048
|
+
super(`Migration '${tag}' failed and rolled back: ${d1Message}`, { cause });
|
|
48049
|
+
this.tag = tag;
|
|
48050
|
+
this.d1Message = d1Message;
|
|
48051
|
+
this.offset = parseD1ErrorOffset(d1Message);
|
|
48052
|
+
}
|
|
48053
|
+
};
|
|
48054
|
+
});
|
|
47536
48055
|
var init_kv = () => {};
|
|
47537
48056
|
var RESERVED_SUBDOMAINS;
|
|
47538
48057
|
var init_hostname = __esm(() => {
|
|
@@ -47542,6 +48061,159 @@ var init_mime = () => {};
|
|
|
47542
48061
|
var init_assets = __esm(() => {
|
|
47543
48062
|
init_mime();
|
|
47544
48063
|
});
|
|
48064
|
+
function findOutOfOrderTags(orderedTags, applied) {
|
|
48065
|
+
let lastAppliedIndex = -1;
|
|
48066
|
+
orderedTags.forEach((tag, index2) => {
|
|
48067
|
+
if (applied.has(tag)) {
|
|
48068
|
+
lastAppliedIndex = index2;
|
|
48069
|
+
}
|
|
48070
|
+
});
|
|
48071
|
+
return orderedTags.filter((tag, index2) => index2 < lastAppliedIndex && !applied.has(tag));
|
|
48072
|
+
}
|
|
48073
|
+
function detectDestructiveStatements(statements) {
|
|
48074
|
+
return statements.filter((statement) => {
|
|
48075
|
+
const scannable = blankStringLiterals(normalizeSqlForChecksum(statement));
|
|
48076
|
+
return DESTRUCTIVE_SQL_PATTERNS.some((pattern) => pattern.test(scannable));
|
|
48077
|
+
});
|
|
48078
|
+
}
|
|
48079
|
+
function splitSqlStatements(sql4) {
|
|
48080
|
+
const statements = [];
|
|
48081
|
+
let current = "";
|
|
48082
|
+
let i2 = 0;
|
|
48083
|
+
while (i2 < sql4.length) {
|
|
48084
|
+
const char3 = sql4[i2];
|
|
48085
|
+
const next = sql4[i2 + 1];
|
|
48086
|
+
if (char3 === ";") {
|
|
48087
|
+
statements.push(current);
|
|
48088
|
+
current = "";
|
|
48089
|
+
i2++;
|
|
48090
|
+
} else if (char3 === "-" && next === "-") {
|
|
48091
|
+
const end = scanLineCommentEnd(sql4, i2);
|
|
48092
|
+
current += sql4.slice(i2, end);
|
|
48093
|
+
i2 = end;
|
|
48094
|
+
} else if (char3 === "/" && next === "*") {
|
|
48095
|
+
const end = scanBlockCommentEnd(sql4, i2);
|
|
48096
|
+
current += sql4.slice(i2, end);
|
|
48097
|
+
i2 = end;
|
|
48098
|
+
} else if (char3 === "'" || char3 === '"' || char3 === "`") {
|
|
48099
|
+
const end = scanQuoteEnd(sql4, i2, char3);
|
|
48100
|
+
current += sql4.slice(i2, end);
|
|
48101
|
+
i2 = end;
|
|
48102
|
+
} else if (char3 === "[") {
|
|
48103
|
+
const end = scanBracketEnd(sql4, i2);
|
|
48104
|
+
current += sql4.slice(i2, end);
|
|
48105
|
+
i2 = end;
|
|
48106
|
+
} else {
|
|
48107
|
+
current += char3;
|
|
48108
|
+
i2++;
|
|
48109
|
+
}
|
|
48110
|
+
}
|
|
48111
|
+
statements.push(current);
|
|
48112
|
+
return statements.map((statement) => statement.trim()).filter((statement) => normalizeSqlForChecksum(statement).trim() !== "");
|
|
48113
|
+
}
|
|
48114
|
+
function blankStringLiterals(sql4) {
|
|
48115
|
+
let output = "";
|
|
48116
|
+
let i2 = 0;
|
|
48117
|
+
while (i2 < sql4.length) {
|
|
48118
|
+
const char3 = sql4[i2];
|
|
48119
|
+
if (char3 === "'") {
|
|
48120
|
+
output += "''";
|
|
48121
|
+
i2 = scanQuoteEnd(sql4, i2, char3);
|
|
48122
|
+
} else if (char3 === '"' || char3 === "`") {
|
|
48123
|
+
const end = scanQuoteEnd(sql4, i2, char3);
|
|
48124
|
+
output += sql4.slice(i2, end);
|
|
48125
|
+
i2 = end;
|
|
48126
|
+
} else if (char3 === "[") {
|
|
48127
|
+
const end = scanBracketEnd(sql4, i2);
|
|
48128
|
+
output += sql4.slice(i2, end);
|
|
48129
|
+
i2 = end;
|
|
48130
|
+
} else {
|
|
48131
|
+
output += char3;
|
|
48132
|
+
i2++;
|
|
48133
|
+
}
|
|
48134
|
+
}
|
|
48135
|
+
return output;
|
|
48136
|
+
}
|
|
48137
|
+
function scanLineCommentEnd(sql4, start2) {
|
|
48138
|
+
let i2 = start2 + 2;
|
|
48139
|
+
while (i2 < sql4.length && sql4[i2] !== `
|
|
48140
|
+
`) {
|
|
48141
|
+
i2++;
|
|
48142
|
+
}
|
|
48143
|
+
return i2;
|
|
48144
|
+
}
|
|
48145
|
+
function scanBlockCommentEnd(sql4, start2) {
|
|
48146
|
+
let i2 = start2 + 2;
|
|
48147
|
+
while (i2 < sql4.length && !(sql4[i2] === "*" && sql4[i2 + 1] === "/")) {
|
|
48148
|
+
i2++;
|
|
48149
|
+
}
|
|
48150
|
+
return Math.min(i2 + 2, sql4.length);
|
|
48151
|
+
}
|
|
48152
|
+
function scanQuoteEnd(sql4, start2, quote) {
|
|
48153
|
+
let i2 = start2 + 1;
|
|
48154
|
+
while (i2 < sql4.length) {
|
|
48155
|
+
if (sql4[i2] !== quote) {
|
|
48156
|
+
i2++;
|
|
48157
|
+
} else if (sql4[i2 + 1] === quote) {
|
|
48158
|
+
i2 += 2;
|
|
48159
|
+
} else {
|
|
48160
|
+
return i2 + 1;
|
|
48161
|
+
}
|
|
48162
|
+
}
|
|
48163
|
+
return i2;
|
|
48164
|
+
}
|
|
48165
|
+
function scanBracketEnd(sql4, start2) {
|
|
48166
|
+
let i2 = start2 + 1;
|
|
48167
|
+
while (i2 < sql4.length) {
|
|
48168
|
+
if (sql4[i2] === "]") {
|
|
48169
|
+
return i2 + 1;
|
|
48170
|
+
}
|
|
48171
|
+
i2++;
|
|
48172
|
+
}
|
|
48173
|
+
return i2;
|
|
48174
|
+
}
|
|
48175
|
+
function isAlreadyExistsSqlError(message) {
|
|
48176
|
+
return /already exists|duplicate column/i.test(message);
|
|
48177
|
+
}
|
|
48178
|
+
function readIdentifier(groups) {
|
|
48179
|
+
return groups.find((group) => group !== undefined) ?? "";
|
|
48180
|
+
}
|
|
48181
|
+
function extractCreatedObjects(statements) {
|
|
48182
|
+
const tables = [];
|
|
48183
|
+
const columns2 = [];
|
|
48184
|
+
for (const statement of statements) {
|
|
48185
|
+
const scannable = blankStringLiterals(normalizeSqlForChecksum(statement));
|
|
48186
|
+
for (const match of scannable.matchAll(CREATE_TABLE_RE)) {
|
|
48187
|
+
const name2 = readIdentifier(match.slice(1, 5));
|
|
48188
|
+
if (name2 && !name2.startsWith("__new_")) {
|
|
48189
|
+
tables.push(name2);
|
|
48190
|
+
}
|
|
48191
|
+
}
|
|
48192
|
+
for (const match of scannable.matchAll(ADD_COLUMN_RE)) {
|
|
48193
|
+
const table8 = readIdentifier(match.slice(1, 5));
|
|
48194
|
+
const column2 = readIdentifier(match.slice(5, 9));
|
|
48195
|
+
if (table8 && column2 && !table8.startsWith("__new_")) {
|
|
48196
|
+
columns2.push({ table: table8, column: column2 });
|
|
48197
|
+
}
|
|
48198
|
+
}
|
|
48199
|
+
}
|
|
48200
|
+
return { tables, columns: columns2 };
|
|
48201
|
+
}
|
|
48202
|
+
var DESTRUCTIVE_SQL_PATTERNS;
|
|
48203
|
+
var IDENTIFIER_SOURCE;
|
|
48204
|
+
var CREATE_TABLE_RE;
|
|
48205
|
+
var ADD_COLUMN_RE;
|
|
48206
|
+
var init_sql3 = __esm(() => {
|
|
48207
|
+
init_schema3();
|
|
48208
|
+
DESTRUCTIVE_SQL_PATTERNS = [
|
|
48209
|
+
/\bDROP\s+TABLE\b/i,
|
|
48210
|
+
/\bALTER\s+TABLE\b[\s\S]*\bDROP\b/i,
|
|
48211
|
+
/\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?["'`[]?__new_/i
|
|
48212
|
+
];
|
|
48213
|
+
IDENTIFIER_SOURCE = String.raw`(?:"([^"]+)"|\`([^\`]+)\`|\[([^\]]+)\]|([A-Za-z_][\w$]*))`;
|
|
48214
|
+
CREATE_TABLE_RE = new RegExp(String.raw`\bCREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?${IDENTIFIER_SOURCE}`, "gi");
|
|
48215
|
+
ADD_COLUMN_RE = new RegExp(String.raw`\bALTER\s+TABLE\s+${IDENTIFIER_SOURCE}\s+ADD\s+(?:COLUMN\s+)?${IDENTIFIER_SOURCE}`, "gi");
|
|
48216
|
+
});
|
|
47545
48217
|
var require_sbmh = __commonJS2((exports, module2) => {
|
|
47546
48218
|
var { EventEmitter } = __require2("node:events");
|
|
47547
48219
|
var { inherits } = __require2("node:util");
|
|
@@ -49444,6 +50116,8 @@ var init_multipart = __esm(() => {
|
|
|
49444
50116
|
var init_utils5 = __esm(() => {
|
|
49445
50117
|
init_hostname();
|
|
49446
50118
|
init_assets();
|
|
50119
|
+
init_schema3();
|
|
50120
|
+
init_sql3();
|
|
49447
50121
|
init_multipart();
|
|
49448
50122
|
});
|
|
49449
50123
|
var init_r2 = __esm(() => {
|
|
@@ -49584,6 +50258,12 @@ var init_client = __esm(() => {
|
|
|
49584
50258
|
var init_core = __esm(() => {
|
|
49585
50259
|
init_client();
|
|
49586
50260
|
});
|
|
50261
|
+
var init_src4 = __esm(() => {
|
|
50262
|
+
init_core();
|
|
50263
|
+
init_namespaces();
|
|
50264
|
+
init_utils5();
|
|
50265
|
+
init_utils5();
|
|
50266
|
+
});
|
|
49587
50267
|
var CUSTOM_DOMAINS_KV_NAME = "cademy-custom-domains";
|
|
49588
50268
|
var QUEUE_NAME_PREFIX = "playcademy";
|
|
49589
50269
|
var GAME_WORKER_DOMAIN_PRODUCTION;
|
|
@@ -52074,43 +52754,316 @@ var matchedPorts;
|
|
|
52074
52754
|
var init_tunnel = __esm(() => {
|
|
52075
52755
|
matchedPorts = new Map;
|
|
52076
52756
|
});
|
|
52077
|
-
function
|
|
52078
|
-
|
|
52757
|
+
function assertBaselineClaimValid(args2) {
|
|
52758
|
+
const validation = validateBaselineClaim(args2);
|
|
52759
|
+
const blocked = validation.contradictions.length > 0 || validation.unverified.length > 0 && !args2.allowUnverified;
|
|
52760
|
+
if (blocked) {
|
|
52761
|
+
addEvent("deployment_state.baseline_claim_rejected", {
|
|
52762
|
+
"app.game.id": args2.gameId,
|
|
52763
|
+
"app.deployment_state.baseline_claim_source": args2.source,
|
|
52764
|
+
"app.deployment_state.baseline_contradictions": validation.contradictions.length,
|
|
52765
|
+
"app.deployment_state.baseline_unverified": validation.unverified.length,
|
|
52766
|
+
"app.deployment_state.baseline_suggested_tag": validation.suggestedTag ?? "none"
|
|
52767
|
+
});
|
|
52768
|
+
throw new BaselineClaimRejectedError({
|
|
52769
|
+
contradictions: validation.contradictions,
|
|
52770
|
+
unverified: validation.unverified,
|
|
52771
|
+
suggestedTag: validation.suggestedTag,
|
|
52772
|
+
claimedTag: args2.claimedTag
|
|
52773
|
+
});
|
|
52774
|
+
}
|
|
52775
|
+
if (validation.unverified.length > 0) {
|
|
52776
|
+
addEvent("deployment_state.baseline_unverified_overridden", {
|
|
52777
|
+
"app.game.id": args2.gameId,
|
|
52778
|
+
"app.user.id": args2.userId,
|
|
52779
|
+
"app.deployment_state.baseline_overridden_tags": validation.unverified.map((entry2) => entry2.tag).join(",")
|
|
52780
|
+
});
|
|
52781
|
+
}
|
|
52782
|
+
}
|
|
52783
|
+
function validateBaselineClaim(args2) {
|
|
52784
|
+
const { claimedTag, evidence, tables, indexes: indexes2, views, lastDeployAt } = args2;
|
|
52785
|
+
const claimedIndex = evidence.findIndex((entry2) => entry2.tag === claimedTag);
|
|
52786
|
+
const verdicts = [];
|
|
52787
|
+
const unverified = [];
|
|
52788
|
+
evidence.forEach((entry2, index2) => {
|
|
52789
|
+
if (claimedIndex === -1 || index2 > claimedIndex) {
|
|
52790
|
+
verdicts.push(judgeBeyond(entry2, tables, indexes2, views));
|
|
52791
|
+
return;
|
|
52792
|
+
}
|
|
52793
|
+
const judged = judgeClaimed(entry2, tables, lastDeployAt);
|
|
52794
|
+
verdicts.push(judged.verdict);
|
|
52795
|
+
if (judged.unverifiable) {
|
|
52796
|
+
unverified.push(judged.verdict);
|
|
52797
|
+
}
|
|
52798
|
+
});
|
|
52799
|
+
return {
|
|
52800
|
+
verdicts,
|
|
52801
|
+
contradictions: verdicts.filter((verdict) => verdict.verdict === "contradicted"),
|
|
52802
|
+
unverified,
|
|
52803
|
+
suggestedTag: suggestTag(evidence, tables)
|
|
52804
|
+
};
|
|
52079
52805
|
}
|
|
52080
|
-
|
|
52081
|
-
|
|
52082
|
-
|
|
52806
|
+
function judgeClaimed(entry2, tables, lastDeployAt) {
|
|
52807
|
+
if (!hasSignal(entry2)) {
|
|
52808
|
+
const generated = new Date(entry2.generatedAt);
|
|
52809
|
+
if (lastDeployAt && generated > lastDeployAt) {
|
|
52810
|
+
return {
|
|
52811
|
+
verdict: {
|
|
52812
|
+
tag: entry2.tag,
|
|
52813
|
+
verdict: "no-signal",
|
|
52814
|
+
detail: `no schema footprint to verify, and it was generated after the last deploy (${lastDeployAt.toISOString()})`
|
|
52815
|
+
},
|
|
52816
|
+
unverifiable: true
|
|
52817
|
+
};
|
|
52818
|
+
}
|
|
52819
|
+
return { verdict: { tag: entry2.tag, verdict: "no-signal" }, unverifiable: false };
|
|
52820
|
+
}
|
|
52821
|
+
for (const table8 of entry2.createsTables) {
|
|
52822
|
+
const columns2 = tables.get(table8.name);
|
|
52823
|
+
if (!columns2) {
|
|
52824
|
+
return {
|
|
52825
|
+
verdict: {
|
|
52826
|
+
tag: entry2.tag,
|
|
52827
|
+
verdict: "contradicted",
|
|
52828
|
+
detail: `creates table \`${table8.name}\`, which is not in the live database`
|
|
52829
|
+
},
|
|
52830
|
+
unverifiable: false
|
|
52831
|
+
};
|
|
52832
|
+
}
|
|
52833
|
+
const missing = table8.columns.filter((column2) => !columns2.includes(column2));
|
|
52834
|
+
if (missing.length > 0) {
|
|
52835
|
+
return {
|
|
52836
|
+
verdict: {
|
|
52837
|
+
tag: entry2.tag,
|
|
52838
|
+
verdict: "contradicted",
|
|
52839
|
+
detail: `table \`${table8.name}\` exists but is missing column(s) ${missing.map((column2) => `\`${column2}\``).join(", ")} this migration defines`
|
|
52840
|
+
},
|
|
52841
|
+
unverifiable: false
|
|
52842
|
+
};
|
|
52843
|
+
}
|
|
52844
|
+
}
|
|
52845
|
+
for (const added of entry2.addsColumns) {
|
|
52846
|
+
const columns2 = tables.get(added.table);
|
|
52847
|
+
if (columns2 && !columns2.includes(added.column)) {
|
|
52848
|
+
return {
|
|
52849
|
+
verdict: {
|
|
52850
|
+
tag: entry2.tag,
|
|
52851
|
+
verdict: "contradicted",
|
|
52852
|
+
detail: `adds column \`${added.column}\` to \`${added.table}\`, which the live table does not have`
|
|
52853
|
+
},
|
|
52854
|
+
unverifiable: false
|
|
52855
|
+
};
|
|
52856
|
+
}
|
|
52857
|
+
}
|
|
52858
|
+
return { verdict: { tag: entry2.tag, verdict: "verified" }, unverifiable: false };
|
|
52859
|
+
}
|
|
52860
|
+
function judgeBeyond(entry2, tables, indexes2, views) {
|
|
52861
|
+
for (const table8 of entry2.createsTables) {
|
|
52862
|
+
if (tables.has(table8.name)) {
|
|
52863
|
+
return {
|
|
52864
|
+
tag: entry2.tag,
|
|
52865
|
+
verdict: "contradicted",
|
|
52866
|
+
detail: `is beyond the claim, but the table it creates (\`${table8.name}\`) already exists in the live database — the claim looks too old`
|
|
52867
|
+
};
|
|
52868
|
+
}
|
|
52869
|
+
}
|
|
52870
|
+
for (const added of entry2.addsColumns) {
|
|
52871
|
+
const columns2 = tables.get(added.table);
|
|
52872
|
+
if (columns2?.includes(added.column)) {
|
|
52873
|
+
return {
|
|
52874
|
+
tag: entry2.tag,
|
|
52875
|
+
verdict: "contradicted",
|
|
52876
|
+
detail: `is beyond the claim, but the column it adds (\`${added.table}.${added.column}\`) already exists — the claim looks too old`
|
|
52877
|
+
};
|
|
52878
|
+
}
|
|
52879
|
+
}
|
|
52880
|
+
for (const index2 of entry2.createsIndexes) {
|
|
52881
|
+
if (indexes2.has(index2)) {
|
|
52882
|
+
return {
|
|
52883
|
+
tag: entry2.tag,
|
|
52884
|
+
verdict: "contradicted",
|
|
52885
|
+
detail: `is beyond the claim, but the index it creates (\`${index2}\`) already exists — the claim looks too old`
|
|
52886
|
+
};
|
|
52887
|
+
}
|
|
52888
|
+
}
|
|
52889
|
+
for (const view2 of entry2.createsViews) {
|
|
52890
|
+
if (views.has(view2)) {
|
|
52891
|
+
return {
|
|
52892
|
+
tag: entry2.tag,
|
|
52893
|
+
verdict: "contradicted",
|
|
52894
|
+
detail: `is beyond the claim, but the view it creates (\`${view2}\`) already exists — the claim looks too old`
|
|
52895
|
+
};
|
|
52896
|
+
}
|
|
52897
|
+
}
|
|
52898
|
+
return { tag: entry2.tag, verdict: "verified" };
|
|
52899
|
+
}
|
|
52900
|
+
function suggestTag(evidence, tables) {
|
|
52901
|
+
let presentPrefix = 0;
|
|
52902
|
+
while (presentPrefix < evidence.length && evidence[presentPrefix].createsTables.every((table8) => tables.has(table8.name))) {
|
|
52903
|
+
presentPrefix++;
|
|
52904
|
+
}
|
|
52905
|
+
let absentFrom = evidence.length;
|
|
52906
|
+
while (absentFrom > 0 && evidence[absentFrom - 1].createsTables.every((table8) => !tables.has(table8.name))) {
|
|
52907
|
+
absentFrom--;
|
|
52908
|
+
}
|
|
52909
|
+
const candidate = presentPrefix - 1;
|
|
52910
|
+
return candidate >= 0 && candidate >= absentFrom - 1 ? evidence[candidate].tag : null;
|
|
52911
|
+
}
|
|
52912
|
+
function hasSignal(entry2) {
|
|
52913
|
+
return entry2.createsTables.length > 0 || entry2.addsColumns.length > 0 || entry2.createsIndexes.length > 0 || entry2.createsViews.length > 0;
|
|
52914
|
+
}
|
|
52915
|
+
var init_baseline_validation_util = __esm(() => {
|
|
52916
|
+
init_spans();
|
|
52917
|
+
init_errors();
|
|
52083
52918
|
});
|
|
52084
|
-
function
|
|
52085
|
-
|
|
52086
|
-
|
|
52919
|
+
function sliceJournalToTag(journal, lastAppliedMigrationTag) {
|
|
52920
|
+
const index2 = journal.findIndex((entry2) => entry2.tag === lastAppliedMigrationTag);
|
|
52921
|
+
return index2 === -1 ? null : journal.slice(0, index2 + 1);
|
|
52922
|
+
}
|
|
52923
|
+
function evaluateBaselineGuardrails(input) {
|
|
52924
|
+
if (input.ledgerTags.length > 0) {
|
|
52925
|
+
return "ledger-not-empty";
|
|
52087
52926
|
}
|
|
52088
|
-
if (
|
|
52089
|
-
return
|
|
52927
|
+
if (input.liveTables.length === 0) {
|
|
52928
|
+
return "database-empty";
|
|
52090
52929
|
}
|
|
52091
|
-
return
|
|
52930
|
+
return "ok";
|
|
52092
52931
|
}
|
|
52093
|
-
function
|
|
52094
|
-
|
|
52932
|
+
function planMigrations(journal, ledger) {
|
|
52933
|
+
const ledgerByTag = new Map(ledger.map((row) => [row.tag, row]));
|
|
52934
|
+
const journalTags = new Set(journal.map((entry2) => entry2.tag));
|
|
52935
|
+
const pendingTags = [];
|
|
52936
|
+
const checksumMismatches = [];
|
|
52937
|
+
for (const entry2 of journal) {
|
|
52938
|
+
const applied = ledgerByTag.get(entry2.tag);
|
|
52939
|
+
if (!applied) {
|
|
52940
|
+
pendingTags.push(entry2.tag);
|
|
52941
|
+
} else {
|
|
52942
|
+
const comparable = applied.checksumAlgo === MIGRATION_CHECKSUM_ALGO;
|
|
52943
|
+
if (comparable && applied.checksum !== entry2.checksum) {
|
|
52944
|
+
checksumMismatches.push({
|
|
52945
|
+
tag: entry2.tag,
|
|
52946
|
+
ledgerChecksum: applied.checksum,
|
|
52947
|
+
journalChecksum: entry2.checksum
|
|
52948
|
+
});
|
|
52949
|
+
}
|
|
52950
|
+
}
|
|
52951
|
+
}
|
|
52952
|
+
const outOfOrderTags = findOutOfOrderTags(journal.map((entry2) => entry2.tag), new Set(ledgerByTag.keys()));
|
|
52953
|
+
const missingFromJournalTags = ledger.filter((row) => !journalTags.has(row.tag)).map((row) => row.tag);
|
|
52954
|
+
return { pendingTags, checksumMismatches, outOfOrderTags, missingFromJournalTags };
|
|
52095
52955
|
}
|
|
52096
|
-
function
|
|
52097
|
-
|
|
52956
|
+
function assessMigrationAlreadyApplied(statements, liveTables) {
|
|
52957
|
+
const created = extractCreatedObjects(statements);
|
|
52958
|
+
const present = [];
|
|
52959
|
+
const missing = [];
|
|
52960
|
+
for (const table8 of created.tables) {
|
|
52961
|
+
(liveTables.has(table8) ? present : missing).push(`table ${table8}`);
|
|
52962
|
+
}
|
|
52963
|
+
for (const added of created.columns) {
|
|
52964
|
+
const live = Boolean(liveTables.get(added.table)?.includes(added.column));
|
|
52965
|
+
(live ? present : missing).push(`column ${added.table}.${added.column}`);
|
|
52966
|
+
}
|
|
52967
|
+
if (present.length === 0) {
|
|
52968
|
+
return { verdict: "no-signal", present, missing };
|
|
52969
|
+
}
|
|
52970
|
+
return {
|
|
52971
|
+
verdict: missing.length === 0 ? "all-present" : "partial",
|
|
52972
|
+
present,
|
|
52973
|
+
missing
|
|
52974
|
+
};
|
|
52098
52975
|
}
|
|
52099
|
-
function
|
|
52100
|
-
|
|
52976
|
+
function parseMigrationFailure(events) {
|
|
52977
|
+
if (!events) {
|
|
52978
|
+
return null;
|
|
52979
|
+
}
|
|
52980
|
+
for (let i2 = events.length - 1;i2 >= 0; i2--) {
|
|
52981
|
+
const event = events[i2];
|
|
52982
|
+
const failure = event ? parseFailureEvent(event) : null;
|
|
52983
|
+
if (failure) {
|
|
52984
|
+
return failure;
|
|
52985
|
+
}
|
|
52986
|
+
}
|
|
52987
|
+
return null;
|
|
52101
52988
|
}
|
|
52102
|
-
function
|
|
52103
|
-
|
|
52989
|
+
function parseFailureEvent(event) {
|
|
52990
|
+
const details = event.details;
|
|
52991
|
+
if (!details) {
|
|
52992
|
+
return null;
|
|
52993
|
+
}
|
|
52994
|
+
const { code, tag, statementIndex, error, d1Message } = details;
|
|
52995
|
+
if (code !== DEPLOY_ERROR_CODES.migrationFailed || typeof tag !== "string") {
|
|
52996
|
+
return null;
|
|
52997
|
+
}
|
|
52998
|
+
return {
|
|
52999
|
+
tag,
|
|
53000
|
+
error: failureMessage(error, d1Message),
|
|
53001
|
+
statementIndex: typeof statementIndex === "number" ? statementIndex : null,
|
|
53002
|
+
at: event.createdAt
|
|
53003
|
+
};
|
|
52104
53004
|
}
|
|
52105
|
-
function
|
|
52106
|
-
|
|
53005
|
+
function failureMessage(error, d1Message) {
|
|
53006
|
+
if (typeof error === "string") {
|
|
53007
|
+
return error;
|
|
53008
|
+
}
|
|
53009
|
+
if (typeof d1Message === "string") {
|
|
53010
|
+
return d1Message;
|
|
53011
|
+
}
|
|
53012
|
+
return "Migration failed";
|
|
52107
53013
|
}
|
|
52108
|
-
var
|
|
52109
|
-
|
|
52110
|
-
|
|
52111
|
-
init_src();
|
|
52112
|
-
init_stages();
|
|
53014
|
+
var init_migration_util = __esm(() => {
|
|
53015
|
+
init_src4();
|
|
53016
|
+
init_errors();
|
|
52113
53017
|
});
|
|
53018
|
+
async function deriveSecretsManifestPepper(platformSecret) {
|
|
53019
|
+
const encoder = new TextEncoder;
|
|
53020
|
+
const keyMaterial = await crypto.subtle.importKey("raw", encoder.encode(platformSecret), "HKDF", false, ["deriveBits"]);
|
|
53021
|
+
const bits = await crypto.subtle.deriveBits({
|
|
53022
|
+
name: "HKDF",
|
|
53023
|
+
hash: "SHA-256",
|
|
53024
|
+
salt: new Uint8Array(32),
|
|
53025
|
+
info: encoder.encode(SECRETS_MANIFEST_HKDF_INFO)
|
|
53026
|
+
}, keyMaterial, 256);
|
|
53027
|
+
return new Uint8Array(bits);
|
|
53028
|
+
}
|
|
53029
|
+
async function computeSecretDigest(pepper, input) {
|
|
53030
|
+
const key = await crypto.subtle.importKey("raw", new Uint8Array(pepper), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
53031
|
+
const message = JSON.stringify([input.gameId, input.key, input.value]);
|
|
53032
|
+
const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(message));
|
|
53033
|
+
return [...new Uint8Array(signature)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
53034
|
+
}
|
|
53035
|
+
function computeSecretsDiff(input) {
|
|
53036
|
+
const { localDigests, manifest, remoteKeys } = input;
|
|
53037
|
+
const added = [];
|
|
53038
|
+
const changed = [];
|
|
53039
|
+
const unchanged = [];
|
|
53040
|
+
for (const [key, digest] of Object.entries(localDigests)) {
|
|
53041
|
+
const recorded = manifest[key];
|
|
53042
|
+
if (recorded === undefined) {
|
|
53043
|
+
added.push(key);
|
|
53044
|
+
} else if (recorded === digest) {
|
|
53045
|
+
unchanged.push(key);
|
|
53046
|
+
} else {
|
|
53047
|
+
changed.push(key);
|
|
53048
|
+
}
|
|
53049
|
+
}
|
|
53050
|
+
const localKeys = new Set(Object.keys(localDigests));
|
|
53051
|
+
const managedKeys = new Set(Object.keys(manifest));
|
|
53052
|
+
const remoteOnlyManaged = [...managedKeys].filter((key) => !localKeys.has(key));
|
|
53053
|
+
const remoteOnlyUnmanaged = remoteKeys.filter((key) => !managedKeys.has(key) && !localKeys.has(key));
|
|
53054
|
+
return {
|
|
53055
|
+
added: added.toSorted(),
|
|
53056
|
+
changed: changed.toSorted(),
|
|
53057
|
+
unchanged: unchanged.toSorted(),
|
|
53058
|
+
remoteOnlyManaged: remoteOnlyManaged.toSorted(),
|
|
53059
|
+
remoteOnlyUnmanaged: remoteOnlyUnmanaged.toSorted()
|
|
53060
|
+
};
|
|
53061
|
+
}
|
|
53062
|
+
function findUnmanagedPruneKeys(pruneSecrets, manifest) {
|
|
53063
|
+
const managed = new Set(Object.keys(manifest ?? {}));
|
|
53064
|
+
return pruneSecrets.filter((key) => !managed.has(key));
|
|
53065
|
+
}
|
|
53066
|
+
var SECRETS_MANIFEST_HKDF_INFO = "playcademy:secrets-manifest:v1";
|
|
52114
53067
|
async function sweepDashboardWorkerKeys(deleteApiKeysByName, slug) {
|
|
52115
53068
|
try {
|
|
52116
53069
|
await deleteApiKeysByName(getDashboardWorkerApiKeyName(slug));
|
|
@@ -52125,6 +53078,9 @@ var init_worker_keys_util = __esm(() => {
|
|
|
52125
53078
|
init_spans();
|
|
52126
53079
|
init_deployment_util();
|
|
52127
53080
|
});
|
|
53081
|
+
function hasBinding(binding) {
|
|
53082
|
+
return binding === true || Array.isArray(binding) && binding.length > 0;
|
|
53083
|
+
}
|
|
52128
53084
|
function readDashboardTheme(config2) {
|
|
52129
53085
|
const theme = config2?.dashboard?.theme;
|
|
52130
53086
|
return {
|
|
@@ -52192,7 +53148,7 @@ class DeployService {
|
|
|
52192
53148
|
data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
|
|
52193
53149
|
};
|
|
52194
53150
|
const keepAssets = hasBackend && !hasFrontend;
|
|
52195
|
-
const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings
|
|
53151
|
+
const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings);
|
|
52196
53152
|
const bindings = deploymentOptions?.bindings;
|
|
52197
53153
|
setAttributes({
|
|
52198
53154
|
"app.deploy.has_d1": Boolean(bindings?.d1?.length),
|
|
@@ -52201,13 +53157,49 @@ class DeployService {
|
|
|
52201
53157
|
"app.deploy.queue_count": bindings?.queues?.length ?? 0,
|
|
52202
53158
|
"app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
|
|
52203
53159
|
});
|
|
52204
|
-
const activeDeployment = await
|
|
52205
|
-
|
|
52206
|
-
|
|
52207
|
-
|
|
52208
|
-
|
|
53160
|
+
const [activeDeployment, deploymentState] = await Promise.all([
|
|
53161
|
+
db2.query.gameDeployments.findFirst({
|
|
53162
|
+
where: activeDeploymentWhere(game2.id, "game"),
|
|
53163
|
+
columns: { resources: true }
|
|
53164
|
+
}),
|
|
53165
|
+
db2.query.gameDeploymentState.findFirst({
|
|
53166
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
53167
|
+
})
|
|
53168
|
+
]);
|
|
53169
|
+
if (request.pruneSecrets?.length) {
|
|
53170
|
+
const unmanaged = findUnmanagedPruneKeys(request.pruneSecrets, deploymentState?.secretsManifest);
|
|
53171
|
+
if (unmanaged.length > 0) {
|
|
53172
|
+
throw new SecretsPruneUnmanagedError(unmanaged);
|
|
53173
|
+
}
|
|
53174
|
+
}
|
|
53175
|
+
let state = deploymentState;
|
|
53176
|
+
if (request.baseline) {
|
|
53177
|
+
if (isSchemaAdopted(state)) {
|
|
53178
|
+
addEvent("deploy.baseline_skipped", {
|
|
53179
|
+
"app.deploy.baseline_source": state?.baselineSource ?? "unknown"
|
|
53180
|
+
});
|
|
53181
|
+
} else {
|
|
53182
|
+
state = yield* this.adoptClientBaseline({
|
|
53183
|
+
game: game2,
|
|
53184
|
+
request,
|
|
53185
|
+
user,
|
|
53186
|
+
deploymentId,
|
|
53187
|
+
baseline: request.baseline,
|
|
53188
|
+
existingResources: activeDeployment?.resources ?? undefined
|
|
53189
|
+
});
|
|
53190
|
+
}
|
|
53191
|
+
}
|
|
53192
|
+
if (deploymentOptions?.bindings?.d1?.length && !isSchemaAdopted(state)) {
|
|
52209
53193
|
await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
|
|
52210
53194
|
}
|
|
53195
|
+
const databaseOutcome = yield* this.executeDatabaseStep({
|
|
53196
|
+
game: game2,
|
|
53197
|
+
request,
|
|
53198
|
+
user,
|
|
53199
|
+
deploymentId,
|
|
53200
|
+
state,
|
|
53201
|
+
existingResources: activeDeployment?.resources ?? undefined
|
|
53202
|
+
});
|
|
52211
53203
|
const result = await this.deployToCloudflare({
|
|
52212
53204
|
deploymentId,
|
|
52213
53205
|
code: request.code,
|
|
@@ -52215,6 +53207,7 @@ class DeployService {
|
|
|
52215
53207
|
tempDir,
|
|
52216
53208
|
options: {
|
|
52217
53209
|
...deploymentOptions,
|
|
53210
|
+
...databaseOutcome.legacySchema && { schema: databaseOutcome.legacySchema },
|
|
52218
53211
|
compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
|
|
52219
53212
|
compatibilityFlags: request.compatibilityFlags,
|
|
52220
53213
|
existingResources: activeDeployment?.resources ?? undefined,
|
|
@@ -52230,8 +53223,12 @@ class DeployService {
|
|
|
52230
53223
|
result,
|
|
52231
53224
|
request,
|
|
52232
53225
|
user,
|
|
52233
|
-
flags: flags2
|
|
53226
|
+
flags: flags2,
|
|
53227
|
+
database: databaseOutcome
|
|
52234
53228
|
});
|
|
53229
|
+
if (request.pruneSecrets?.length) {
|
|
53230
|
+
yield* this.pruneManagedSecretsStep(game2.id, result.deploymentId, request.pruneSecrets);
|
|
53231
|
+
}
|
|
52235
53232
|
yield { type: "complete", data: updatedGame };
|
|
52236
53233
|
}
|
|
52237
53234
|
async* deployDashboard(context2) {
|
|
@@ -52358,7 +53355,10 @@ class DeployService {
|
|
|
52358
53355
|
"app.deploy.code_size": request.code?.length ?? 0,
|
|
52359
53356
|
"app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
|
|
52360
53357
|
"app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
|
|
52361
|
-
"app.deploy.has_schema": Boolean(request.schema)
|
|
53358
|
+
"app.deploy.has_schema": Boolean(request.schema),
|
|
53359
|
+
"app.deploy.has_database_payload": Boolean(request.database),
|
|
53360
|
+
"app.deploy.has_baseline": Boolean(request.baseline),
|
|
53361
|
+
"app.deploy.prune_secret_count": request.pruneSecrets?.length ?? 0
|
|
52362
53362
|
});
|
|
52363
53363
|
if (!hasBackend && !hasFrontend && !hasMetadata) {
|
|
52364
53364
|
throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
|
|
@@ -52395,18 +53395,18 @@ class DeployService {
|
|
|
52395
53395
|
}
|
|
52396
53396
|
return "metadata_only";
|
|
52397
53397
|
}
|
|
52398
|
-
mapGameBindingsToOptions(deploymentId, bindings
|
|
52399
|
-
if (!bindings
|
|
53398
|
+
mapGameBindingsToOptions(deploymentId, bindings) {
|
|
53399
|
+
if (!bindings) {
|
|
52400
53400
|
return;
|
|
52401
53401
|
}
|
|
52402
53402
|
const workerBindings = {};
|
|
52403
|
-
if (bindings
|
|
53403
|
+
if (hasBinding(bindings.database)) {
|
|
52404
53404
|
workerBindings.d1 = [deploymentId];
|
|
52405
53405
|
}
|
|
52406
|
-
if (bindings
|
|
53406
|
+
if (hasBinding(bindings.keyValue)) {
|
|
52407
53407
|
workerBindings.kv = [deploymentId];
|
|
52408
53408
|
}
|
|
52409
|
-
if (bindings
|
|
53409
|
+
if (hasBinding(bindings.bucket)) {
|
|
52410
53410
|
workerBindings.r2 = [deploymentId];
|
|
52411
53411
|
}
|
|
52412
53412
|
if (bindings?.queues) {
|
|
@@ -52435,10 +53435,7 @@ class DeployService {
|
|
|
52435
53435
|
});
|
|
52436
53436
|
}
|
|
52437
53437
|
const hasBindings = workerBindings.d1?.length || workerBindings.kv?.length || workerBindings.r2?.length || workerBindings.queues?.length;
|
|
52438
|
-
return {
|
|
52439
|
-
...hasBindings && { bindings: workerBindings },
|
|
52440
|
-
...schema2 && { schema: schema2 }
|
|
52441
|
-
};
|
|
53438
|
+
return hasBindings ? { bindings: workerBindings } : undefined;
|
|
52442
53439
|
}
|
|
52443
53440
|
static countDeadLetterQueues(bindings) {
|
|
52444
53441
|
return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
|
|
@@ -52462,6 +53459,473 @@ class DeployService {
|
|
|
52462
53459
|
}
|
|
52463
53460
|
}
|
|
52464
53461
|
}
|
|
53462
|
+
async* executeDatabaseStep(context2) {
|
|
53463
|
+
const { game: game2, request, user, deploymentId, state } = context2;
|
|
53464
|
+
if (request.schema) {
|
|
53465
|
+
if (isSchemaAdopted(state)) {
|
|
53466
|
+
throw new LegacySchemaUpgradeRequiredError;
|
|
53467
|
+
}
|
|
53468
|
+
const legacyDbId = context2.existingResources?.d1?.find((db2) => db2.name === deploymentId)?.id;
|
|
53469
|
+
if (legacyDbId) {
|
|
53470
|
+
const ledger = await this.getCloudflare().d1.readMigrationLedger(legacyDbId);
|
|
53471
|
+
if (ledger.length > 0) {
|
|
53472
|
+
throw new LegacySchemaUpgradeRequiredError;
|
|
53473
|
+
}
|
|
53474
|
+
}
|
|
53475
|
+
setAttribute("app.deploy.db_mode", "legacy");
|
|
53476
|
+
return { ...NO_DATABASE_WORK, legacySchema: request.schema };
|
|
53477
|
+
}
|
|
53478
|
+
const database = request.database;
|
|
53479
|
+
if (!database) {
|
|
53480
|
+
return NO_DATABASE_WORK;
|
|
53481
|
+
}
|
|
53482
|
+
if (!hasBinding(request.bindings?.database)) {
|
|
53483
|
+
throw new ValidationError("The database payload requires a database binding");
|
|
53484
|
+
}
|
|
53485
|
+
if (!request.deployId) {
|
|
53486
|
+
throw new ValidationError("deployId is required when a database payload is present");
|
|
53487
|
+
}
|
|
53488
|
+
setAttribute("app.deploy.db_mode", database.mode);
|
|
53489
|
+
const cf = this.getCloudflare();
|
|
53490
|
+
const persistedId = context2.existingResources?.d1?.find((db2) => db2.name === deploymentId)?.id;
|
|
53491
|
+
const databaseId = persistedId ?? await cf.d1.create(deploymentId);
|
|
53492
|
+
if (database.mode === "migrate") {
|
|
53493
|
+
return yield* this.runMigrateMode({
|
|
53494
|
+
game: game2,
|
|
53495
|
+
user,
|
|
53496
|
+
deployId: request.deployId,
|
|
53497
|
+
databaseId,
|
|
53498
|
+
migrations: database.migrations,
|
|
53499
|
+
state
|
|
53500
|
+
});
|
|
53501
|
+
}
|
|
53502
|
+
return yield* this.runPushMode({ game: game2, databaseId, payload: database, state });
|
|
53503
|
+
}
|
|
53504
|
+
async* runMigrateMode(args2) {
|
|
53505
|
+
const { game: game2, user, deployId, databaseId, migrations, state } = args2;
|
|
53506
|
+
const cf = this.getCloudflare();
|
|
53507
|
+
yield { type: "status", data: { message: "Preparing database migrations" } };
|
|
53508
|
+
await this.assertNoSchemaDrift(databaseId, state);
|
|
53509
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
53510
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
53511
|
+
const plan = planMigrations(migrations.map((migration) => ({ tag: migration.tag, checksum: migration.checksum })), ledger.map((row) => ({
|
|
53512
|
+
tag: row.tag,
|
|
53513
|
+
checksum: row.checksum,
|
|
53514
|
+
checksumAlgo: row.checksum_algo
|
|
53515
|
+
})));
|
|
53516
|
+
setAttributes({
|
|
53517
|
+
"app.deploy.migrations_total": migrations.length,
|
|
53518
|
+
"app.deploy.migrations_applied_before": ledger.length,
|
|
53519
|
+
"app.deploy.migrations_pending": plan.pendingTags.length
|
|
53520
|
+
});
|
|
53521
|
+
if (plan.checksumMismatches.length > 0) {
|
|
53522
|
+
throw new MigrationChecksumMismatchError(plan.checksumMismatches);
|
|
53523
|
+
}
|
|
53524
|
+
if (plan.missingFromJournalTags.length > 0) {
|
|
53525
|
+
throw new MigrationJournalDivergenceError(plan.missingFromJournalTags);
|
|
53526
|
+
}
|
|
53527
|
+
if (plan.outOfOrderTags.length > 0) {
|
|
53528
|
+
throw new MigrationOrderError(plan.outOfOrderTags);
|
|
53529
|
+
}
|
|
53530
|
+
if (plan.pendingTags.length === 0) {
|
|
53531
|
+
yield { type: "status", data: { message: "Database schema is up to date" } };
|
|
53532
|
+
if (ledger.length > 0 && !state?.schemaFingerprint) {
|
|
53533
|
+
const fingerprint2 = await cf.d1.fingerprintSchema(databaseId);
|
|
53534
|
+
await this.persistDeploymentState(game2.id, {
|
|
53535
|
+
schemaFingerprint: fingerprint2.fingerprint
|
|
53536
|
+
});
|
|
53537
|
+
return {
|
|
53538
|
+
...NO_DATABASE_WORK,
|
|
53539
|
+
schemaFingerprint: fingerprint2.fingerprint
|
|
53540
|
+
};
|
|
53541
|
+
}
|
|
53542
|
+
return {
|
|
53543
|
+
...NO_DATABASE_WORK,
|
|
53544
|
+
schemaFingerprint: state?.schemaFingerprint ?? null
|
|
53545
|
+
};
|
|
53546
|
+
}
|
|
53547
|
+
const capture = yield* this.captureBookmarkStep(databaseId);
|
|
53548
|
+
const migrationsByTag = new Map(migrations.map((migration) => [migration.tag, migration]));
|
|
53549
|
+
let appliedCount = 0;
|
|
53550
|
+
for (const tag of plan.pendingTags) {
|
|
53551
|
+
const migration = migrationsByTag.get(tag);
|
|
53552
|
+
const count = migration.statements.length;
|
|
53553
|
+
yield {
|
|
53554
|
+
type: "status",
|
|
53555
|
+
data: { message: `Applying ${tag} (${count} statement${count === 1 ? "" : "s"})` }
|
|
53556
|
+
};
|
|
53557
|
+
const startedAt = Date.now();
|
|
53558
|
+
try {
|
|
53559
|
+
await withSpan("deploy.apply_migration", () => cf.d1.applyMigration(databaseId, {
|
|
53560
|
+
tag,
|
|
53561
|
+
statements: migration.statements,
|
|
53562
|
+
checksum: migration.checksum,
|
|
53563
|
+
deployId,
|
|
53564
|
+
appliedBy: user.id
|
|
53565
|
+
}));
|
|
53566
|
+
} catch (error) {
|
|
53567
|
+
if (appliedCount > 0) {
|
|
53568
|
+
await this.recordAppliedPrefixFingerprint(game2.id, databaseId);
|
|
53569
|
+
}
|
|
53570
|
+
throw await this.toMigrationStepError(databaseId, error, migration);
|
|
53571
|
+
}
|
|
53572
|
+
appliedCount++;
|
|
53573
|
+
const seconds = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
53574
|
+
yield { type: "status", data: { message: `Applied ${tag} (${seconds}s)` } };
|
|
53575
|
+
}
|
|
53576
|
+
setAttribute("app.deploy.migrations_applied", plan.pendingTags.length);
|
|
53577
|
+
const fingerprint = await cf.d1.fingerprintSchema(databaseId);
|
|
53578
|
+
await this.persistDeploymentState(game2.id, {
|
|
53579
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
53580
|
+
schemaHash: null,
|
|
53581
|
+
schemaSnapshot: null
|
|
53582
|
+
});
|
|
53583
|
+
return {
|
|
53584
|
+
schemaHash: null,
|
|
53585
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
53586
|
+
timeTravelBookmark: capture?.bookmark ?? null,
|
|
53587
|
+
bookmarkCapturedAt: capture?.capturedAt ?? null
|
|
53588
|
+
};
|
|
53589
|
+
}
|
|
53590
|
+
async* runPushMode(args2) {
|
|
53591
|
+
const { game: game2, databaseId, payload, state } = args2;
|
|
53592
|
+
const cf = this.getCloudflare();
|
|
53593
|
+
yield { type: "status", data: { message: "Verifying database schema state" } };
|
|
53594
|
+
if (typeof payload.baselineHash !== "string" && payload.baselineHash !== null) {
|
|
53595
|
+
throw new ValidationError("Push deploys require baselineHash (null on first deploy)");
|
|
53596
|
+
}
|
|
53597
|
+
if (isMigrateManaged(state)) {
|
|
53598
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
53599
|
+
throw new ValidationError(`This database is migrate-managed (${ledger.length} applied ` + "migration(s)) and does not accept push-mode SQL.", { code: DEPLOY_ERROR_CODES.strategyMismatch });
|
|
53600
|
+
}
|
|
53601
|
+
const storedHash = state?.schemaHash ?? null;
|
|
53602
|
+
if (payload.baselineHash !== storedHash) {
|
|
53603
|
+
throw await this.buildStateConflictError(game2.id, payload.baselineHash);
|
|
53604
|
+
}
|
|
53605
|
+
const statements = splitSqlStatements(payload.sql);
|
|
53606
|
+
const oversized = findOversizedStatement(statements);
|
|
53607
|
+
if (oversized) {
|
|
53608
|
+
throw new ValidationError(`Push statement ${oversized.index} is ${oversized.byteLength} bytes — over ` + `D1's ${D1_MAX_STATEMENT_BYTES / 1024} KB per-statement limit. Split the ` + "statement or reduce its size.", {
|
|
53609
|
+
code: DEPLOY_ERROR_CODES.pushFailed,
|
|
53610
|
+
statementIndex: oversized.index,
|
|
53611
|
+
offset: null
|
|
53612
|
+
});
|
|
53613
|
+
}
|
|
53614
|
+
const destructive = detectDestructiveStatements(statements);
|
|
53615
|
+
setAttributes({
|
|
53616
|
+
"app.deploy.push_statement_count": statements.length,
|
|
53617
|
+
"app.deploy.push_destructive_count": destructive.length,
|
|
53618
|
+
"app.deploy.push_accept_data_loss": Boolean(payload.acceptDataLoss)
|
|
53619
|
+
});
|
|
53620
|
+
if (destructive.length > 0 && !payload.acceptDataLoss) {
|
|
53621
|
+
throw new DestructiveSchemaError(destructive);
|
|
53622
|
+
}
|
|
53623
|
+
await this.assertNoSchemaDrift(databaseId, state);
|
|
53624
|
+
const reserved = await this.persistPushDeploymentState(game2.id, payload.baselineHash, {
|
|
53625
|
+
schemaFingerprint: PUSH_RESERVATION_FINGERPRINT,
|
|
53626
|
+
schemaHash: payload.nextHash,
|
|
53627
|
+
schemaSnapshot: payload.nextSnapshot
|
|
53628
|
+
});
|
|
53629
|
+
if (!reserved) {
|
|
53630
|
+
throw await this.buildStateConflictError(game2.id, payload.baselineHash);
|
|
53631
|
+
}
|
|
53632
|
+
let capture = null;
|
|
53633
|
+
if (statements.length > 0) {
|
|
53634
|
+
capture = yield* this.captureBookmarkStep(databaseId);
|
|
53635
|
+
yield {
|
|
53636
|
+
type: "status",
|
|
53637
|
+
data: {
|
|
53638
|
+
message: `Applying database schema changes (${statements.length} statements)`
|
|
53639
|
+
}
|
|
53640
|
+
};
|
|
53641
|
+
try {
|
|
53642
|
+
await cf.d1.batch(databaseId, [
|
|
53643
|
+
{ sql: "PRAGMA defer_foreign_keys = on" },
|
|
53644
|
+
...statements.map((statement) => ({ sql: statement })),
|
|
53645
|
+
{ sql: "PRAGMA defer_foreign_keys = off" }
|
|
53646
|
+
]);
|
|
53647
|
+
} catch (error) {
|
|
53648
|
+
await this.persistPushDeploymentState(game2.id, payload.nextHash, {
|
|
53649
|
+
schemaFingerprint: state?.schemaFingerprint ?? null,
|
|
53650
|
+
schemaHash: state?.schemaHash ?? null,
|
|
53651
|
+
schemaSnapshot: state?.schemaSnapshot ?? null
|
|
53652
|
+
});
|
|
53653
|
+
throw this.toPushStepError(error);
|
|
53654
|
+
}
|
|
53655
|
+
}
|
|
53656
|
+
const fingerprint = await cf.d1.fingerprintSchema(databaseId);
|
|
53657
|
+
await this.persistDeploymentState(game2.id, {
|
|
53658
|
+
schemaFingerprint: fingerprint.fingerprint
|
|
53659
|
+
});
|
|
53660
|
+
return {
|
|
53661
|
+
schemaHash: payload.nextHash,
|
|
53662
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
53663
|
+
timeTravelBookmark: capture?.bookmark ?? null,
|
|
53664
|
+
bookmarkCapturedAt: capture?.capturedAt ?? null
|
|
53665
|
+
};
|
|
53666
|
+
}
|
|
53667
|
+
async* captureBookmarkStep(databaseId) {
|
|
53668
|
+
const cf = this.getCloudflare();
|
|
53669
|
+
const bookmark = await cf.d1.captureBookmark(databaseId);
|
|
53670
|
+
const capturedAt = new Date;
|
|
53671
|
+
setAttribute("app.deploy.db_bookmark_captured", bookmark.captured);
|
|
53672
|
+
if (!bookmark.captured) {
|
|
53673
|
+
yield {
|
|
53674
|
+
type: "status",
|
|
53675
|
+
data: { message: `Time Travel bookmark unavailable (${bookmark.error})` }
|
|
53676
|
+
};
|
|
53677
|
+
return null;
|
|
53678
|
+
}
|
|
53679
|
+
yield {
|
|
53680
|
+
type: "status",
|
|
53681
|
+
data: {
|
|
53682
|
+
message: "Captured Time Travel bookmark",
|
|
53683
|
+
details: { bookmark: bookmark.bookmark }
|
|
53684
|
+
}
|
|
53685
|
+
};
|
|
53686
|
+
return { bookmark: bookmark.bookmark, capturedAt };
|
|
53687
|
+
}
|
|
53688
|
+
async toMigrationStepError(databaseId, error, migration) {
|
|
53689
|
+
if (error instanceof D1StatementTooLargeError) {
|
|
53690
|
+
return new ValidationError(error.message, {
|
|
53691
|
+
code: DEPLOY_ERROR_CODES.migrationFailed,
|
|
53692
|
+
tag: migration.tag,
|
|
53693
|
+
statementIndex: error.statementIndex,
|
|
53694
|
+
offset: null,
|
|
53695
|
+
d1Message: error.message
|
|
53696
|
+
});
|
|
53697
|
+
}
|
|
53698
|
+
if (error instanceof D1MigrationError) {
|
|
53699
|
+
addEvent("deploy.migration_failed", {
|
|
53700
|
+
"app.d1.migration_tag": migration.tag,
|
|
53701
|
+
"app.error.message": error.d1Message,
|
|
53702
|
+
...error.offset !== null && { "app.d1.error_offset": error.offset }
|
|
53703
|
+
});
|
|
53704
|
+
const alreadyApplied = isAlreadyExistsSqlError(error.d1Message) ? await this.assessAlreadyApplied(databaseId, migration.statements) : null;
|
|
53705
|
+
return new MigrationExecutionError({
|
|
53706
|
+
tag: migration.tag,
|
|
53707
|
+
d1Message: error.d1Message,
|
|
53708
|
+
offset: error.offset,
|
|
53709
|
+
...alreadyApplied ? { alreadyApplied } : {}
|
|
53710
|
+
});
|
|
53711
|
+
}
|
|
53712
|
+
return error;
|
|
53713
|
+
}
|
|
53714
|
+
async assessAlreadyApplied(databaseId, statements) {
|
|
53715
|
+
try {
|
|
53716
|
+
const cf = this.getCloudflare();
|
|
53717
|
+
const live = await cf.d1.fingerprintSchema(databaseId);
|
|
53718
|
+
const tables = await cf.d1.readTableColumns(databaseId, live.tables);
|
|
53719
|
+
const assessment = assessMigrationAlreadyApplied(statements, tables);
|
|
53720
|
+
addEvent("deploy.already_applied_assessment", {
|
|
53721
|
+
"app.deploy.already_applied_verdict": assessment.verdict,
|
|
53722
|
+
"app.deploy.already_applied_present": assessment.present.length,
|
|
53723
|
+
"app.deploy.already_applied_missing": assessment.missing.length
|
|
53724
|
+
});
|
|
53725
|
+
return assessment.verdict === "no-signal" ? null : assessment;
|
|
53726
|
+
} catch (assessError) {
|
|
53727
|
+
addEvent("deploy.already_applied_check_failed", {
|
|
53728
|
+
"app.error.message": errorMessage2(assessError)
|
|
53729
|
+
});
|
|
53730
|
+
return null;
|
|
53731
|
+
}
|
|
53732
|
+
}
|
|
53733
|
+
toPushStepError(error) {
|
|
53734
|
+
if (error instanceof D1BatchError) {
|
|
53735
|
+
addEvent("deploy.push_failed", {
|
|
53736
|
+
"app.error.message": error.d1Message,
|
|
53737
|
+
...error.offset !== null && { "app.d1.error_offset": error.offset }
|
|
53738
|
+
});
|
|
53739
|
+
return new PushExecutionError({ d1Message: error.d1Message, offset: error.offset });
|
|
53740
|
+
}
|
|
53741
|
+
return error;
|
|
53742
|
+
}
|
|
53743
|
+
async assertNoSchemaDrift(databaseId, state) {
|
|
53744
|
+
if (!state?.schemaFingerprint) {
|
|
53745
|
+
return;
|
|
53746
|
+
}
|
|
53747
|
+
const live = await this.getCloudflare().d1.fingerprintSchema(databaseId);
|
|
53748
|
+
if (live.fingerprint !== state.schemaFingerprint) {
|
|
53749
|
+
addEvent("deploy.state_drift", {
|
|
53750
|
+
"app.deploy.expected_fingerprint": state.schemaFingerprint,
|
|
53751
|
+
"app.deploy.actual_fingerprint": live.fingerprint
|
|
53752
|
+
});
|
|
53753
|
+
throw new DeploymentStateDriftError({
|
|
53754
|
+
expectedFingerprint: state.schemaFingerprint,
|
|
53755
|
+
actualFingerprint: live.fingerprint
|
|
53756
|
+
});
|
|
53757
|
+
}
|
|
53758
|
+
}
|
|
53759
|
+
async buildStateConflictError(gameId, claimedHash) {
|
|
53760
|
+
const [row, lastDeploy] = await Promise.all([
|
|
53761
|
+
this.deps.db.query.gameDeploymentState.findFirst({
|
|
53762
|
+
where: eq(gameDeploymentState.gameId, gameId),
|
|
53763
|
+
columns: { schemaHash: true }
|
|
53764
|
+
}),
|
|
53765
|
+
findLastSuccessfulDeployWithEmail(this.deps.db, gameId)
|
|
53766
|
+
]);
|
|
53767
|
+
const currentHash = row?.schemaHash ?? null;
|
|
53768
|
+
addEvent("deploy.state_conflict", {
|
|
53769
|
+
"app.deploy.baseline_hash": claimedHash ?? "null",
|
|
53770
|
+
"app.deploy.current_hash": currentHash ?? "null"
|
|
53771
|
+
});
|
|
53772
|
+
return new DeploymentStateConflictError({
|
|
53773
|
+
currentHash,
|
|
53774
|
+
lastDeployAt: lastDeploy?.at.toISOString() ?? null,
|
|
53775
|
+
lastDeployBy: lastDeploy?.email ?? lastDeploy?.userId ?? null
|
|
53776
|
+
});
|
|
53777
|
+
}
|
|
53778
|
+
async recordAppliedPrefixFingerprint(gameId, databaseId) {
|
|
53779
|
+
try {
|
|
53780
|
+
const fingerprint = await this.getCloudflare().d1.fingerprintSchema(databaseId);
|
|
53781
|
+
await this.persistDeploymentState(gameId, {
|
|
53782
|
+
schemaFingerprint: fingerprint.fingerprint
|
|
53783
|
+
});
|
|
53784
|
+
} catch (error) {
|
|
53785
|
+
addEvent("deploy.fingerprint_persist_failed", {
|
|
53786
|
+
"exception.type": errorType(error),
|
|
53787
|
+
"app.error.message": errorMessage2(error)
|
|
53788
|
+
});
|
|
53789
|
+
}
|
|
53790
|
+
}
|
|
53791
|
+
async persistDeploymentState(gameId, patch) {
|
|
53792
|
+
const set = {
|
|
53793
|
+
...patch,
|
|
53794
|
+
updatedAt: new Date
|
|
53795
|
+
};
|
|
53796
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, baselineSource: "deploy", ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
53797
|
+
}
|
|
53798
|
+
async persistArtifactHashes(gameId, patch) {
|
|
53799
|
+
const set = {
|
|
53800
|
+
...patch.buildHash !== undefined && { buildHash: patch.buildHash },
|
|
53801
|
+
...patch.integrationsHash !== undefined && {
|
|
53802
|
+
integrationsHash: patch.integrationsHash
|
|
53803
|
+
}
|
|
53804
|
+
};
|
|
53805
|
+
if (Object.keys(set).length === 0) {
|
|
53806
|
+
return;
|
|
53807
|
+
}
|
|
53808
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, ...set, updatedAt: new Date }).onConflictDoUpdate({
|
|
53809
|
+
target: gameDeploymentState.gameId,
|
|
53810
|
+
set: { ...set, updatedAt: new Date }
|
|
53811
|
+
});
|
|
53812
|
+
}
|
|
53813
|
+
async persistPushDeploymentState(gameId, baselineHash, patch) {
|
|
53814
|
+
const set = { ...patch, updatedAt: new Date };
|
|
53815
|
+
if (baselineHash === null) {
|
|
53816
|
+
const claimed2 = await this.deps.db.insert(gameDeploymentState).values({ gameId, baselineSource: "deploy", ...set }).onConflictDoUpdate({
|
|
53817
|
+
target: gameDeploymentState.gameId,
|
|
53818
|
+
set,
|
|
53819
|
+
setWhere: isNull(gameDeploymentState.schemaHash)
|
|
53820
|
+
}).returning({ gameId: gameDeploymentState.gameId });
|
|
53821
|
+
return claimed2.length > 0;
|
|
53822
|
+
}
|
|
53823
|
+
const claimed = await this.deps.db.update(gameDeploymentState).set(set).where(and(eq(gameDeploymentState.gameId, gameId), eq(gameDeploymentState.schemaHash, baselineHash))).returning({ gameId: gameDeploymentState.gameId });
|
|
53824
|
+
return claimed.length > 0;
|
|
53825
|
+
}
|
|
53826
|
+
async* adoptClientBaseline(context2) {
|
|
53827
|
+
const { game: game2, request, user, deploymentId, baseline } = context2;
|
|
53828
|
+
const cf = this.getCloudflare();
|
|
53829
|
+
const persistedId = context2.existingResources?.d1?.find((db2) => db2.name === deploymentId)?.id;
|
|
53830
|
+
const databaseId = persistedId ?? (hasBinding(request.bindings?.database) ? await cf.d1.create(deploymentId) : null);
|
|
53831
|
+
if (baseline.lastAppliedMigrationTag && !databaseId) {
|
|
53832
|
+
throw new ValidationError("Baseline claims applied migrations, but the deploy has no database binding");
|
|
53833
|
+
}
|
|
53834
|
+
const live = databaseId ? await cf.d1.fingerprintSchema(databaseId) : null;
|
|
53835
|
+
let recordedRows = 0;
|
|
53836
|
+
if (databaseId && baseline.lastAppliedMigrationTag) {
|
|
53837
|
+
if (live.tables.length === 0) {
|
|
53838
|
+
throw new BaselineDatabaseEmptyError;
|
|
53839
|
+
}
|
|
53840
|
+
const slice = sliceJournalToTag(baseline.journal ?? [], baseline.lastAppliedMigrationTag);
|
|
53841
|
+
if (!slice) {
|
|
53842
|
+
throw new ValidationError(`Baseline lastAppliedMigrationTag '${baseline.lastAppliedMigrationTag}' ` + "is not in the submitted journal");
|
|
53843
|
+
}
|
|
53844
|
+
if (baseline.evidence?.length) {
|
|
53845
|
+
const [tables, lastDeploy] = await Promise.all([
|
|
53846
|
+
cf.d1.readTableColumns(databaseId, live.tables),
|
|
53847
|
+
findLastSuccessfulDeploy(this.deps.db, game2.id)
|
|
53848
|
+
]);
|
|
53849
|
+
assertBaselineClaimValid({
|
|
53850
|
+
claimedTag: baseline.lastAppliedMigrationTag,
|
|
53851
|
+
evidence: baseline.evidence,
|
|
53852
|
+
tables,
|
|
53853
|
+
indexes: new Set(live.indexes),
|
|
53854
|
+
views: new Set(live.views),
|
|
53855
|
+
lastDeployAt: lastDeploy?.at ?? null,
|
|
53856
|
+
allowUnverified: false,
|
|
53857
|
+
source: "seed",
|
|
53858
|
+
gameId: game2.id,
|
|
53859
|
+
userId: user.id
|
|
53860
|
+
});
|
|
53861
|
+
}
|
|
53862
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
53863
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
53864
|
+
const plan = planMigrations(slice, ledger.map((row) => ({
|
|
53865
|
+
tag: row.tag,
|
|
53866
|
+
checksum: row.checksum,
|
|
53867
|
+
checksumAlgo: row.checksum_algo
|
|
53868
|
+
})));
|
|
53869
|
+
if (plan.checksumMismatches.length > 0) {
|
|
53870
|
+
throw new MigrationChecksumMismatchError(plan.checksumMismatches);
|
|
53871
|
+
}
|
|
53872
|
+
if (plan.missingFromJournalTags.length > 0) {
|
|
53873
|
+
throw new MigrationJournalDivergenceError(plan.missingFromJournalTags);
|
|
53874
|
+
}
|
|
53875
|
+
if (plan.outOfOrderTags.length > 0) {
|
|
53876
|
+
throw new MigrationOrderError(plan.outOfOrderTags);
|
|
53877
|
+
}
|
|
53878
|
+
const checksumByTag = new Map(slice.map((entry2) => [entry2.tag, entry2.checksum]));
|
|
53879
|
+
const rows = plan.pendingTags.map((tag) => ({
|
|
53880
|
+
tag,
|
|
53881
|
+
checksum: checksumByTag.get(tag)
|
|
53882
|
+
}));
|
|
53883
|
+
if (rows.length > 0) {
|
|
53884
|
+
await cf.d1.recordLedgerRows(databaseId, {
|
|
53885
|
+
rows,
|
|
53886
|
+
deployId: `baseline:${request.deployId ?? crypto.randomUUID()}`,
|
|
53887
|
+
appliedBy: user.id,
|
|
53888
|
+
source: "baseline"
|
|
53889
|
+
});
|
|
53890
|
+
}
|
|
53891
|
+
recordedRows = rows.length;
|
|
53892
|
+
}
|
|
53893
|
+
const fingerprint = live?.fingerprint ?? null;
|
|
53894
|
+
const set = {
|
|
53895
|
+
...baseline.schemaHash !== undefined && { schemaHash: baseline.schemaHash },
|
|
53896
|
+
...baseline.schemaSnapshot !== undefined && {
|
|
53897
|
+
schemaSnapshot: baseline.schemaSnapshot
|
|
53898
|
+
},
|
|
53899
|
+
...baseline.integrationsHash !== undefined && {
|
|
53900
|
+
integrationsHash: baseline.integrationsHash
|
|
53901
|
+
},
|
|
53902
|
+
...baseline.buildHash !== undefined && { buildHash: baseline.buildHash },
|
|
53903
|
+
...fingerprint !== null && { schemaFingerprint: fingerprint },
|
|
53904
|
+
baselineSource: "client-baseline",
|
|
53905
|
+
updatedAt: new Date
|
|
53906
|
+
};
|
|
53907
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId: game2.id, ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
53908
|
+
setAttributes({
|
|
53909
|
+
"app.deploy.baseline_adopted": true,
|
|
53910
|
+
"app.deploy.baseline_ledger_rows": recordedRows,
|
|
53911
|
+
"app.deploy.baseline_has_snapshot": baseline.schemaSnapshot !== undefined
|
|
53912
|
+
});
|
|
53913
|
+
yield {
|
|
53914
|
+
type: "status",
|
|
53915
|
+
data: {
|
|
53916
|
+
message: "Adopted deployment state from client baseline",
|
|
53917
|
+
details: {
|
|
53918
|
+
ledgerRows: recordedRows,
|
|
53919
|
+
...baseline.lastAppliedMigrationTag && {
|
|
53920
|
+
lastAppliedMigrationTag: baseline.lastAppliedMigrationTag
|
|
53921
|
+
}
|
|
53922
|
+
}
|
|
53923
|
+
}
|
|
53924
|
+
};
|
|
53925
|
+
return this.deps.db.query.gameDeploymentState.findFirst({
|
|
53926
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
53927
|
+
});
|
|
53928
|
+
}
|
|
52465
53929
|
async applyGameMetadata(gameId, request, hasFrontend, hasMetadata, deploymentUrl) {
|
|
52466
53930
|
const updates = { updatedAt: new Date };
|
|
52467
53931
|
if (hasFrontend) {
|
|
@@ -52487,7 +53951,8 @@ class DeployService {
|
|
|
52487
53951
|
result,
|
|
52488
53952
|
request,
|
|
52489
53953
|
user,
|
|
52490
|
-
flags: flags2
|
|
53954
|
+
flags: flags2,
|
|
53955
|
+
database
|
|
52491
53956
|
}) {
|
|
52492
53957
|
const { hasBackend, hasFrontend, hasMetadata } = flags2;
|
|
52493
53958
|
const db2 = this.deps.db;
|
|
@@ -52497,9 +53962,17 @@ class DeployService {
|
|
|
52497
53962
|
deploymentId: result.deploymentId,
|
|
52498
53963
|
url: result.url,
|
|
52499
53964
|
codeHash,
|
|
53965
|
+
schemaHash: database.schemaHash,
|
|
53966
|
+
schemaFingerprint: database.schemaFingerprint,
|
|
53967
|
+
timeTravelBookmark: database.timeTravelBookmark,
|
|
53968
|
+
bookmarkCapturedAt: database.bookmarkCapturedAt,
|
|
52500
53969
|
resources: result.resources,
|
|
52501
53970
|
target: "game"
|
|
52502
53971
|
});
|
|
53972
|
+
await this.persistArtifactHashes(game2.id, {
|
|
53973
|
+
buildHash: hasFrontend ? request.buildHash : undefined,
|
|
53974
|
+
integrationsHash: request.integrationsHash
|
|
53975
|
+
});
|
|
52503
53976
|
if (hasBackend) {
|
|
52504
53977
|
await withSpan("deploy.configure_worker_secrets", async () => {
|
|
52505
53978
|
await this.ensureWorkerApiKeyOnWorker(user, DeployService.gameWorkerKeySpec(slug), result.deploymentId);
|
|
@@ -52636,6 +54109,29 @@ class DeployService {
|
|
|
52636
54109
|
const cf = this.getCloudflare();
|
|
52637
54110
|
await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
|
|
52638
54111
|
}
|
|
54112
|
+
async* pruneManagedSecretsStep(gameId, deploymentId, pruneSecrets) {
|
|
54113
|
+
const cf = this.getCloudflare();
|
|
54114
|
+
const keys = [...new Set(pruneSecrets)];
|
|
54115
|
+
yield {
|
|
54116
|
+
type: "status",
|
|
54117
|
+
data: {
|
|
54118
|
+
message: `Pruning ${keys.length} managed secret(s)`,
|
|
54119
|
+
details: { keys }
|
|
54120
|
+
}
|
|
54121
|
+
};
|
|
54122
|
+
await withSpan("deploy.prune_secrets", async () => {
|
|
54123
|
+
const existing = await cf.listSecrets(deploymentId);
|
|
54124
|
+
for (const key of keys) {
|
|
54125
|
+
const prefixedKey = `${SECRETS_PREFIX}${key}`;
|
|
54126
|
+
if (existing.includes(prefixedKey)) {
|
|
54127
|
+
await cf.deleteSecret(deploymentId, prefixedKey);
|
|
54128
|
+
}
|
|
54129
|
+
}
|
|
54130
|
+
const pruned = keys.reduce((expr, key) => sql`${expr} - ${key}::text`, sql`coalesce(${gameDeploymentState.secretsManifest}, '{}'::jsonb)`);
|
|
54131
|
+
await this.deps.db.update(gameDeploymentState).set({ secretsManifest: pruned, updatedAt: new Date }).where(eq(gameDeploymentState.gameId, gameId));
|
|
54132
|
+
});
|
|
54133
|
+
setAttribute("app.deploy.pruned_secret_count", keys.length);
|
|
54134
|
+
}
|
|
52639
54135
|
async ensureDashboardSessionSecret(deploymentId, existingSecrets, hasPriorDeployment) {
|
|
52640
54136
|
if (existingSecrets === null && hasPriorDeployment) {
|
|
52641
54137
|
setAttribute("app.deploy.session_secret_outcome", "check_failed_kept");
|
|
@@ -52740,6 +54236,10 @@ class DeployService {
|
|
|
52740
54236
|
target: record.target,
|
|
52741
54237
|
url: record.url,
|
|
52742
54238
|
codeHash: record.codeHash,
|
|
54239
|
+
schemaHash: record.schemaHash ?? null,
|
|
54240
|
+
schemaFingerprint: record.schemaFingerprint ?? null,
|
|
54241
|
+
timeTravelBookmark: record.timeTravelBookmark ?? null,
|
|
54242
|
+
bookmarkCapturedAt: record.bookmarkCapturedAt ?? null,
|
|
52743
54243
|
resources: record.resources,
|
|
52744
54244
|
isActive: true
|
|
52745
54245
|
});
|
|
@@ -52749,8 +54249,11 @@ class DeployService {
|
|
|
52749
54249
|
await this.deps.alerts.notifyDeploymentFailure(failure).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
|
|
52750
54250
|
}
|
|
52751
54251
|
}
|
|
54252
|
+
var PUSH_RESERVATION_FINGERPRINT = "reserved:push-in-flight";
|
|
54253
|
+
var NO_DATABASE_WORK;
|
|
52752
54254
|
var init_deploy_service = __esm(() => {
|
|
52753
54255
|
init_drizzle_orm();
|
|
54256
|
+
init_src4();
|
|
52754
54257
|
init_playcademy();
|
|
52755
54258
|
init_src();
|
|
52756
54259
|
init_helpers_index();
|
|
@@ -52758,9 +54261,17 @@ var init_deploy_service = __esm(() => {
|
|
|
52758
54261
|
init_spans();
|
|
52759
54262
|
init_tunnel();
|
|
52760
54263
|
init_errors();
|
|
54264
|
+
init_baseline_validation_util();
|
|
52761
54265
|
init_dashboard_util();
|
|
52762
54266
|
init_deployment_util();
|
|
54267
|
+
init_migration_util();
|
|
52763
54268
|
init_worker_keys_util();
|
|
54269
|
+
NO_DATABASE_WORK = {
|
|
54270
|
+
schemaHash: null,
|
|
54271
|
+
schemaFingerprint: null,
|
|
54272
|
+
timeTravelBookmark: null,
|
|
54273
|
+
bookmarkCapturedAt: null
|
|
54274
|
+
};
|
|
52764
54275
|
});
|
|
52765
54276
|
|
|
52766
54277
|
class DeveloperService {
|
|
@@ -54205,7 +55716,7 @@ function createGameServices(deps) {
|
|
|
54205
55716
|
}
|
|
54206
55717
|
};
|
|
54207
55718
|
}
|
|
54208
|
-
var
|
|
55719
|
+
var init_game3 = __esm(() => {
|
|
54209
55720
|
init_dashboard_service();
|
|
54210
55721
|
init_deploy_job_service();
|
|
54211
55722
|
init_deploy_service();
|
|
@@ -54395,16 +55906,37 @@ class AlertsService {
|
|
|
54395
55906
|
await this.sendAlert(discord, embed.build());
|
|
54396
55907
|
}
|
|
54397
55908
|
async notifyDeploymentFailure(failure) {
|
|
54398
|
-
const
|
|
55909
|
+
const refused = DEPLOY_REFUSAL_CODES.has(failure.errorCode ?? "");
|
|
55910
|
+
const discord = this.recordAlert(refused ? "deployment_blocked" : "deployment_failure");
|
|
54399
55911
|
if (!discord) {
|
|
54400
55912
|
return;
|
|
54401
55913
|
}
|
|
54402
|
-
const [
|
|
54403
|
-
const
|
|
55914
|
+
const [noun, subject] = failure.target === "dashboard" ? ["Dashboard Deployment", "Dashboard deployment"] : ["Deployment", "Deployment"];
|
|
55915
|
+
const title = refused ? `\uD83D\uDEA7 ${noun} Blocked` : `❌ ${noun} Failed`;
|
|
55916
|
+
const verb = refused ? "was blocked" : "failed";
|
|
55917
|
+
const embed = new DiscordEmbedBuilder().setTitle(title).setDescription(`${subject} ${verb} for **${failure.displayName || failure.slug}** (**${this.getEnvironment()}**).`).setColor(refused ? DiscordColors.YELLOW : DiscordColors.RED).addField("Slug", failure.slug, true);
|
|
54404
55918
|
if (failure.developer) {
|
|
54405
55919
|
embed.addField("Developer", failure.developer.email || failure.developer.id, true);
|
|
54406
55920
|
}
|
|
54407
|
-
embed.addField("Error", failure.error, false)
|
|
55921
|
+
embed.addField(refused ? "Reason" : "Error", failure.error, false);
|
|
55922
|
+
if (refused) {
|
|
55923
|
+
embed.addField("Next step", "The developer received repair guidance in their terminal", false);
|
|
55924
|
+
}
|
|
55925
|
+
embed.setFooter("Playcademy Developer Platform").setTimestamp();
|
|
55926
|
+
await this.sendAlert(discord, embed.build());
|
|
55927
|
+
}
|
|
55928
|
+
async notifyDeployBlocked(blocked) {
|
|
55929
|
+
const discord = this.recordAlert("deployment_blocked");
|
|
55930
|
+
if (!discord) {
|
|
55931
|
+
return;
|
|
55932
|
+
}
|
|
55933
|
+
const embed = new DiscordEmbedBuilder().setTitle("\uD83D\uDEA7 Deployment Blocked").setDescription(`Deployment was blocked for **${blocked.displayName || blocked.slug}** (**${this.getEnvironment()}**).`).setColor(DiscordColors.YELLOW).addField("Slug", blocked.slug, true);
|
|
55934
|
+
if (blocked.developer) {
|
|
55935
|
+
embed.addField("Developer", blocked.developer.email || blocked.developer.id, true);
|
|
55936
|
+
}
|
|
55937
|
+
embed.addField("Reason", blocked.reason, false);
|
|
55938
|
+
embed.addField("Next step", "The developer received repair guidance in their terminal", false);
|
|
55939
|
+
embed.setFooter("Playcademy Developer Platform").setTimestamp();
|
|
54408
55940
|
await this.sendAlert(discord, embed.build());
|
|
54409
55941
|
}
|
|
54410
55942
|
async notifyGameDeletion(game2) {
|
|
@@ -54465,6 +55997,7 @@ var DISCORD_FIELD_LIMIT = 1024;
|
|
|
54465
55997
|
var init_alerts_service = __esm(() => {
|
|
54466
55998
|
init_discord();
|
|
54467
55999
|
init_spans();
|
|
56000
|
+
init_game2();
|
|
54468
56001
|
});
|
|
54469
56002
|
|
|
54470
56003
|
class KVBackupService {
|
|
@@ -54893,6 +56426,15 @@ class DatabaseService {
|
|
|
54893
56426
|
constructor(deps) {
|
|
54894
56427
|
this.deps = deps;
|
|
54895
56428
|
}
|
|
56429
|
+
static remapD1Resource(resources, d1ResourceName, databaseId) {
|
|
56430
|
+
return {
|
|
56431
|
+
resources: {
|
|
56432
|
+
...resources,
|
|
56433
|
+
d1: resources.d1?.map((dbResource) => dbResource.name === d1ResourceName ? { ...dbResource, id: databaseId } : dbResource)
|
|
56434
|
+
},
|
|
56435
|
+
timeTravelBookmark: null
|
|
56436
|
+
};
|
|
56437
|
+
}
|
|
54896
56438
|
getD1() {
|
|
54897
56439
|
const d1 = this.deps.cloudflare?.d1;
|
|
54898
56440
|
if (!d1) {
|
|
@@ -54911,11 +56453,7 @@ class DatabaseService {
|
|
|
54911
56453
|
try {
|
|
54912
56454
|
await this.deps.cloudflare.updateD1Binding(dashboardDeployment.deploymentId, databaseId);
|
|
54913
56455
|
if (dashboardDeployment.resources?.d1?.length) {
|
|
54914
|
-
|
|
54915
|
-
...dashboardDeployment.resources,
|
|
54916
|
-
d1: dashboardDeployment.resources.d1.map((dbResource) => dbResource.name === d1ResourceName ? { ...dbResource, id: databaseId } : dbResource)
|
|
54917
|
-
};
|
|
54918
|
-
await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, dashboardDeployment.id));
|
|
56456
|
+
await this.deps.db.update(gameDeployments).set(DatabaseService.remapD1Resource(dashboardDeployment.resources, d1ResourceName, databaseId)).where(eq(gameDeployments.id, dashboardDeployment.id));
|
|
54919
56457
|
}
|
|
54920
56458
|
setAttribute("app.database.dashboard_binding", "updated");
|
|
54921
56459
|
} catch (error) {
|
|
@@ -54926,20 +56464,36 @@ class DatabaseService {
|
|
|
54926
56464
|
});
|
|
54927
56465
|
}
|
|
54928
56466
|
}
|
|
54929
|
-
async reset(slug, user,
|
|
56467
|
+
async reset(slug, user, request = {}) {
|
|
56468
|
+
const { schema: schema2, database } = request;
|
|
54930
56469
|
setAttributes({
|
|
54931
56470
|
"app.database.operation": "reset",
|
|
56471
|
+
"app.database.mode": database?.mode ?? (schema2 ? "legacy" : "none"),
|
|
54932
56472
|
"app.database.schema_size": schema2?.sql.length,
|
|
54933
56473
|
"app.database.schema_version": schema2?.hash
|
|
54934
56474
|
});
|
|
54935
56475
|
const d1 = this.getD1();
|
|
54936
56476
|
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56477
|
+
const state = await this.deps.db.query.gameDeploymentState.findFirst({
|
|
56478
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
56479
|
+
});
|
|
56480
|
+
if (isSchemaAdopted(state) && !database) {
|
|
56481
|
+
if (schema2) {
|
|
56482
|
+
throw new LegacySchemaUpgradeRequiredError;
|
|
56483
|
+
}
|
|
56484
|
+
throw new ValidationError("This game's database state is server-managed: resetting it requires the mode-aware rebuild payload so the recorded state matches the new database. Run the reset through the Playcademy CLI.");
|
|
56485
|
+
}
|
|
54937
56486
|
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56487
|
+
let resetDatabaseId = null;
|
|
54938
56488
|
try {
|
|
54939
56489
|
const databaseId = await d1.reset(deploymentId);
|
|
56490
|
+
resetDatabaseId = databaseId;
|
|
54940
56491
|
setAttribute("app.database.id", databaseId);
|
|
54941
56492
|
let schemaPushed = false;
|
|
54942
|
-
if (
|
|
56493
|
+
if (database) {
|
|
56494
|
+
await this.rebuildDatabase(databaseId, database, game2.id, user);
|
|
56495
|
+
schemaPushed = true;
|
|
56496
|
+
} else if (schema2?.sql) {
|
|
54943
56497
|
await d1.executeSchema(databaseId, schema2);
|
|
54944
56498
|
schemaPushed = true;
|
|
54945
56499
|
}
|
|
@@ -54955,11 +56509,7 @@ class DatabaseService {
|
|
|
54955
56509
|
});
|
|
54956
56510
|
setAttribute("app.database.active_deployment_found", Boolean(activeDeployment));
|
|
54957
56511
|
if (activeDeployment?.resources?.d1?.length) {
|
|
54958
|
-
|
|
54959
|
-
...activeDeployment.resources,
|
|
54960
|
-
d1: activeDeployment.resources.d1.map((dbResource) => dbResource.name === deploymentId ? { ...dbResource, id: databaseId } : dbResource)
|
|
54961
|
-
};
|
|
54962
|
-
await this.deps.db.update(gameDeployments).set({ resources: updatedResources }).where(eq(gameDeployments.id, activeDeployment.id));
|
|
56512
|
+
await this.deps.db.update(gameDeployments).set(DatabaseService.remapD1Resource(activeDeployment.resources, deploymentId, databaseId)).where(eq(gameDeployments.id, activeDeployment.id));
|
|
54963
56513
|
}
|
|
54964
56514
|
await this.syncDashboardD1Binding(game2.id, deploymentId, databaseId);
|
|
54965
56515
|
setAttributes({
|
|
@@ -54973,24 +56523,604 @@ class DatabaseService {
|
|
|
54973
56523
|
schemaPushed
|
|
54974
56524
|
};
|
|
54975
56525
|
} catch (error) {
|
|
56526
|
+
if (database && resetDatabaseId) {
|
|
56527
|
+
await this.recordLiveFingerprint(game2.id, resetDatabaseId);
|
|
56528
|
+
}
|
|
54976
56529
|
this.deps.alerts.notifyDatabaseResetFailure({
|
|
54977
56530
|
slug,
|
|
54978
56531
|
displayName: game2.displayName,
|
|
54979
56532
|
error: errorMessage2(error),
|
|
54980
56533
|
developer: { id: user.id, email: user.email }
|
|
54981
56534
|
}).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "database_reset" }));
|
|
56535
|
+
if (error instanceof DomainError) {
|
|
56536
|
+
throw error;
|
|
56537
|
+
}
|
|
54982
56538
|
throw new ValidationError(`Database reset failed: ${errorMessage2(error)}`);
|
|
54983
56539
|
}
|
|
54984
56540
|
}
|
|
56541
|
+
async rebuildDatabase(databaseId, payload, gameId, user) {
|
|
56542
|
+
const d1 = this.getD1();
|
|
56543
|
+
if (payload.mode === "migrate") {
|
|
56544
|
+
const deployId = `reset:${crypto.randomUUID()}`;
|
|
56545
|
+
await d1.ensureMigrationLedger(databaseId);
|
|
56546
|
+
for (const migration of payload.migrations) {
|
|
56547
|
+
await d1.applyMigration(databaseId, {
|
|
56548
|
+
tag: migration.tag,
|
|
56549
|
+
statements: migration.statements,
|
|
56550
|
+
checksum: migration.checksum,
|
|
56551
|
+
deployId,
|
|
56552
|
+
appliedBy: user.id
|
|
56553
|
+
});
|
|
56554
|
+
}
|
|
56555
|
+
setAttribute("app.database.migrations_replayed", payload.migrations.length);
|
|
56556
|
+
const fingerprint2 = await d1.fingerprintSchema(databaseId);
|
|
56557
|
+
await this.persistDeploymentState(gameId, {
|
|
56558
|
+
schemaFingerprint: fingerprint2.fingerprint,
|
|
56559
|
+
schemaHash: null,
|
|
56560
|
+
schemaSnapshot: null
|
|
56561
|
+
});
|
|
56562
|
+
return;
|
|
56563
|
+
}
|
|
56564
|
+
const statements = splitSqlStatements(payload.sql);
|
|
56565
|
+
if (statements.length > 0) {
|
|
56566
|
+
await d1.batch(databaseId, [
|
|
56567
|
+
{ sql: "PRAGMA defer_foreign_keys = on" },
|
|
56568
|
+
...statements.map((statement) => ({ sql: statement })),
|
|
56569
|
+
{ sql: "PRAGMA defer_foreign_keys = off" }
|
|
56570
|
+
]);
|
|
56571
|
+
}
|
|
56572
|
+
setAttribute("app.database.push_statement_count", statements.length);
|
|
56573
|
+
const fingerprint = await d1.fingerprintSchema(databaseId);
|
|
56574
|
+
await this.persistDeploymentState(gameId, {
|
|
56575
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
56576
|
+
schemaHash: payload.nextHash,
|
|
56577
|
+
schemaSnapshot: payload.nextSnapshot
|
|
56578
|
+
});
|
|
56579
|
+
}
|
|
56580
|
+
async persistDeploymentState(gameId, patch) {
|
|
56581
|
+
const set = { ...patch, updatedAt: new Date };
|
|
56582
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, baselineSource: "deploy", ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
56583
|
+
}
|
|
56584
|
+
async recordLiveFingerprint(gameId, databaseId) {
|
|
56585
|
+
try {
|
|
56586
|
+
const fingerprint = await this.getD1().fingerprintSchema(databaseId);
|
|
56587
|
+
await this.persistDeploymentState(gameId, {
|
|
56588
|
+
schemaFingerprint: fingerprint.fingerprint
|
|
56589
|
+
});
|
|
56590
|
+
} catch (error) {
|
|
56591
|
+
addEvent("database.fingerprint_persist_failed", {
|
|
56592
|
+
"exception.type": errorType(error),
|
|
56593
|
+
"app.error.message": errorMessage2(error)
|
|
56594
|
+
});
|
|
56595
|
+
}
|
|
56596
|
+
}
|
|
54985
56597
|
}
|
|
54986
56598
|
var init_database_service = __esm(() => {
|
|
54987
56599
|
init_drizzle_orm();
|
|
56600
|
+
init_src4();
|
|
54988
56601
|
init_helpers_index();
|
|
54989
56602
|
init_tables_index();
|
|
54990
56603
|
init_spans();
|
|
54991
56604
|
init_errors();
|
|
54992
56605
|
init_deployment_util();
|
|
54993
56606
|
});
|
|
56607
|
+
async function listUserSecretKeys(cloudflare2, deploymentId) {
|
|
56608
|
+
try {
|
|
56609
|
+
const allKeys = await cloudflare2.listSecrets(deploymentId);
|
|
56610
|
+
return allKeys.filter((key) => key.startsWith(SECRETS_PREFIX)).map((key) => key.slice(SECRETS_PREFIX.length));
|
|
56611
|
+
} catch (error) {
|
|
56612
|
+
const message = errorMessage2(error);
|
|
56613
|
+
if (message.includes("not found") || message.includes("10007")) {
|
|
56614
|
+
return null;
|
|
56615
|
+
}
|
|
56616
|
+
throw error;
|
|
56617
|
+
}
|
|
56618
|
+
}
|
|
56619
|
+
var init_secrets_util = __esm(() => {
|
|
56620
|
+
init_src();
|
|
56621
|
+
});
|
|
56622
|
+
function historyEventEntry(event) {
|
|
56623
|
+
const base = { at: event.createdAt.toISOString(), by: event.email ?? DELETED_ACCOUNT_LABEL };
|
|
56624
|
+
if (event.kind === "restore" && "restoredTo" in event.payload) {
|
|
56625
|
+
return [{ kind: "restore", ...base, restoredTo: event.payload.restoredTo }];
|
|
56626
|
+
}
|
|
56627
|
+
if (event.kind === "blocked" && "code" in event.payload) {
|
|
56628
|
+
return [
|
|
56629
|
+
{ kind: "blocked", ...base, code: event.payload.code, reason: event.payload.reason }
|
|
56630
|
+
];
|
|
56631
|
+
}
|
|
56632
|
+
return [];
|
|
56633
|
+
}
|
|
56634
|
+
function databaseIdFromResources(resources, deploymentId) {
|
|
56635
|
+
const database = resources?.d1?.find((db2) => db2.name === deploymentId) ?? resources?.d1?.[0];
|
|
56636
|
+
return database?.id ?? null;
|
|
56637
|
+
}
|
|
56638
|
+
function restorePointCapturedAt(row) {
|
|
56639
|
+
return row.bookmarkCapturedAt ?? row.deployedAt;
|
|
56640
|
+
}
|
|
56641
|
+
|
|
56642
|
+
class DeploymentStateService {
|
|
56643
|
+
deps;
|
|
56644
|
+
constructor(deps) {
|
|
56645
|
+
this.deps = deps;
|
|
56646
|
+
}
|
|
56647
|
+
async partitionSecretKeys(slug, manifest) {
|
|
56648
|
+
const managedKeys = Object.keys(manifest ?? {});
|
|
56649
|
+
if (!this.deps.cloudflare) {
|
|
56650
|
+
addEvent("deployment_state.secret_keys_unavailable", {
|
|
56651
|
+
"app.error.message": "Cloudflare provider not configured"
|
|
56652
|
+
});
|
|
56653
|
+
return { managedKeys, unmanagedKeys: null };
|
|
56654
|
+
}
|
|
56655
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56656
|
+
const remoteKeys = await listUserSecretKeys(this.deps.cloudflare, deploymentId);
|
|
56657
|
+
if (remoteKeys === null) {
|
|
56658
|
+
return { managedKeys, unmanagedKeys: [] };
|
|
56659
|
+
}
|
|
56660
|
+
const managed = new Set(managedKeys);
|
|
56661
|
+
return {
|
|
56662
|
+
managedKeys,
|
|
56663
|
+
unmanagedKeys: remoteKeys.filter((key) => !managed.has(key))
|
|
56664
|
+
};
|
|
56665
|
+
}
|
|
56666
|
+
async readAppliedMigrations(slug, resources) {
|
|
56667
|
+
if (!this.deps.cloudflare || !resources?.d1?.length) {
|
|
56668
|
+
return null;
|
|
56669
|
+
}
|
|
56670
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56671
|
+
const databaseId = databaseIdFromResources(resources, deploymentId);
|
|
56672
|
+
if (!databaseId) {
|
|
56673
|
+
return null;
|
|
56674
|
+
}
|
|
56675
|
+
const ledger = await this.deps.cloudflare.d1.readMigrationLedger(databaseId);
|
|
56676
|
+
setAttributes({ "app.deployment_state.applied_migrations": ledger.length });
|
|
56677
|
+
return ledger.map((row) => ({
|
|
56678
|
+
tag: row.tag,
|
|
56679
|
+
checksum: row.checksum,
|
|
56680
|
+
checksumAlgo: row.checksum_algo
|
|
56681
|
+
}));
|
|
56682
|
+
}
|
|
56683
|
+
async get(slug, user, options = {}) {
|
|
56684
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56685
|
+
const [state, gameDeployment, dashboardDeployment, lastSucceededJob, lastFailedJob] = await Promise.all([
|
|
56686
|
+
this.deps.db.query.gameDeploymentState.findFirst({
|
|
56687
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
56688
|
+
}),
|
|
56689
|
+
this.deps.db.query.gameDeployments.findFirst({
|
|
56690
|
+
where: activeDeploymentWhere(game2.id, "game"),
|
|
56691
|
+
columns: { codeHash: true, url: true, deployedAt: true, resources: true }
|
|
56692
|
+
}),
|
|
56693
|
+
this.deps.db.query.gameDeployments.findFirst({
|
|
56694
|
+
where: activeDeploymentWhere(game2.id, "dashboard"),
|
|
56695
|
+
columns: { url: true, deployedAt: true }
|
|
56696
|
+
}),
|
|
56697
|
+
findLastSuccessfulDeployWithEmail(this.deps.db, game2.id),
|
|
56698
|
+
this.deps.db.query.gameDeployJobs.findFirst({
|
|
56699
|
+
where: and(eq(gameDeployJobs.gameId, game2.id), eq(gameDeployJobs.status, "failed")),
|
|
56700
|
+
orderBy: desc(deployJobInstant()),
|
|
56701
|
+
columns: { events: true }
|
|
56702
|
+
})
|
|
56703
|
+
]);
|
|
56704
|
+
const [secrets, appliedMigrations] = await Promise.all([
|
|
56705
|
+
this.partitionSecretKeys(slug, state?.secretsManifest ?? null),
|
|
56706
|
+
this.readAppliedMigrations(slug, gameDeployment?.resources ?? null)
|
|
56707
|
+
]);
|
|
56708
|
+
setAttributes({
|
|
56709
|
+
"app.deployment_state.seeded": Boolean(state),
|
|
56710
|
+
"app.deployment_state.game_deployed": Boolean(gameDeployment),
|
|
56711
|
+
"app.deployment_state.dashboard_deployed": Boolean(dashboardDeployment)
|
|
56712
|
+
});
|
|
56713
|
+
return {
|
|
56714
|
+
gameId: game2.id,
|
|
56715
|
+
seeded: Boolean(state),
|
|
56716
|
+
game: gameDeployment ? {
|
|
56717
|
+
codeHash: gameDeployment.codeHash,
|
|
56718
|
+
buildHash: state?.buildHash ?? null,
|
|
56719
|
+
url: gameDeployment.url,
|
|
56720
|
+
deployedAt: gameDeployment.deployedAt.toISOString()
|
|
56721
|
+
} : null,
|
|
56722
|
+
dashboard: dashboardDeployment ? {
|
|
56723
|
+
url: dashboardDeployment.url,
|
|
56724
|
+
deployedAt: dashboardDeployment.deployedAt.toISOString()
|
|
56725
|
+
} : null,
|
|
56726
|
+
database: {
|
|
56727
|
+
appliedMigrations,
|
|
56728
|
+
lastFailure: parseMigrationFailure(lastFailedJob?.events ?? null),
|
|
56729
|
+
schemaHash: state?.schemaHash ?? null,
|
|
56730
|
+
schemaFingerprint: state?.schemaFingerprint ?? null,
|
|
56731
|
+
...options.includeSchemaSnapshot && {
|
|
56732
|
+
schemaSnapshot: state?.schemaSnapshot ?? null
|
|
56733
|
+
}
|
|
56734
|
+
},
|
|
56735
|
+
secrets,
|
|
56736
|
+
integrationsHash: state?.integrationsHash ?? null,
|
|
56737
|
+
compatibilityDate: state?.compatibilityDate ?? null,
|
|
56738
|
+
lastDeploy: lastSucceededJob ? {
|
|
56739
|
+
at: lastSucceededJob.at.toISOString(),
|
|
56740
|
+
by: lastSucceededJob.email ?? DELETED_ACCOUNT_LABEL
|
|
56741
|
+
} : null
|
|
56742
|
+
};
|
|
56743
|
+
}
|
|
56744
|
+
requireCloudflare() {
|
|
56745
|
+
if (!this.deps.cloudflare) {
|
|
56746
|
+
throw new ValidationError("Deployment-state operations are not available in this environment");
|
|
56747
|
+
}
|
|
56748
|
+
return this.deps.cloudflare;
|
|
56749
|
+
}
|
|
56750
|
+
async resolveDatabaseId(slug, gameId) {
|
|
56751
|
+
const databaseId = await this.findDatabaseId(slug, gameId);
|
|
56752
|
+
if (!databaseId) {
|
|
56753
|
+
throw new ValidationError("Game has no deployed database — deploy with a database binding first");
|
|
56754
|
+
}
|
|
56755
|
+
return databaseId;
|
|
56756
|
+
}
|
|
56757
|
+
findSchemaHashState(gameId) {
|
|
56758
|
+
return this.deps.db.query.gameDeploymentState.findFirst({
|
|
56759
|
+
where: eq(gameDeploymentState.gameId, gameId),
|
|
56760
|
+
columns: { schemaHash: true }
|
|
56761
|
+
});
|
|
56762
|
+
}
|
|
56763
|
+
async findDatabaseId(slug, gameId) {
|
|
56764
|
+
const deployment = await this.deps.db.query.gameDeployments.findFirst({
|
|
56765
|
+
where: activeDeploymentWhere(gameId, "game"),
|
|
56766
|
+
columns: { resources: true }
|
|
56767
|
+
});
|
|
56768
|
+
return databaseIdFromResources(deployment?.resources, getGameDeploymentId(slug, this.deps.config.sstStage));
|
|
56769
|
+
}
|
|
56770
|
+
async baseline(slug, input, user) {
|
|
56771
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56772
|
+
const cf = this.requireCloudflare();
|
|
56773
|
+
const state = await this.deps.db.query.gameDeploymentState.findFirst({
|
|
56774
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
56775
|
+
});
|
|
56776
|
+
const schemaAdopted = isSchemaAdopted(state);
|
|
56777
|
+
const migrateOnlyClaim = Boolean(input.lastAppliedMigrationTag) && input.schemaHash === undefined && input.schemaSnapshot === undefined;
|
|
56778
|
+
if (schemaAdopted && !migrateOnlyClaim) {
|
|
56779
|
+
throw new BaselineAlreadyAdoptedError(state?.baselineSource ?? null);
|
|
56780
|
+
}
|
|
56781
|
+
const databaseId = await this.resolveDatabaseId(slug, game2.id);
|
|
56782
|
+
const [ledger, live] = await Promise.all([
|
|
56783
|
+
cf.d1.readMigrationLedger(databaseId),
|
|
56784
|
+
cf.d1.fingerprintSchema(databaseId)
|
|
56785
|
+
]);
|
|
56786
|
+
const verdict = evaluateBaselineGuardrails({
|
|
56787
|
+
ledgerTags: ledger.map((row) => row.tag),
|
|
56788
|
+
liveTables: live.tables
|
|
56789
|
+
});
|
|
56790
|
+
if (verdict === "ledger-not-empty") {
|
|
56791
|
+
throw new BaselineLedgerNotEmptyError(ledger.map((row) => row.tag));
|
|
56792
|
+
}
|
|
56793
|
+
if (verdict === "database-empty") {
|
|
56794
|
+
throw new BaselineDatabaseEmptyError;
|
|
56795
|
+
}
|
|
56796
|
+
if (schemaAdopted && state?.schemaFingerprint && live.fingerprint !== state.schemaFingerprint) {
|
|
56797
|
+
throw new DeploymentStateDriftError({
|
|
56798
|
+
expectedFingerprint: state.schemaFingerprint,
|
|
56799
|
+
actualFingerprint: live.fingerprint
|
|
56800
|
+
});
|
|
56801
|
+
}
|
|
56802
|
+
if (input.lastAppliedMigrationTag && input.evidence?.length) {
|
|
56803
|
+
const [tables, lastDeploy] = await Promise.all([
|
|
56804
|
+
cf.d1.readTableColumns(databaseId, live.tables),
|
|
56805
|
+
findLastSuccessfulDeploy(this.deps.db, game2.id)
|
|
56806
|
+
]);
|
|
56807
|
+
assertBaselineClaimValid({
|
|
56808
|
+
claimedTag: input.lastAppliedMigrationTag,
|
|
56809
|
+
evidence: input.evidence,
|
|
56810
|
+
tables,
|
|
56811
|
+
indexes: new Set(live.indexes),
|
|
56812
|
+
views: new Set(live.views),
|
|
56813
|
+
lastDeployAt: lastDeploy?.at ?? null,
|
|
56814
|
+
allowUnverified: Boolean(input.allowUnverified),
|
|
56815
|
+
source: "manual",
|
|
56816
|
+
gameId: game2.id,
|
|
56817
|
+
userId: user.id
|
|
56818
|
+
});
|
|
56819
|
+
}
|
|
56820
|
+
let recordedTags = [];
|
|
56821
|
+
if (input.lastAppliedMigrationTag) {
|
|
56822
|
+
const slice = sliceJournalToTag(input.journal ?? [], input.lastAppliedMigrationTag);
|
|
56823
|
+
if (!slice) {
|
|
56824
|
+
throw new ValidationError(`lastAppliedMigrationTag '${input.lastAppliedMigrationTag}' is not in the submitted journal`);
|
|
56825
|
+
}
|
|
56826
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
56827
|
+
await cf.d1.recordLedgerRows(databaseId, {
|
|
56828
|
+
rows: slice,
|
|
56829
|
+
deployId: `baseline:${crypto.randomUUID()}`,
|
|
56830
|
+
appliedBy: user.id,
|
|
56831
|
+
source: "baseline"
|
|
56832
|
+
});
|
|
56833
|
+
recordedTags = slice.map((entry2) => entry2.tag);
|
|
56834
|
+
}
|
|
56835
|
+
const graduating = migrateOnlyClaim && isPushAdopted(state);
|
|
56836
|
+
const set = {
|
|
56837
|
+
...graduating ? { schemaHash: null, schemaSnapshot: null } : {
|
|
56838
|
+
...input.schemaHash !== undefined && { schemaHash: input.schemaHash },
|
|
56839
|
+
...input.schemaSnapshot !== undefined && {
|
|
56840
|
+
schemaSnapshot: input.schemaSnapshot
|
|
56841
|
+
}
|
|
56842
|
+
},
|
|
56843
|
+
schemaFingerprint: live.fingerprint,
|
|
56844
|
+
baselineSource: "manual-baseline",
|
|
56845
|
+
updatedAt: new Date
|
|
56846
|
+
};
|
|
56847
|
+
await this.persistDeploymentState(game2.id, set);
|
|
56848
|
+
addEvent("deployment_state.baseline_recorded", {
|
|
56849
|
+
"app.game.id": game2.id,
|
|
56850
|
+
"app.deployment_state.baseline_source": "manual-baseline",
|
|
56851
|
+
"app.deployment_state.baseline_ledger_rows": recordedTags.length,
|
|
56852
|
+
"app.deployment_state.baseline_has_snapshot": input.schemaSnapshot !== undefined,
|
|
56853
|
+
"app.deployment_state.baseline_graduated_from_push": graduating
|
|
56854
|
+
});
|
|
56855
|
+
return this.get(slug, user);
|
|
56856
|
+
}
|
|
56857
|
+
async realignMigration(slug, tag, checksum, user) {
|
|
56858
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56859
|
+
const cf = this.requireCloudflare();
|
|
56860
|
+
const databaseId = await this.resolveDatabaseId(slug, game2.id);
|
|
56861
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
56862
|
+
if (!ledger.some((row) => row.tag === tag)) {
|
|
56863
|
+
throw new NotFoundError("Applied migration", tag);
|
|
56864
|
+
}
|
|
56865
|
+
const updated = await cf.d1.updateLedgerChecksum(databaseId, { tag, checksum });
|
|
56866
|
+
if (!updated) {
|
|
56867
|
+
throw new NotFoundError("Applied migration", tag);
|
|
56868
|
+
}
|
|
56869
|
+
addEvent("deployment_state.migration_realigned", {
|
|
56870
|
+
"app.game.id": game2.id,
|
|
56871
|
+
"app.d1.migration_tag": tag,
|
|
56872
|
+
"app.user.id": user.id
|
|
56873
|
+
});
|
|
56874
|
+
return { tag, checksum, checksumAlgo: MIGRATION_CHECKSUM_ALGO };
|
|
56875
|
+
}
|
|
56876
|
+
async persistDeploymentState(gameId, set) {
|
|
56877
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
56878
|
+
}
|
|
56879
|
+
async updateDeploymentState(gameId, set) {
|
|
56880
|
+
const updated = await this.deps.db.update(gameDeploymentState).set(set).where(eq(gameDeploymentState.gameId, gameId)).returning({ gameId: gameDeploymentState.gameId });
|
|
56881
|
+
return updated.length > 0;
|
|
56882
|
+
}
|
|
56883
|
+
async resolveMigration(slug, tag, input, user) {
|
|
56884
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56885
|
+
const cf = this.requireCloudflare();
|
|
56886
|
+
const databaseId = await this.resolveDatabaseId(slug, game2.id);
|
|
56887
|
+
const [ledger, live] = await Promise.all([
|
|
56888
|
+
cf.d1.readMigrationLedger(databaseId),
|
|
56889
|
+
cf.d1.fingerprintSchema(databaseId)
|
|
56890
|
+
]);
|
|
56891
|
+
const exists2 = ledger.some((row) => row.tag === tag);
|
|
56892
|
+
if (input.resolution === "applied") {
|
|
56893
|
+
if (!input.checksum) {
|
|
56894
|
+
throw new ValidationError("Resolving a migration as 'applied' requires its checksum");
|
|
56895
|
+
}
|
|
56896
|
+
if (exists2) {
|
|
56897
|
+
throw new AlreadyExistsError(`Migration '${tag}' is already recorded as applied`);
|
|
56898
|
+
}
|
|
56899
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
56900
|
+
await cf.d1.recordLedgerRows(databaseId, {
|
|
56901
|
+
rows: [{ tag, checksum: input.checksum }],
|
|
56902
|
+
deployId: `resolve:${crypto.randomUUID()}`,
|
|
56903
|
+
appliedBy: user.id,
|
|
56904
|
+
source: "resolve"
|
|
56905
|
+
});
|
|
56906
|
+
} else {
|
|
56907
|
+
if (!exists2) {
|
|
56908
|
+
throw new NotFoundError("Migration ledger row", tag);
|
|
56909
|
+
}
|
|
56910
|
+
await cf.d1.deleteLedgerRow(databaseId, tag);
|
|
56911
|
+
}
|
|
56912
|
+
const fingerprintRecorded = await this.updateDeploymentState(game2.id, {
|
|
56913
|
+
schemaFingerprint: live.fingerprint,
|
|
56914
|
+
updatedAt: new Date
|
|
56915
|
+
});
|
|
56916
|
+
addEvent("deployment_state.migration_resolved", {
|
|
56917
|
+
"app.game.id": game2.id,
|
|
56918
|
+
"app.d1.migration_tag": tag,
|
|
56919
|
+
"app.deployment_state.resolution": input.resolution,
|
|
56920
|
+
"app.deployment_state.fingerprint_rerecorded": fingerprintRecorded,
|
|
56921
|
+
"app.user.id": user.id
|
|
56922
|
+
});
|
|
56923
|
+
return {
|
|
56924
|
+
tag,
|
|
56925
|
+
resolution: input.resolution,
|
|
56926
|
+
schemaFingerprint: live.fingerprint,
|
|
56927
|
+
fingerprintRecorded
|
|
56928
|
+
};
|
|
56929
|
+
}
|
|
56930
|
+
async history(slug, user, options = {}) {
|
|
56931
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56932
|
+
const limit = options.limit ?? 20;
|
|
56933
|
+
const [jobs, events] = await Promise.all([
|
|
56934
|
+
this.deps.db.select({
|
|
56935
|
+
status: gameDeployJobs.status,
|
|
56936
|
+
createdAt: gameDeployJobs.createdAt,
|
|
56937
|
+
completedAt: gameDeployJobs.completedAt,
|
|
56938
|
+
deployId: gameDeployJobs.deployId,
|
|
56939
|
+
error: gameDeployJobs.error,
|
|
56940
|
+
email: users.email
|
|
56941
|
+
}).from(gameDeployJobs).leftJoin(users, eq(gameDeployJobs.userId, users.id)).where(eq(gameDeployJobs.gameId, game2.id)).orderBy(desc(deployJobInstant())).limit(limit),
|
|
56942
|
+
this.deps.db.select({
|
|
56943
|
+
kind: gameDeployEvents.kind,
|
|
56944
|
+
payload: gameDeployEvents.payload,
|
|
56945
|
+
createdAt: gameDeployEvents.createdAt,
|
|
56946
|
+
email: users.email
|
|
56947
|
+
}).from(gameDeployEvents).leftJoin(users, eq(gameDeployEvents.userId, users.id)).where(eq(gameDeployEvents.gameId, game2.id)).orderBy(desc(gameDeployEvents.createdAt)).limit(limit)
|
|
56948
|
+
]);
|
|
56949
|
+
setAttributes({
|
|
56950
|
+
"app.deployment_state.history_jobs": jobs.length,
|
|
56951
|
+
"app.deployment_state.history_events": events.length
|
|
56952
|
+
});
|
|
56953
|
+
const deploys = jobs.map((job) => ({
|
|
56954
|
+
kind: "deploy",
|
|
56955
|
+
status: job.status,
|
|
56956
|
+
at: (job.completedAt ?? job.createdAt).toISOString(),
|
|
56957
|
+
completedAt: job.completedAt?.toISOString() ?? null,
|
|
56958
|
+
by: job.email ?? DELETED_ACCOUNT_LABEL,
|
|
56959
|
+
deployId: job.deployId,
|
|
56960
|
+
error: job.error
|
|
56961
|
+
}));
|
|
56962
|
+
const merged = [...deploys, ...events.flatMap(historyEventEntry)].toSorted((a, b) => a.at < b.at ? 1 : -1).slice(0, limit);
|
|
56963
|
+
return { deploys: merged };
|
|
56964
|
+
}
|
|
56965
|
+
async restorePoints(slug, user) {
|
|
56966
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56967
|
+
const retentionCutoff = new Date(Date.now() - D1_TIME_TRAVEL_RETENTION_DAYS * 24 * 60 * 60 * 1000);
|
|
56968
|
+
const [currentDatabaseId, rows, state] = await Promise.all([
|
|
56969
|
+
this.findDatabaseId(slug, game2.id),
|
|
56970
|
+
this.deps.db.query.gameDeployments.findMany({
|
|
56971
|
+
where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "game"), isNotNull(gameDeployments.timeTravelBookmark), gte(gameDeployments.deployedAt, retentionCutoff)),
|
|
56972
|
+
orderBy: desc(gameDeployments.deployedAt),
|
|
56973
|
+
limit: 100,
|
|
56974
|
+
columns: {
|
|
56975
|
+
id: true,
|
|
56976
|
+
deployedAt: true,
|
|
56977
|
+
bookmarkCapturedAt: true,
|
|
56978
|
+
isActive: true,
|
|
56979
|
+
resources: true
|
|
56980
|
+
}
|
|
56981
|
+
}),
|
|
56982
|
+
this.findSchemaHashState(game2.id)
|
|
56983
|
+
]);
|
|
56984
|
+
if (isPushAdopted(state)) {
|
|
56985
|
+
return { restorePoints: [], restoreUnsupported: true };
|
|
56986
|
+
}
|
|
56987
|
+
if (!currentDatabaseId) {
|
|
56988
|
+
return { restorePoints: [], restoreUnsupported: false };
|
|
56989
|
+
}
|
|
56990
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56991
|
+
const restorable = rows.filter((row) => databaseIdFromResources(row.resources, deploymentId) === currentDatabaseId && restorePointCapturedAt(row) >= retentionCutoff).slice(0, 20);
|
|
56992
|
+
setAttributes({ "app.deployment_state.restore_points": restorable.length });
|
|
56993
|
+
return {
|
|
56994
|
+
restorePoints: restorable.map((row) => ({
|
|
56995
|
+
id: row.id,
|
|
56996
|
+
capturedAt: restorePointCapturedAt(row).toISOString(),
|
|
56997
|
+
active: row.isActive
|
|
56998
|
+
})),
|
|
56999
|
+
restoreUnsupported: false
|
|
57000
|
+
};
|
|
57001
|
+
}
|
|
57002
|
+
async restoreToBookmark(slug, input, user) {
|
|
57003
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57004
|
+
const cf = this.requireCloudflare();
|
|
57005
|
+
const [row, state, currentDatabaseId, runningJob] = await Promise.all([
|
|
57006
|
+
this.deps.db.query.gameDeployments.findFirst({
|
|
57007
|
+
where: and(eq(gameDeployments.id, input.restorePointId), eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "game")),
|
|
57008
|
+
columns: {
|
|
57009
|
+
id: true,
|
|
57010
|
+
timeTravelBookmark: true,
|
|
57011
|
+
deployedAt: true,
|
|
57012
|
+
bookmarkCapturedAt: true,
|
|
57013
|
+
resources: true
|
|
57014
|
+
}
|
|
57015
|
+
}),
|
|
57016
|
+
this.findSchemaHashState(game2.id),
|
|
57017
|
+
this.findDatabaseId(slug, game2.id),
|
|
57018
|
+
this.deps.db.query.gameDeployJobs.findFirst({
|
|
57019
|
+
where: and(eq(gameDeployJobs.gameId, game2.id), or(eq(gameDeployJobs.status, "pending"), and(eq(gameDeployJobs.status, "running"), gt(gameDeployJobs.leaseExpiresAt, new Date)))),
|
|
57020
|
+
columns: { id: true }
|
|
57021
|
+
})
|
|
57022
|
+
]);
|
|
57023
|
+
if (!row?.timeTravelBookmark) {
|
|
57024
|
+
throw new NotFoundError("Bookmark", input.restorePointId);
|
|
57025
|
+
}
|
|
57026
|
+
if (isPushAdopted(state)) {
|
|
57027
|
+
throw new ValidationError("Restore is not supported for push-mode projects: rewinding the database would leave future push deploys diffing against the wrong schema.", { code: DEPLOY_ERROR_CODES.restoreUnsupported });
|
|
57028
|
+
}
|
|
57029
|
+
if (!currentDatabaseId) {
|
|
57030
|
+
throw new ValidationError("Game has no deployed database — deploy with a database binding first");
|
|
57031
|
+
}
|
|
57032
|
+
const databaseId = currentDatabaseId;
|
|
57033
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
57034
|
+
if (databaseIdFromResources(row.resources, deploymentId) !== databaseId) {
|
|
57035
|
+
throw new ValidationError("This bookmark was captured on a previous database (a reset replaced it since) and can no longer be restored");
|
|
57036
|
+
}
|
|
57037
|
+
const retentionCutoff = new Date(Date.now() - D1_TIME_TRAVEL_RETENTION_DAYS * 24 * 60 * 60 * 1000);
|
|
57038
|
+
if (restorePointCapturedAt(row) < retentionCutoff) {
|
|
57039
|
+
throw new ValidationError(`This bookmark is older than Time Travel's ${D1_TIME_TRAVEL_RETENTION_DAYS}-day retention window and can no longer be restored.`, { code: DEPLOY_ERROR_CODES.restoreExpired });
|
|
57040
|
+
}
|
|
57041
|
+
if (runningJob) {
|
|
57042
|
+
throw new ValidationError("A deploy is currently running for this game. Wait for it to finish, then restore.", { code: DEPLOY_ERROR_CODES.restoreBlockedByDeploy });
|
|
57043
|
+
}
|
|
57044
|
+
const restored = await cf.d1.restoreBookmark(databaseId, row.timeTravelBookmark);
|
|
57045
|
+
const restoredTo = restorePointCapturedAt(row).toISOString();
|
|
57046
|
+
let live;
|
|
57047
|
+
let fingerprintRecorded;
|
|
57048
|
+
try {
|
|
57049
|
+
live = await cf.d1.fingerprintSchema(databaseId);
|
|
57050
|
+
fingerprintRecorded = await this.updateDeploymentState(game2.id, {
|
|
57051
|
+
schemaFingerprint: live.fingerprint,
|
|
57052
|
+
updatedAt: new Date
|
|
57053
|
+
});
|
|
57054
|
+
await this.deps.db.insert(gameDeployEvents).values({
|
|
57055
|
+
gameId: game2.id,
|
|
57056
|
+
userId: user.id,
|
|
57057
|
+
kind: "restore",
|
|
57058
|
+
payload: { restoredTo, previousBookmark: restored.previousBookmark }
|
|
57059
|
+
});
|
|
57060
|
+
} catch (error) {
|
|
57061
|
+
throw new ValidationError(`The database WAS restored, but re-recording its schema fingerprint failed (${errorMessage2(error)}). Run the same restore again to complete it.`, { code: DEPLOY_ERROR_CODES.restoreIncomplete });
|
|
57062
|
+
}
|
|
57063
|
+
addEvent("deployment_state.bookmark_restored", {
|
|
57064
|
+
"app.game.id": game2.id,
|
|
57065
|
+
"app.deployment_state.restore_point": row.id,
|
|
57066
|
+
"app.deployment_state.fingerprint_rerecorded": fingerprintRecorded,
|
|
57067
|
+
"app.user.id": user.id
|
|
57068
|
+
});
|
|
57069
|
+
return {
|
|
57070
|
+
restorePointId: row.id,
|
|
57071
|
+
restoredTo,
|
|
57072
|
+
schemaFingerprint: live.fingerprint,
|
|
57073
|
+
fingerprintRecorded,
|
|
57074
|
+
previousBookmark: restored.previousBookmark
|
|
57075
|
+
};
|
|
57076
|
+
}
|
|
57077
|
+
async reportDeployBlocked(slug, user, report) {
|
|
57078
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57079
|
+
const recent = await this.deps.db.query.gameDeployEvents.findMany({
|
|
57080
|
+
where: and(eq(gameDeployEvents.gameId, game2.id), eq(gameDeployEvents.kind, "blocked"), gt(gameDeployEvents.createdAt, new Date(Date.now() - BLOCKED_ALERT_DEDUP_MS))),
|
|
57081
|
+
orderBy: desc(gameDeployEvents.createdAt),
|
|
57082
|
+
limit: 10,
|
|
57083
|
+
columns: { payload: true }
|
|
57084
|
+
});
|
|
57085
|
+
const duplicate = recent.some((event) => ("code" in event.payload) && event.payload.code === report.code);
|
|
57086
|
+
await this.deps.db.insert(gameDeployEvents).values({
|
|
57087
|
+
gameId: game2.id,
|
|
57088
|
+
userId: user.id,
|
|
57089
|
+
kind: "blocked",
|
|
57090
|
+
payload: { code: report.code, reason: report.reason }
|
|
57091
|
+
});
|
|
57092
|
+
addEvent("deployment_state.deploy_blocked", {
|
|
57093
|
+
"app.game.id": game2.id,
|
|
57094
|
+
"app.deploy.blocked_code": report.code,
|
|
57095
|
+
"app.user.id": user.id,
|
|
57096
|
+
"app.alerts.deduped": duplicate
|
|
57097
|
+
});
|
|
57098
|
+
if (!duplicate) {
|
|
57099
|
+
await this.deps.alerts.notifyDeployBlocked({
|
|
57100
|
+
slug,
|
|
57101
|
+
displayName: game2.displayName,
|
|
57102
|
+
reason: report.reason,
|
|
57103
|
+
developer: { id: user.id, email: user.email ?? null }
|
|
57104
|
+
});
|
|
57105
|
+
}
|
|
57106
|
+
}
|
|
57107
|
+
}
|
|
57108
|
+
var BLOCKED_ALERT_DEDUP_MS;
|
|
57109
|
+
var init_deployment_state_service = __esm(() => {
|
|
57110
|
+
init_drizzle_orm();
|
|
57111
|
+
init_src4();
|
|
57112
|
+
init_src();
|
|
57113
|
+
init_helpers_index();
|
|
57114
|
+
init_tables_index();
|
|
57115
|
+
init_spans();
|
|
57116
|
+
init_game2();
|
|
57117
|
+
init_errors();
|
|
57118
|
+
init_baseline_validation_util();
|
|
57119
|
+
init_deployment_util();
|
|
57120
|
+
init_migration_util();
|
|
57121
|
+
init_secrets_util();
|
|
57122
|
+
BLOCKED_ALERT_DEDUP_MS = 600000;
|
|
57123
|
+
});
|
|
54994
57124
|
|
|
54995
57125
|
class DomainService {
|
|
54996
57126
|
deps;
|
|
@@ -55355,6 +57485,7 @@ var init_kv_service = __esm(() => {
|
|
|
55355
57485
|
|
|
55356
57486
|
class SecretsService {
|
|
55357
57487
|
deps;
|
|
57488
|
+
pepperPromise = null;
|
|
55358
57489
|
constructor(deps) {
|
|
55359
57490
|
this.deps = deps;
|
|
55360
57491
|
}
|
|
@@ -55367,36 +57498,69 @@ class SecretsService {
|
|
|
55367
57498
|
getGameDeploymentId(slug) {
|
|
55368
57499
|
return getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
55369
57500
|
}
|
|
55370
|
-
|
|
55371
|
-
|
|
55372
|
-
|
|
55373
|
-
|
|
55374
|
-
|
|
55375
|
-
|
|
55376
|
-
|
|
55377
|
-
|
|
55378
|
-
|
|
55379
|
-
|
|
55380
|
-
|
|
55381
|
-
|
|
55382
|
-
|
|
55383
|
-
}
|
|
55384
|
-
|
|
55385
|
-
|
|
57501
|
+
getPepper() {
|
|
57502
|
+
const pepperSecret = this.deps.config.secretsManifestPepper;
|
|
57503
|
+
if (!pepperSecret) {
|
|
57504
|
+
throw new ValidationError("Secrets manifest is not configured (missing manifest pepper secret)");
|
|
57505
|
+
}
|
|
57506
|
+
this.pepperPromise ??= deriveSecretsManifestPepper(pepperSecret);
|
|
57507
|
+
return this.pepperPromise;
|
|
57508
|
+
}
|
|
57509
|
+
async computeManifestEntries(gameId, secrets) {
|
|
57510
|
+
const pepper = await this.getPepper();
|
|
57511
|
+
const entries = {};
|
|
57512
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
57513
|
+
entries[key] = await computeSecretDigest(pepper, { gameId, key, value });
|
|
57514
|
+
}
|
|
57515
|
+
return entries;
|
|
57516
|
+
}
|
|
57517
|
+
async readManifest(gameId) {
|
|
57518
|
+
const state = await this.deps.db.query.gameDeploymentState.findFirst({
|
|
57519
|
+
where: eq(gameDeploymentState.gameId, gameId),
|
|
57520
|
+
columns: { secretsManifest: true }
|
|
57521
|
+
});
|
|
57522
|
+
return state?.secretsManifest ?? {};
|
|
57523
|
+
}
|
|
57524
|
+
async upsertManifestEntries(gameId, entries) {
|
|
57525
|
+
const merged = sql`coalesce(${gameDeploymentState.secretsManifest}, '{}'::jsonb) || ${JSON.stringify(entries)}::jsonb`;
|
|
57526
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, secretsManifest: entries, updatedAt: new Date }).onConflictDoUpdate({
|
|
57527
|
+
target: gameDeploymentState.gameId,
|
|
57528
|
+
set: { secretsManifest: merged, updatedAt: new Date }
|
|
57529
|
+
});
|
|
57530
|
+
}
|
|
57531
|
+
async removeManifestKey(gameId, key) {
|
|
57532
|
+
await this.deps.db.update(gameDeploymentState).set({
|
|
57533
|
+
secretsManifest: sql`coalesce(${gameDeploymentState.secretsManifest}, '{}'::jsonb) - ${key}::text`,
|
|
57534
|
+
updatedAt: new Date
|
|
57535
|
+
}).where(eq(gameDeploymentState.gameId, gameId));
|
|
57536
|
+
}
|
|
57537
|
+
assertNoReservedKeys(keys, operation) {
|
|
57538
|
+
for (const key of keys) {
|
|
57539
|
+
if (INTERNAL_SECRET_KEYS.includes(key)) {
|
|
55386
57540
|
setAttributes({
|
|
55387
|
-
"app.secrets.operation":
|
|
55388
|
-
"app.secrets.
|
|
55389
|
-
"app.secrets.game_deployed": false
|
|
57541
|
+
"app.secrets.operation": operation,
|
|
57542
|
+
"app.secrets.reserved_key_rejected": true
|
|
55390
57543
|
});
|
|
55391
|
-
|
|
57544
|
+
throw new ValidationError(operation === "set" ? `Cannot set reserved secret "${key}"` : `Reserved secret "${key}" cannot be managed — remove it locally`);
|
|
55392
57545
|
}
|
|
55393
|
-
throw error;
|
|
55394
57546
|
}
|
|
55395
57547
|
}
|
|
55396
|
-
async
|
|
57548
|
+
async listKeys(slug, user) {
|
|
55397
57549
|
await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
55398
57550
|
const cf = this.getCloudflare();
|
|
55399
57551
|
const deploymentId = this.getGameDeploymentId(slug);
|
|
57552
|
+
const userKeys = await listUserSecretKeys(cf, deploymentId);
|
|
57553
|
+
setAttributes({
|
|
57554
|
+
"app.secrets.operation": "list",
|
|
57555
|
+
"app.secrets.count": userKeys?.length ?? 0,
|
|
57556
|
+
"app.secrets.game_deployed": userKeys !== null
|
|
57557
|
+
});
|
|
57558
|
+
return userKeys ?? [];
|
|
57559
|
+
}
|
|
57560
|
+
async setSecrets(slug, newSecrets, user) {
|
|
57561
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57562
|
+
const cf = this.getCloudflare();
|
|
57563
|
+
const deploymentId = this.getGameDeploymentId(slug);
|
|
55400
57564
|
const secretKeys = Object.keys(newSecrets);
|
|
55401
57565
|
if (secretKeys.length === 0) {
|
|
55402
57566
|
throw new ValidationError("At least one secret must be provided");
|
|
@@ -55405,14 +57569,9 @@ class SecretsService {
|
|
|
55405
57569
|
if (typeof value !== "string") {
|
|
55406
57570
|
throw new ValidationError(`Secret value for "${key}" must be a string`);
|
|
55407
57571
|
}
|
|
55408
|
-
if (INTERNAL_SECRET_KEYS.includes(key)) {
|
|
55409
|
-
setAttributes({
|
|
55410
|
-
"app.secrets.operation": "set",
|
|
55411
|
-
"app.secrets.reserved_key_rejected": true
|
|
55412
|
-
});
|
|
55413
|
-
throw new ValidationError(`Cannot set reserved secret "${key}"`);
|
|
55414
|
-
}
|
|
55415
57572
|
}
|
|
57573
|
+
this.assertNoReservedKeys(secretKeys, "set");
|
|
57574
|
+
const manifestEntries = await this.computeManifestEntries(game2.id, newSecrets);
|
|
55416
57575
|
try {
|
|
55417
57576
|
const prefixedSecrets = {};
|
|
55418
57577
|
for (const [key, value] of Object.entries(newSecrets)) {
|
|
@@ -55425,8 +57584,6 @@ class SecretsService {
|
|
|
55425
57584
|
"app.secrets.game_deployed": true,
|
|
55426
57585
|
"app.secrets.reserved_key_rejected": false
|
|
55427
57586
|
});
|
|
55428
|
-
const allKeys = await cf.listSecrets(deploymentId);
|
|
55429
|
-
return allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
|
|
55430
57587
|
} catch (error) {
|
|
55431
57588
|
const message = errorMessage2(error);
|
|
55432
57589
|
if (message.includes("not found") || message.includes("10007")) {
|
|
@@ -55438,6 +57595,9 @@ class SecretsService {
|
|
|
55438
57595
|
}
|
|
55439
57596
|
throw error;
|
|
55440
57597
|
}
|
|
57598
|
+
await this.upsertManifestEntries(game2.id, manifestEntries);
|
|
57599
|
+
const allKeys = await cf.listSecrets(deploymentId);
|
|
57600
|
+
return allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
|
|
55441
57601
|
}
|
|
55442
57602
|
async deleteSecret(slug, key, user) {
|
|
55443
57603
|
if (INTERNAL_SECRET_KEYS.includes(key)) {
|
|
@@ -55447,19 +57607,25 @@ class SecretsService {
|
|
|
55447
57607
|
});
|
|
55448
57608
|
throw new ValidationError(`Cannot delete reserved secret "${key}"`);
|
|
55449
57609
|
}
|
|
55450
|
-
await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57610
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
55451
57611
|
const cf = this.getCloudflare();
|
|
55452
57612
|
const deploymentId = this.getGameDeploymentId(slug);
|
|
57613
|
+
const manifest = await this.readManifest(game2.id);
|
|
57614
|
+
const managed = key in manifest;
|
|
55453
57615
|
try {
|
|
55454
57616
|
const prefixedKey = `${SECRETS_PREFIX}${key}`;
|
|
55455
57617
|
const existingKeys = await cf.listSecrets(deploymentId);
|
|
55456
|
-
|
|
57618
|
+
const onWorker = existingKeys.includes(prefixedKey);
|
|
57619
|
+
if (!onWorker && !managed) {
|
|
55457
57620
|
throw new NotFoundError("Secret", key);
|
|
55458
57621
|
}
|
|
55459
|
-
|
|
57622
|
+
if (onWorker) {
|
|
57623
|
+
await cf.deleteSecret(deploymentId, prefixedKey);
|
|
57624
|
+
}
|
|
55460
57625
|
setAttributes({
|
|
55461
57626
|
"app.secrets.operation": "delete",
|
|
55462
57627
|
"app.secrets.game_deployed": true,
|
|
57628
|
+
"app.secrets.managed": managed,
|
|
55463
57629
|
"app.secrets.reserved_key_rejected": false
|
|
55464
57630
|
});
|
|
55465
57631
|
} catch (error) {
|
|
@@ -55476,14 +57642,45 @@ class SecretsService {
|
|
|
55476
57642
|
}
|
|
55477
57643
|
throw error;
|
|
55478
57644
|
}
|
|
57645
|
+
if (managed) {
|
|
57646
|
+
await this.removeManifestKey(game2.id, key);
|
|
57647
|
+
}
|
|
57648
|
+
}
|
|
57649
|
+
async diff(slug, localSecrets, user) {
|
|
57650
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57651
|
+
const deploymentId = this.getGameDeploymentId(slug);
|
|
57652
|
+
this.assertNoReservedKeys(Object.keys(localSecrets), "diff");
|
|
57653
|
+
const [manifest, remoteKeys, localDigests] = await Promise.all([
|
|
57654
|
+
this.readManifest(game2.id),
|
|
57655
|
+
this.deps.cloudflare ? listUserSecretKeys(this.deps.cloudflare, deploymentId) : null,
|
|
57656
|
+
this.computeManifestEntries(game2.id, localSecrets)
|
|
57657
|
+
]);
|
|
57658
|
+
const verdicts = computeSecretsDiff({
|
|
57659
|
+
localDigests,
|
|
57660
|
+
manifest,
|
|
57661
|
+
remoteKeys: remoteKeys ?? []
|
|
57662
|
+
});
|
|
57663
|
+
setAttributes({
|
|
57664
|
+
"app.secrets.operation": "diff",
|
|
57665
|
+
"app.secrets.game_deployed": remoteKeys !== null,
|
|
57666
|
+
"app.secrets.diff_added": verdicts.added.length,
|
|
57667
|
+
"app.secrets.diff_changed": verdicts.changed.length,
|
|
57668
|
+
"app.secrets.diff_unchanged": verdicts.unchanged.length,
|
|
57669
|
+
"app.secrets.diff_remote_only_managed": verdicts.remoteOnlyManaged.length,
|
|
57670
|
+
"app.secrets.diff_remote_only_unmanaged": verdicts.remoteOnlyUnmanaged.length
|
|
57671
|
+
});
|
|
57672
|
+
return verdicts;
|
|
55479
57673
|
}
|
|
55480
57674
|
}
|
|
55481
57675
|
var INTERNAL_SECRET_KEYS;
|
|
55482
57676
|
var init_secrets_service = __esm(() => {
|
|
57677
|
+
init_drizzle_orm();
|
|
55483
57678
|
init_src();
|
|
57679
|
+
init_tables_index();
|
|
55484
57680
|
init_spans();
|
|
55485
57681
|
init_errors();
|
|
55486
57682
|
init_deployment_util();
|
|
57683
|
+
init_secrets_util();
|
|
55487
57684
|
INTERNAL_SECRET_KEYS = ["PLAYCADEMY_API_KEY", "GAME_ID", "PLAYCADEMY_BASE_URL"];
|
|
55488
57685
|
});
|
|
55489
57686
|
function prefixSecrets(secrets) {
|
|
@@ -56019,6 +58216,9 @@ var SINGLE_EMOJI_REGEX;
|
|
|
56019
58216
|
var init_emoji = __esm(() => {
|
|
56020
58217
|
SINGLE_EMOJI_REGEX = /^(?:(?:\p{Regional_Indicator}{2})|(?:[#*0-9]\uFE0F?\u20E3)|(?:\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?)*))$/u;
|
|
56021
58218
|
});
|
|
58219
|
+
function requestsBinding(binding) {
|
|
58220
|
+
return binding === true || Array.isArray(binding) && binding.length > 0;
|
|
58221
|
+
}
|
|
56022
58222
|
var HttpUrlSchema;
|
|
56023
58223
|
var GameEmojiSchema;
|
|
56024
58224
|
var GameMetadataRecordSchema;
|
|
@@ -56026,6 +58226,7 @@ var InsertGameSchema;
|
|
|
56026
58226
|
var UpdateGameSchema;
|
|
56027
58227
|
var InsertGameDeploymentSchema;
|
|
56028
58228
|
var InsertGameDeployJobSchema;
|
|
58229
|
+
var InsertGameDeploymentStateSchema;
|
|
56029
58230
|
var UpsertGameMetadataSchema;
|
|
56030
58231
|
var PatchGameMetadataSchema;
|
|
56031
58232
|
var AddGameMemberSchema;
|
|
@@ -56038,11 +58239,22 @@ var ALLOWED_UPLOAD_EXTENSIONS;
|
|
|
56038
58239
|
var InitiateUploadSchema;
|
|
56039
58240
|
var AddCustomHostnameSchema;
|
|
56040
58241
|
var SetSecretsRequestSchema;
|
|
58242
|
+
var SecretsDiffRequestSchema;
|
|
56041
58243
|
var SeedRequestSchema;
|
|
56042
58244
|
var SchemaInfoSchema;
|
|
56043
|
-
var DatabaseResetRequestSchema;
|
|
56044
58245
|
var VerifyTokenSchema;
|
|
56045
58246
|
var KVSeedRequestSchema;
|
|
58247
|
+
var DeployMigrationSchema;
|
|
58248
|
+
var DeployDatabaseSchema;
|
|
58249
|
+
var DatabaseResetDatabaseSchema;
|
|
58250
|
+
var DatabaseResetRequestSchema;
|
|
58251
|
+
var BaselineEvidenceSchema;
|
|
58252
|
+
var DeployBaselineSchema;
|
|
58253
|
+
var DeploymentStateBaselineSchema;
|
|
58254
|
+
var MigrationRealignSchema;
|
|
58255
|
+
var MigrationResolveSchema;
|
|
58256
|
+
var DatabaseRestoreSchema;
|
|
58257
|
+
var DeployBlockedReportSchema;
|
|
56046
58258
|
var DeployRequestSchema;
|
|
56047
58259
|
var init_schemas2 = __esm(() => {
|
|
56048
58260
|
init_drizzle_zod();
|
|
@@ -56122,6 +58334,9 @@ var init_schemas2 = __esm(() => {
|
|
|
56122
58334
|
InsertGameDeployJobSchema = createInsertSchema(gameDeployJobs, {
|
|
56123
58335
|
status: exports_external.enum(deployJobStatusEnum.enumValues)
|
|
56124
58336
|
});
|
|
58337
|
+
InsertGameDeploymentStateSchema = createInsertSchema(gameDeploymentState, {
|
|
58338
|
+
secretsManifest: exports_external.record(exports_external.string(), exports_external.string()).nullable().optional()
|
|
58339
|
+
});
|
|
56125
58340
|
UpsertGameMetadataSchema = exports_external.object({
|
|
56126
58341
|
displayName: exports_external.string().min(1),
|
|
56127
58342
|
platform: exports_external.enum(gamePlatformEnum.enumValues),
|
|
@@ -56179,6 +58394,9 @@ var init_schemas2 = __esm(() => {
|
|
|
56179
58394
|
hostname: exports_external.string().min(1).max(255)
|
|
56180
58395
|
});
|
|
56181
58396
|
SetSecretsRequestSchema = exports_external.record(exports_external.string().min(1), exports_external.string());
|
|
58397
|
+
SecretsDiffRequestSchema = exports_external.object({
|
|
58398
|
+
secrets: exports_external.record(exports_external.string().min(1), exports_external.string())
|
|
58399
|
+
});
|
|
56182
58400
|
SeedRequestSchema = exports_external.object({
|
|
56183
58401
|
code: exports_external.string().min(1, "Seed code is required"),
|
|
56184
58402
|
secrets: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
@@ -56187,9 +58405,6 @@ var init_schemas2 = __esm(() => {
|
|
|
56187
58405
|
sql: exports_external.string(),
|
|
56188
58406
|
hash: exports_external.string()
|
|
56189
58407
|
});
|
|
56190
|
-
DatabaseResetRequestSchema = exports_external.object({
|
|
56191
|
-
schema: SchemaInfoSchema.optional()
|
|
56192
|
-
});
|
|
56193
58408
|
VerifyTokenSchema = exports_external.object({
|
|
56194
58409
|
token: exports_external.string().min(1, "Token is required")
|
|
56195
58410
|
});
|
|
@@ -56201,8 +58416,119 @@ var init_schemas2 = __esm(() => {
|
|
|
56201
58416
|
metadata: exports_external.record(exports_external.unknown()).optional()
|
|
56202
58417
|
}))
|
|
56203
58418
|
});
|
|
58419
|
+
DeployMigrationSchema = exports_external.object({
|
|
58420
|
+
tag: exports_external.string().min(1),
|
|
58421
|
+
statements: exports_external.array(exports_external.string().min(1)).min(1),
|
|
58422
|
+
checksum: exports_external.string().min(1)
|
|
58423
|
+
});
|
|
58424
|
+
DeployDatabaseSchema = exports_external.discriminatedUnion("mode", [
|
|
58425
|
+
exports_external.object({
|
|
58426
|
+
mode: exports_external.literal("push"),
|
|
58427
|
+
sql: exports_external.string(),
|
|
58428
|
+
baselineHash: exports_external.string().nullable(),
|
|
58429
|
+
nextSnapshot: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
58430
|
+
nextHash: exports_external.string().min(1),
|
|
58431
|
+
acceptDataLoss: exports_external.boolean().optional()
|
|
58432
|
+
}),
|
|
58433
|
+
exports_external.object({
|
|
58434
|
+
mode: exports_external.literal("migrate"),
|
|
58435
|
+
migrations: exports_external.array(DeployMigrationSchema)
|
|
58436
|
+
})
|
|
58437
|
+
]);
|
|
58438
|
+
DatabaseResetDatabaseSchema = exports_external.discriminatedUnion("mode", [
|
|
58439
|
+
exports_external.object({
|
|
58440
|
+
mode: exports_external.literal("push"),
|
|
58441
|
+
sql: exports_external.string(),
|
|
58442
|
+
nextSnapshot: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
58443
|
+
nextHash: exports_external.string().min(1)
|
|
58444
|
+
}),
|
|
58445
|
+
exports_external.object({
|
|
58446
|
+
mode: exports_external.literal("migrate"),
|
|
58447
|
+
migrations: exports_external.array(DeployMigrationSchema)
|
|
58448
|
+
})
|
|
58449
|
+
]);
|
|
58450
|
+
DatabaseResetRequestSchema = exports_external.object({
|
|
58451
|
+
schema: SchemaInfoSchema.optional(),
|
|
58452
|
+
database: DatabaseResetDatabaseSchema.optional()
|
|
58453
|
+
}).refine((data) => !(data.schema && data.database), {
|
|
58454
|
+
message: "Send either the database payload or the legacy schema field, not both",
|
|
58455
|
+
path: ["database"]
|
|
58456
|
+
});
|
|
58457
|
+
BaselineEvidenceSchema = exports_external.array(exports_external.object({
|
|
58458
|
+
tag: exports_external.string().min(1),
|
|
58459
|
+
generatedAt: exports_external.string().min(1),
|
|
58460
|
+
createsTables: exports_external.array(exports_external.object({
|
|
58461
|
+
name: exports_external.string().min(1),
|
|
58462
|
+
columns: exports_external.array(exports_external.string())
|
|
58463
|
+
})),
|
|
58464
|
+
addsColumns: exports_external.array(exports_external.object({
|
|
58465
|
+
table: exports_external.string().min(1),
|
|
58466
|
+
column: exports_external.string().min(1)
|
|
58467
|
+
})),
|
|
58468
|
+
createsIndexes: exports_external.array(exports_external.string()),
|
|
58469
|
+
createsViews: exports_external.array(exports_external.string())
|
|
58470
|
+
})).optional();
|
|
58471
|
+
DeployBaselineSchema = exports_external.object({
|
|
58472
|
+
lastAppliedMigrationTag: exports_external.string().min(1).optional(),
|
|
58473
|
+
journal: exports_external.array(exports_external.object({
|
|
58474
|
+
tag: exports_external.string().min(1),
|
|
58475
|
+
checksum: exports_external.string().min(1)
|
|
58476
|
+
})).optional(),
|
|
58477
|
+
evidence: BaselineEvidenceSchema,
|
|
58478
|
+
schemaSnapshot: exports_external.unknown().optional(),
|
|
58479
|
+
schemaHash: exports_external.string().min(1).optional(),
|
|
58480
|
+
integrationsHash: exports_external.string().min(1).optional(),
|
|
58481
|
+
buildHash: exports_external.string().min(1).optional()
|
|
58482
|
+
}).refine((data) => !data.lastAppliedMigrationTag === !data.journal, {
|
|
58483
|
+
message: "lastAppliedMigrationTag and journal must be provided together",
|
|
58484
|
+
path: ["journal"]
|
|
58485
|
+
}).refine((data) => data.schemaSnapshot === undefined === !data.schemaHash, {
|
|
58486
|
+
message: "schemaSnapshot and schemaHash must be provided together",
|
|
58487
|
+
path: ["schemaHash"]
|
|
58488
|
+
}).refine((data) => Boolean(data.lastAppliedMigrationTag || data.schemaHash || data.integrationsHash || data.buildHash), {
|
|
58489
|
+
message: "A baseline must claim something — migration history, a schema snapshot, or artifact hashes",
|
|
58490
|
+
path: ["lastAppliedMigrationTag"]
|
|
58491
|
+
});
|
|
58492
|
+
DeploymentStateBaselineSchema = exports_external.object({
|
|
58493
|
+
lastAppliedMigrationTag: exports_external.string().min(1).optional(),
|
|
58494
|
+
journal: exports_external.array(exports_external.object({
|
|
58495
|
+
tag: exports_external.string().min(1),
|
|
58496
|
+
checksum: exports_external.string().min(1)
|
|
58497
|
+
})).optional(),
|
|
58498
|
+
schemaSnapshot: exports_external.unknown().optional(),
|
|
58499
|
+
schemaHash: exports_external.string().min(1).optional(),
|
|
58500
|
+
evidence: BaselineEvidenceSchema,
|
|
58501
|
+
allowUnverified: exports_external.boolean().optional()
|
|
58502
|
+
}).refine((data) => !data.lastAppliedMigrationTag === !data.journal, {
|
|
58503
|
+
message: "lastAppliedMigrationTag and journal must be provided together",
|
|
58504
|
+
path: ["journal"]
|
|
58505
|
+
}).refine((data) => data.schemaSnapshot === undefined === !data.schemaHash, {
|
|
58506
|
+
message: "schemaSnapshot and schemaHash must be provided together",
|
|
58507
|
+
path: ["schemaHash"]
|
|
58508
|
+
}).refine((data) => Boolean(data.lastAppliedMigrationTag) || Boolean(data.schemaHash), {
|
|
58509
|
+
message: "A baseline needs a migration claim, a schema snapshot, or both",
|
|
58510
|
+
path: ["lastAppliedMigrationTag"]
|
|
58511
|
+
});
|
|
58512
|
+
MigrationRealignSchema = exports_external.object({
|
|
58513
|
+
checksum: exports_external.string().min(1)
|
|
58514
|
+
});
|
|
58515
|
+
MigrationResolveSchema = exports_external.object({
|
|
58516
|
+
resolution: exports_external.enum(["applied", "rolled-back"]),
|
|
58517
|
+
checksum: exports_external.string().min(1).optional()
|
|
58518
|
+
}).refine((data) => data.resolution !== "applied" || Boolean(data.checksum), {
|
|
58519
|
+
message: "Resolving a migration as 'applied' requires its checksum",
|
|
58520
|
+
path: ["checksum"]
|
|
58521
|
+
});
|
|
58522
|
+
DatabaseRestoreSchema = exports_external.object({
|
|
58523
|
+
restorePointId: exports_external.string().uuid()
|
|
58524
|
+
});
|
|
58525
|
+
DeployBlockedReportSchema = exports_external.object({
|
|
58526
|
+
code: exports_external.string().regex(/^blocked:[a-z-]+$/).max(64),
|
|
58527
|
+
reason: exports_external.string().min(1).max(2000)
|
|
58528
|
+
});
|
|
56204
58529
|
DeployRequestSchema = exports_external.object({
|
|
56205
58530
|
target: exports_external.enum(deploymentTargetEnum.enumValues).optional().default("game"),
|
|
58531
|
+
deployId: exports_external.string().min(1).optional(),
|
|
56206
58532
|
uploadToken: exports_external.string().optional(),
|
|
56207
58533
|
code: exports_external.string().optional(),
|
|
56208
58534
|
codeUploadToken: exports_external.string().optional(),
|
|
@@ -56229,6 +58555,11 @@ var init_schemas2 = __esm(() => {
|
|
|
56229
58555
|
sql: exports_external.string(),
|
|
56230
58556
|
hash: exports_external.string()
|
|
56231
58557
|
}).optional(),
|
|
58558
|
+
database: DeployDatabaseSchema.optional(),
|
|
58559
|
+
baseline: DeployBaselineSchema.optional(),
|
|
58560
|
+
buildHash: exports_external.string().min(1).optional(),
|
|
58561
|
+
integrationsHash: exports_external.string().min(1).optional(),
|
|
58562
|
+
pruneSecrets: exports_external.array(exports_external.string().min(1)).optional(),
|
|
56232
58563
|
metadata: exports_external.object({
|
|
56233
58564
|
displayName: exports_external.string().optional(),
|
|
56234
58565
|
description: exports_external.string().optional(),
|
|
@@ -56238,9 +58569,24 @@ var init_schemas2 = __esm(() => {
|
|
|
56238
58569
|
}).refine((data) => !(data.code && data.codeUploadToken), {
|
|
56239
58570
|
message: "Specify either code or codeUploadToken, not both",
|
|
56240
58571
|
path: ["codeUploadToken"]
|
|
56241
|
-
}).refine((data) => !(data.target === "dashboard" && (data.schema || data.bindings)), {
|
|
56242
|
-
message: "Dashboard deployments cannot include schema or bindings — they attach to the game deployment’s existing resources",
|
|
58572
|
+
}).refine((data) => !(data.target === "dashboard" && (data.schema || data.bindings || data.database)), {
|
|
58573
|
+
message: "Dashboard deployments cannot include schema, database, or bindings — they attach to the game deployment’s existing resources",
|
|
56243
58574
|
path: ["target"]
|
|
58575
|
+
}).refine((data) => !(data.target === "dashboard" && (data.baseline || data.pruneSecrets)), {
|
|
58576
|
+
message: "Dashboard deployments cannot adopt a baseline or prune secrets",
|
|
58577
|
+
path: ["target"]
|
|
58578
|
+
}).refine((data) => !(data.database && !data.deployId), {
|
|
58579
|
+
message: "deployId is required when a database payload is present",
|
|
58580
|
+
path: ["deployId"]
|
|
58581
|
+
}).refine((data) => !(data.database && data.schema), {
|
|
58582
|
+
message: "Send either the database payload or the legacy schema field, not both",
|
|
58583
|
+
path: ["database"]
|
|
58584
|
+
}).refine((data) => !(data.baseline && data.schema), {
|
|
58585
|
+
message: "The legacy schema field cannot ride a baseline-adopting deploy",
|
|
58586
|
+
path: ["baseline"]
|
|
58587
|
+
}).refine((data) => !(data.database && !requestsBinding(data.bindings?.database)), {
|
|
58588
|
+
message: "The database payload requires a database binding",
|
|
58589
|
+
path: ["database"]
|
|
56244
58590
|
});
|
|
56245
58591
|
});
|
|
56246
58592
|
var LeaderboardQuerySchema;
|
|
@@ -101209,7 +103555,7 @@ var init_pure = __esm(() => {
|
|
|
101209
103555
|
init_log();
|
|
101210
103556
|
init_spinner();
|
|
101211
103557
|
});
|
|
101212
|
-
var
|
|
103558
|
+
var init_src5 = __esm(() => {
|
|
101213
103559
|
init_pure();
|
|
101214
103560
|
});
|
|
101215
103561
|
function createOneRosterUrls(baseUrl) {
|
|
@@ -103185,7 +105531,7 @@ var init_dist5 = __esm(async () => {
|
|
|
103185
105531
|
init_spans();
|
|
103186
105532
|
init_src();
|
|
103187
105533
|
init_spans();
|
|
103188
|
-
|
|
105534
|
+
init_src5();
|
|
103189
105535
|
init_src();
|
|
103190
105536
|
init_spans();
|
|
103191
105537
|
init_spans();
|
|
@@ -103760,7 +106106,7 @@ function selectTimebackMetricDiscrepancyQueueItems(candidates, options) {
|
|
|
103760
106106
|
}
|
|
103761
106107
|
var DATE_INPUT_RE;
|
|
103762
106108
|
var init_timeback_discrepancy_queue_util = __esm(() => {
|
|
103763
|
-
|
|
106109
|
+
init_src5();
|
|
103764
106110
|
init_timeback_util();
|
|
103765
106111
|
DATE_INPUT_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
103766
106112
|
});
|
|
@@ -106273,7 +108619,7 @@ var init_timeback_admin_service = __esm(async () => {
|
|
|
106273
108619
|
init_constants3();
|
|
106274
108620
|
init_types2();
|
|
106275
108621
|
init_utils6();
|
|
106276
|
-
|
|
108622
|
+
init_src5();
|
|
106277
108623
|
init_timeback3();
|
|
106278
108624
|
init_errors();
|
|
106279
108625
|
init_timeback_admin_metrics_util();
|
|
@@ -108707,8 +111053,15 @@ function createPlatformServices(deps) {
|
|
|
108707
111053
|
validateDeveloperAccess
|
|
108708
111054
|
});
|
|
108709
111055
|
const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
108710
|
-
const secrets = new SecretsService({ config: config4, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
111056
|
+
const secrets = new SecretsService({ db: db2, config: config4, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
108711
111057
|
const domain3 = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
|
|
111058
|
+
const deploymentState = new DeploymentStateService({
|
|
111059
|
+
db: db2,
|
|
111060
|
+
config: config4,
|
|
111061
|
+
cloudflare: cloudflare2,
|
|
111062
|
+
alerts,
|
|
111063
|
+
validateDeveloperAccessBySlug
|
|
111064
|
+
});
|
|
108712
111065
|
const database = new DatabaseService({
|
|
108713
111066
|
db: db2,
|
|
108714
111067
|
config: config4,
|
|
@@ -108747,6 +111100,7 @@ function createPlatformServices(deps) {
|
|
|
108747
111100
|
kv,
|
|
108748
111101
|
secrets,
|
|
108749
111102
|
domain: domain3,
|
|
111103
|
+
deploymentState,
|
|
108750
111104
|
database,
|
|
108751
111105
|
seed,
|
|
108752
111106
|
timeback: timeback2,
|
|
@@ -108757,6 +111111,7 @@ function createPlatformServices(deps) {
|
|
|
108757
111111
|
var init_platform2 = __esm(async () => {
|
|
108758
111112
|
init_bucket_service();
|
|
108759
111113
|
init_database_service();
|
|
111114
|
+
init_deployment_state_service();
|
|
108760
111115
|
init_domain_service();
|
|
108761
111116
|
init_kv_service();
|
|
108762
111117
|
init_secrets_service();
|
|
@@ -109474,7 +111829,7 @@ function createServices(ctx) {
|
|
|
109474
111829
|
};
|
|
109475
111830
|
}
|
|
109476
111831
|
var init_factory = __esm(async () => {
|
|
109477
|
-
|
|
111832
|
+
init_game3();
|
|
109478
111833
|
init_infra2();
|
|
109479
111834
|
init_player();
|
|
109480
111835
|
init_standalone();
|
|
@@ -109767,6 +112122,7 @@ function buildConfig(options) {
|
|
|
109767
112122
|
gameDomain: "localhost",
|
|
109768
112123
|
uploadBucket: "sandbox-uploads",
|
|
109769
112124
|
ltiTestMode: true,
|
|
112125
|
+
secretsManifestPepper: "sandbox-secrets-manifest-pepper",
|
|
109770
112126
|
...options.config
|
|
109771
112127
|
});
|
|
109772
112128
|
}
|
|
@@ -127811,7 +130167,7 @@ var _a27;
|
|
|
127811
130167
|
var _b12;
|
|
127812
130168
|
var _c2;
|
|
127813
130169
|
var View3;
|
|
127814
|
-
var
|
|
130170
|
+
var init_sql4;
|
|
127815
130171
|
var _a28;
|
|
127816
130172
|
var ColumnAliasProxyHandler2;
|
|
127817
130173
|
var _a29;
|
|
@@ -128304,7 +130660,7 @@ var PgSequence;
|
|
|
128304
130660
|
var init_sequence2;
|
|
128305
130661
|
var _a164;
|
|
128306
130662
|
var PgSchema5;
|
|
128307
|
-
var
|
|
130663
|
+
var init_schema4;
|
|
128308
130664
|
var _a165;
|
|
128309
130665
|
var Cache;
|
|
128310
130666
|
var _a166;
|
|
@@ -149783,7 +152139,7 @@ globstar while`, file3, fr, pattern, pr2, swallowee);
|
|
|
149783
152139
|
});
|
|
149784
152140
|
}
|
|
149785
152141
|
});
|
|
149786
|
-
|
|
152142
|
+
init_sql4 = __esm2({
|
|
149787
152143
|
"../drizzle-orm/dist/sql/sql.js"() {
|
|
149788
152144
|
init_entity2();
|
|
149789
152145
|
init_enum2();
|
|
@@ -150136,7 +152492,7 @@ globstar while`, file3, fr, pattern, pr2, swallowee);
|
|
|
150136
152492
|
"../drizzle-orm/dist/alias.js"() {
|
|
150137
152493
|
init_column2();
|
|
150138
152494
|
init_entity2();
|
|
150139
|
-
|
|
152495
|
+
init_sql4();
|
|
150140
152496
|
init_table8();
|
|
150141
152497
|
init_view_common3();
|
|
150142
152498
|
_a28 = entityKind2;
|
|
@@ -150310,7 +152666,7 @@ params: ${params}`);
|
|
|
150310
152666
|
"../drizzle-orm/dist/utils.js"() {
|
|
150311
152667
|
init_column2();
|
|
150312
152668
|
init_entity2();
|
|
150313
|
-
|
|
152669
|
+
init_sql4();
|
|
150314
152670
|
init_subquery2();
|
|
150315
152671
|
init_table8();
|
|
150316
152672
|
init_view_common3();
|
|
@@ -150569,7 +152925,7 @@ params: ${params}`);
|
|
|
150569
152925
|
init_date_common2 = __esm2({
|
|
150570
152926
|
"../drizzle-orm/dist/pg-core/columns/date.common.js"() {
|
|
150571
152927
|
init_entity2();
|
|
150572
|
-
|
|
152928
|
+
init_sql4();
|
|
150573
152929
|
init_common22();
|
|
150574
152930
|
PgDateColumnBaseBuilder2 = class extends (_b33 = PgColumnBuilder2, _a54 = entityKind2, _b33) {
|
|
150575
152931
|
defaultNow() {
|
|
@@ -151354,7 +153710,7 @@ params: ${params}`);
|
|
|
151354
153710
|
init_uuid3 = __esm2({
|
|
151355
153711
|
"../drizzle-orm/dist/pg-core/columns/uuid.js"() {
|
|
151356
153712
|
init_entity2();
|
|
151357
|
-
|
|
153713
|
+
init_sql4();
|
|
151358
153714
|
init_common22();
|
|
151359
153715
|
PgUUIDBuilder2 = class extends (_b88 = PgColumnBuilder2, _a109 = entityKind2, _b88) {
|
|
151360
153716
|
constructor(name22) {
|
|
@@ -151625,7 +153981,7 @@ params: ${params}`);
|
|
|
151625
153981
|
init_column2();
|
|
151626
153982
|
init_entity2();
|
|
151627
153983
|
init_table8();
|
|
151628
|
-
|
|
153984
|
+
init_sql4();
|
|
151629
153985
|
eq2 = (left, right) => {
|
|
151630
153986
|
return sql4`${left} = ${bindIfParam2(right, left)}`;
|
|
151631
153987
|
};
|
|
@@ -151648,7 +154004,7 @@ params: ${params}`);
|
|
|
151648
154004
|
});
|
|
151649
154005
|
init_select3 = __esm2({
|
|
151650
154006
|
"../drizzle-orm/dist/sql/expressions/select.js"() {
|
|
151651
|
-
|
|
154007
|
+
init_sql4();
|
|
151652
154008
|
}
|
|
151653
154009
|
});
|
|
151654
154010
|
init_expressions2 = __esm2({
|
|
@@ -151664,7 +154020,7 @@ params: ${params}`);
|
|
|
151664
154020
|
init_entity2();
|
|
151665
154021
|
init_primary_keys2();
|
|
151666
154022
|
init_expressions2();
|
|
151667
|
-
|
|
154023
|
+
init_sql4();
|
|
151668
154024
|
_a124 = entityKind2;
|
|
151669
154025
|
Relation2 = class {
|
|
151670
154026
|
constructor(sourceTable, referencedTable, relationName) {
|
|
@@ -151718,12 +154074,12 @@ params: ${params}`);
|
|
|
151718
154074
|
"../drizzle-orm/dist/sql/functions/aggregate.js"() {
|
|
151719
154075
|
init_column2();
|
|
151720
154076
|
init_entity2();
|
|
151721
|
-
|
|
154077
|
+
init_sql4();
|
|
151722
154078
|
}
|
|
151723
154079
|
});
|
|
151724
154080
|
init_vector22 = __esm2({
|
|
151725
154081
|
"../drizzle-orm/dist/sql/functions/vector.js"() {
|
|
151726
|
-
|
|
154082
|
+
init_sql4();
|
|
151727
154083
|
}
|
|
151728
154084
|
});
|
|
151729
154085
|
init_functions2 = __esm2({
|
|
@@ -151736,7 +154092,7 @@ params: ${params}`);
|
|
|
151736
154092
|
"../drizzle-orm/dist/sql/index.js"() {
|
|
151737
154093
|
init_expressions2();
|
|
151738
154094
|
init_functions2();
|
|
151739
|
-
|
|
154095
|
+
init_sql4();
|
|
151740
154096
|
}
|
|
151741
154097
|
});
|
|
151742
154098
|
dist_exports = {};
|
|
@@ -151953,7 +154309,7 @@ params: ${params}`);
|
|
|
151953
154309
|
init_alias3();
|
|
151954
154310
|
init_column2();
|
|
151955
154311
|
init_entity2();
|
|
151956
|
-
|
|
154312
|
+
init_sql4();
|
|
151957
154313
|
init_subquery2();
|
|
151958
154314
|
init_view_common3();
|
|
151959
154315
|
_a130 = entityKind2;
|
|
@@ -152012,7 +154368,7 @@ params: ${params}`);
|
|
|
152012
154368
|
});
|
|
152013
154369
|
init_indexes2 = __esm2({
|
|
152014
154370
|
"../drizzle-orm/dist/pg-core/indexes.js"() {
|
|
152015
|
-
|
|
154371
|
+
init_sql4();
|
|
152016
154372
|
init_entity2();
|
|
152017
154373
|
init_columns2();
|
|
152018
154374
|
_a131 = entityKind2;
|
|
@@ -152175,7 +154531,7 @@ params: ${params}`);
|
|
|
152175
154531
|
init_view_base2 = __esm2({
|
|
152176
154532
|
"../drizzle-orm/dist/pg-core/view-base.js"() {
|
|
152177
154533
|
init_entity2();
|
|
152178
|
-
|
|
154534
|
+
init_sql4();
|
|
152179
154535
|
PgViewBase2 = class extends (_b103 = View3, _a136 = entityKind2, _b103) {
|
|
152180
154536
|
};
|
|
152181
154537
|
__publicField(PgViewBase2, _a136, "PgViewBase");
|
|
@@ -152192,7 +154548,7 @@ params: ${params}`);
|
|
|
152192
154548
|
init_table22();
|
|
152193
154549
|
init_relations2();
|
|
152194
154550
|
init_sql22();
|
|
152195
|
-
|
|
154551
|
+
init_sql4();
|
|
152196
154552
|
init_subquery2();
|
|
152197
154553
|
init_table8();
|
|
152198
154554
|
init_utils22();
|
|
@@ -152776,7 +155132,7 @@ params: ${params}`);
|
|
|
152776
155132
|
init_query_builder3();
|
|
152777
155133
|
init_query_promise2();
|
|
152778
155134
|
init_selection_proxy2();
|
|
152779
|
-
|
|
155135
|
+
init_sql4();
|
|
152780
155136
|
init_subquery2();
|
|
152781
155137
|
init_table8();
|
|
152782
155138
|
init_tracing2();
|
|
@@ -153397,7 +155753,7 @@ params: ${params}`);
|
|
|
153397
155753
|
"../drizzle-orm/dist/pg-core/utils.js"() {
|
|
153398
155754
|
init_entity2();
|
|
153399
155755
|
init_table22();
|
|
153400
|
-
|
|
155756
|
+
init_sql4();
|
|
153401
155757
|
init_subquery2();
|
|
153402
155758
|
init_table8();
|
|
153403
155759
|
init_view_common3();
|
|
@@ -153485,7 +155841,7 @@ params: ${params}`);
|
|
|
153485
155841
|
init_entity2();
|
|
153486
155842
|
init_query_promise2();
|
|
153487
155843
|
init_selection_proxy2();
|
|
153488
|
-
|
|
155844
|
+
init_sql4();
|
|
153489
155845
|
init_table8();
|
|
153490
155846
|
init_tracing2();
|
|
153491
155847
|
init_utils22();
|
|
@@ -153679,7 +156035,7 @@ params: ${params}`);
|
|
|
153679
156035
|
init_table22();
|
|
153680
156036
|
init_query_promise2();
|
|
153681
156037
|
init_selection_proxy2();
|
|
153682
|
-
|
|
156038
|
+
init_sql4();
|
|
153683
156039
|
init_subquery2();
|
|
153684
156040
|
init_table8();
|
|
153685
156041
|
init_utils22();
|
|
@@ -153853,7 +156209,7 @@ params: ${params}`);
|
|
|
153853
156209
|
init_count2 = __esm2({
|
|
153854
156210
|
"../drizzle-orm/dist/pg-core/query-builders/count.js"() {
|
|
153855
156211
|
init_entity2();
|
|
153856
|
-
|
|
156212
|
+
init_sql4();
|
|
153857
156213
|
_PgCountBuilder = class _PgCountBuilder2 extends (_c6 = SQL2, _b116 = entityKind2, _a157 = Symbol.toStringTag, _c6) {
|
|
153858
156214
|
constructor(params) {
|
|
153859
156215
|
super(_PgCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -154021,7 +156377,7 @@ params: ${params}`);
|
|
|
154021
156377
|
init_entity2();
|
|
154022
156378
|
init_query_builders2();
|
|
154023
156379
|
init_selection_proxy2();
|
|
154024
|
-
|
|
156380
|
+
init_sql4();
|
|
154025
156381
|
init_subquery2();
|
|
154026
156382
|
init_count2();
|
|
154027
156383
|
init_query2();
|
|
@@ -154193,10 +156549,10 @@ params: ${params}`);
|
|
|
154193
156549
|
__publicField(PgSequence, _a163, "PgSequence");
|
|
154194
156550
|
}
|
|
154195
156551
|
});
|
|
154196
|
-
|
|
156552
|
+
init_schema4 = __esm2({
|
|
154197
156553
|
"../drizzle-orm/dist/pg-core/schema.js"() {
|
|
154198
156554
|
init_entity2();
|
|
154199
|
-
|
|
156555
|
+
init_sql4();
|
|
154200
156556
|
init_enum2();
|
|
154201
156557
|
init_sequence2();
|
|
154202
156558
|
init_table22();
|
|
@@ -154412,7 +156768,7 @@ params: ${params}`);
|
|
|
154412
156768
|
init_primary_keys2();
|
|
154413
156769
|
init_query_builders2();
|
|
154414
156770
|
init_roles2();
|
|
154415
|
-
|
|
156771
|
+
init_schema4();
|
|
154416
156772
|
init_sequence2();
|
|
154417
156773
|
init_session3();
|
|
154418
156774
|
init_subquery22();
|
|
@@ -156220,7 +158576,7 @@ ORDER BY
|
|
|
156220
158576
|
init_integer22 = __esm2({
|
|
156221
158577
|
"../drizzle-orm/dist/sqlite-core/columns/integer.js"() {
|
|
156222
158578
|
init_entity2();
|
|
156223
|
-
|
|
158579
|
+
init_sql4();
|
|
156224
158580
|
init_utils22();
|
|
156225
158581
|
init_common3();
|
|
156226
158582
|
SQLiteBaseIntegerBuilder = class extends (_b131 = SQLiteColumnBuilder, _a187 = entityKind2, _b131) {
|
|
@@ -156583,7 +158939,7 @@ ORDER BY
|
|
|
156583
158939
|
init_utils72 = __esm2({
|
|
156584
158940
|
"../drizzle-orm/dist/sqlite-core/utils.js"() {
|
|
156585
158941
|
init_entity2();
|
|
156586
|
-
|
|
158942
|
+
init_sql4();
|
|
156587
158943
|
init_subquery2();
|
|
156588
158944
|
init_table8();
|
|
156589
158945
|
init_view_common3();
|
|
@@ -156677,7 +159033,7 @@ ORDER BY
|
|
|
156677
159033
|
init_view_base22 = __esm2({
|
|
156678
159034
|
"../drizzle-orm/dist/sqlite-core/view-base.js"() {
|
|
156679
159035
|
init_entity2();
|
|
156680
|
-
|
|
159036
|
+
init_sql4();
|
|
156681
159037
|
SQLiteViewBase = class extends (_b153 = View3, _a214 = entityKind2, _b153) {
|
|
156682
159038
|
};
|
|
156683
159039
|
__publicField(SQLiteViewBase, _a214, "SQLiteViewBase");
|
|
@@ -156692,7 +159048,7 @@ ORDER BY
|
|
|
156692
159048
|
init_errors22();
|
|
156693
159049
|
init_relations2();
|
|
156694
159050
|
init_sql22();
|
|
156695
|
-
|
|
159051
|
+
init_sql4();
|
|
156696
159052
|
init_columns22();
|
|
156697
159053
|
init_table32();
|
|
156698
159054
|
init_subquery2();
|
|
@@ -157272,7 +159628,7 @@ ORDER BY
|
|
|
157272
159628
|
init_query_builder3();
|
|
157273
159629
|
init_query_promise2();
|
|
157274
159630
|
init_selection_proxy2();
|
|
157275
|
-
|
|
159631
|
+
init_sql4();
|
|
157276
159632
|
init_subquery2();
|
|
157277
159633
|
init_table8();
|
|
157278
159634
|
init_utils22();
|
|
@@ -157635,7 +159991,7 @@ ORDER BY
|
|
|
157635
159991
|
"../drizzle-orm/dist/sqlite-core/query-builders/insert.js"() {
|
|
157636
159992
|
init_entity2();
|
|
157637
159993
|
init_query_promise2();
|
|
157638
|
-
|
|
159994
|
+
init_sql4();
|
|
157639
159995
|
init_table32();
|
|
157640
159996
|
init_table8();
|
|
157641
159997
|
init_utils22();
|
|
@@ -157882,7 +160238,7 @@ ORDER BY
|
|
|
157882
160238
|
init_count22 = __esm2({
|
|
157883
160239
|
"../drizzle-orm/dist/sqlite-core/query-builders/count.js"() {
|
|
157884
160240
|
init_entity2();
|
|
157885
|
-
|
|
160241
|
+
init_sql4();
|
|
157886
160242
|
_SQLiteCountBuilder = class _SQLiteCountBuilder2 extends (_c8 = SQL2, _b160 = entityKind2, _a226 = Symbol.toStringTag, _c8) {
|
|
157887
160243
|
constructor(params) {
|
|
157888
160244
|
super(_SQLiteCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -158051,7 +160407,7 @@ ORDER BY
|
|
|
158051
160407
|
"../drizzle-orm/dist/sqlite-core/db.js"() {
|
|
158052
160408
|
init_entity2();
|
|
158053
160409
|
init_selection_proxy2();
|
|
158054
|
-
|
|
160410
|
+
init_sql4();
|
|
158055
160411
|
init_query_builders22();
|
|
158056
160412
|
init_subquery2();
|
|
158057
160413
|
init_count22();
|
|
@@ -160022,7 +162378,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160022
162378
|
init_date_common22 = __esm2({
|
|
160023
162379
|
"../drizzle-orm/dist/mysql-core/columns/date.common.js"() {
|
|
160024
162380
|
init_entity2();
|
|
160025
|
-
|
|
162381
|
+
init_sql4();
|
|
160026
162382
|
init_common4();
|
|
160027
162383
|
MySqlDateColumnBaseBuilder = class extends (_b223 = MySqlColumnBuilder, _a301 = entityKind2, _b223) {
|
|
160028
162384
|
defaultNow() {
|
|
@@ -160248,7 +162604,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160248
162604
|
init_count3 = __esm2({
|
|
160249
162605
|
"../drizzle-orm/dist/mysql-core/query-builders/count.js"() {
|
|
160250
162606
|
init_entity2();
|
|
160251
|
-
|
|
162607
|
+
init_sql4();
|
|
160252
162608
|
_MySqlCountBuilder = class _MySqlCountBuilder2 extends (_c9 = SQL2, _b237 = entityKind2, _a315 = Symbol.toStringTag, _c9) {
|
|
160253
162609
|
constructor(params) {
|
|
160254
162610
|
super(_MySqlCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -160510,7 +162866,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160510
162866
|
init_view_base3 = __esm2({
|
|
160511
162867
|
"../drizzle-orm/dist/mysql-core/view-base.js"() {
|
|
160512
162868
|
init_entity2();
|
|
160513
|
-
|
|
162869
|
+
init_sql4();
|
|
160514
162870
|
MySqlViewBase = class extends (_b240 = View3, _a323 = entityKind2, _b240) {
|
|
160515
162871
|
};
|
|
160516
162872
|
__publicField(MySqlViewBase, _a323, "MySqlViewBase");
|
|
@@ -160525,7 +162881,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160525
162881
|
init_errors22();
|
|
160526
162882
|
init_relations2();
|
|
160527
162883
|
init_expressions2();
|
|
160528
|
-
|
|
162884
|
+
init_sql4();
|
|
160529
162885
|
init_subquery2();
|
|
160530
162886
|
init_table8();
|
|
160531
162887
|
init_utils22();
|
|
@@ -161291,7 +163647,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
161291
163647
|
init_query_builder3();
|
|
161292
163648
|
init_query_promise2();
|
|
161293
163649
|
init_selection_proxy2();
|
|
161294
|
-
|
|
163650
|
+
init_sql4();
|
|
161295
163651
|
init_subquery2();
|
|
161296
163652
|
init_table8();
|
|
161297
163653
|
init_utils22();
|
|
@@ -161692,7 +164048,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
161692
164048
|
"../drizzle-orm/dist/mysql-core/query-builders/insert.js"() {
|
|
161693
164049
|
init_entity2();
|
|
161694
164050
|
init_query_promise2();
|
|
161695
|
-
|
|
164051
|
+
init_sql4();
|
|
161696
164052
|
init_table8();
|
|
161697
164053
|
init_utils22();
|
|
161698
164054
|
init_utils8();
|
|
@@ -161972,7 +164328,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
161972
164328
|
"../drizzle-orm/dist/mysql-core/db.js"() {
|
|
161973
164329
|
init_entity2();
|
|
161974
164330
|
init_selection_proxy2();
|
|
161975
|
-
|
|
164331
|
+
init_sql4();
|
|
161976
164332
|
init_subquery2();
|
|
161977
164333
|
init_count3();
|
|
161978
164334
|
init_query_builders3();
|
|
@@ -162201,7 +164557,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
162201
164557
|
init_cache();
|
|
162202
164558
|
init_entity2();
|
|
162203
164559
|
init_errors22();
|
|
162204
|
-
|
|
164560
|
+
init_sql4();
|
|
162205
164561
|
init_db3();
|
|
162206
164562
|
_a341 = entityKind2;
|
|
162207
164563
|
MySqlPreparedQuery = class {
|
|
@@ -164224,7 +166580,7 @@ AND
|
|
|
164224
166580
|
init_date_common3 = __esm2({
|
|
164225
166581
|
"../drizzle-orm/dist/singlestore-core/columns/date.common.js"() {
|
|
164226
166582
|
init_entity2();
|
|
164227
|
-
|
|
166583
|
+
init_sql4();
|
|
164228
166584
|
init_common5();
|
|
164229
166585
|
SingleStoreDateColumnBaseBuilder = class extends (_b302 = SingleStoreColumnBuilder, _a399 = entityKind2, _b302) {
|
|
164230
166586
|
defaultNow() {
|
|
@@ -164249,7 +166605,7 @@ AND
|
|
|
164249
166605
|
init_timestamp3 = __esm2({
|
|
164250
166606
|
"../drizzle-orm/dist/singlestore-core/columns/timestamp.js"() {
|
|
164251
166607
|
init_entity2();
|
|
164252
|
-
|
|
166608
|
+
init_sql4();
|
|
164253
166609
|
init_utils22();
|
|
164254
166610
|
init_date_common3();
|
|
164255
166611
|
SingleStoreTimestampBuilder = class extends (_b304 = SingleStoreDateColumnBaseBuilder, _a401 = entityKind2, _b304) {
|
|
@@ -164484,7 +166840,7 @@ AND
|
|
|
164484
166840
|
init_count4 = __esm2({
|
|
164485
166841
|
"../drizzle-orm/dist/singlestore-core/query-builders/count.js"() {
|
|
164486
166842
|
init_entity2();
|
|
164487
|
-
|
|
166843
|
+
init_sql4();
|
|
164488
166844
|
_SingleStoreCountBuilder = class _SingleStoreCountBuilder2 extends (_c12 = SQL2, _b318 = entityKind2, _a415 = Symbol.toStringTag, _c12) {
|
|
164489
166845
|
constructor(params) {
|
|
164490
166846
|
super(_SingleStoreCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -164654,7 +167010,7 @@ AND
|
|
|
164654
167010
|
init_utils10 = __esm2({
|
|
164655
167011
|
"../drizzle-orm/dist/singlestore-core/utils.js"() {
|
|
164656
167012
|
init_entity2();
|
|
164657
|
-
|
|
167013
|
+
init_sql4();
|
|
164658
167014
|
init_subquery2();
|
|
164659
167015
|
init_table8();
|
|
164660
167016
|
init_indexes4();
|
|
@@ -164732,7 +167088,7 @@ AND
|
|
|
164732
167088
|
"../drizzle-orm/dist/singlestore-core/query-builders/insert.js"() {
|
|
164733
167089
|
init_entity2();
|
|
164734
167090
|
init_query_promise2();
|
|
164735
|
-
|
|
167091
|
+
init_sql4();
|
|
164736
167092
|
init_table8();
|
|
164737
167093
|
init_utils22();
|
|
164738
167094
|
init_utils10();
|
|
@@ -164829,7 +167185,7 @@ AND
|
|
|
164829
167185
|
init_errors22();
|
|
164830
167186
|
init_relations2();
|
|
164831
167187
|
init_expressions2();
|
|
164832
|
-
|
|
167188
|
+
init_sql4();
|
|
164833
167189
|
init_subquery2();
|
|
164834
167190
|
init_table8();
|
|
164835
167191
|
init_utils22();
|
|
@@ -165360,7 +167716,7 @@ AND
|
|
|
165360
167716
|
init_query_builder3();
|
|
165361
167717
|
init_query_promise2();
|
|
165362
167718
|
init_selection_proxy2();
|
|
165363
|
-
|
|
167719
|
+
init_sql4();
|
|
165364
167720
|
init_subquery2();
|
|
165365
167721
|
init_table8();
|
|
165366
167722
|
init_utils22();
|
|
@@ -165818,7 +168174,7 @@ AND
|
|
|
165818
168174
|
"../drizzle-orm/dist/singlestore-core/db.js"() {
|
|
165819
168175
|
init_entity2();
|
|
165820
168176
|
init_selection_proxy2();
|
|
165821
|
-
|
|
168177
|
+
init_sql4();
|
|
165822
168178
|
init_subquery2();
|
|
165823
168179
|
init_count4();
|
|
165824
168180
|
init_query_builders4();
|
|
@@ -165932,7 +168288,7 @@ AND
|
|
|
165932
168288
|
init_cache();
|
|
165933
168289
|
init_entity2();
|
|
165934
168290
|
init_errors22();
|
|
165935
|
-
|
|
168291
|
+
init_sql4();
|
|
165936
168292
|
init_db4();
|
|
165937
168293
|
_a434 = entityKind2;
|
|
165938
168294
|
SingleStorePreparedQuery = class {
|
|
@@ -168168,7 +170524,7 @@ function requireUserId(userId) {
|
|
|
168168
170524
|
return userId;
|
|
168169
170525
|
}
|
|
168170
170526
|
var init_params_util = __esm(() => {
|
|
168171
|
-
|
|
170527
|
+
init_src5();
|
|
168172
170528
|
init_errors();
|
|
168173
170529
|
});
|
|
168174
170530
|
function formatZodError(error89) {
|
|
@@ -168218,6 +170574,7 @@ var init_utils11 = __esm(() => {
|
|
|
168218
170574
|
init_lti_util();
|
|
168219
170575
|
init_lti_provisioning();
|
|
168220
170576
|
init_params_util();
|
|
170577
|
+
init_secrets_util();
|
|
168221
170578
|
init_timeback_util();
|
|
168222
170579
|
init_validation_util();
|
|
168223
170580
|
});
|
|
@@ -168414,7 +170771,7 @@ var init_database_controller = __esm(() => {
|
|
|
168414
170771
|
throw ApiError.unprocessableEntity("Validation failed", details);
|
|
168415
170772
|
}
|
|
168416
170773
|
}
|
|
168417
|
-
return ctx.services.database.reset(slug2, ctx.user, body2
|
|
170774
|
+
return ctx.services.database.reset(slug2, ctx.user, body2);
|
|
168418
170775
|
});
|
|
168419
170776
|
database = defineControllerNames("database", {
|
|
168420
170777
|
reset
|
|
@@ -168464,6 +170821,91 @@ var init_deploy_controller = __esm(() => {
|
|
|
168464
170821
|
getJob: requireDeveloper(getJob)
|
|
168465
170822
|
});
|
|
168466
170823
|
});
|
|
170824
|
+
var get;
|
|
170825
|
+
var baseline;
|
|
170826
|
+
var realignMigration;
|
|
170827
|
+
var resolveMigration;
|
|
170828
|
+
var history;
|
|
170829
|
+
var restorePoints;
|
|
170830
|
+
var restore;
|
|
170831
|
+
var reportBlocked;
|
|
170832
|
+
var deploymentState;
|
|
170833
|
+
var init_deployment_state_controller = __esm(() => {
|
|
170834
|
+
init_schemas_index();
|
|
170835
|
+
init_errors();
|
|
170836
|
+
init_utils11();
|
|
170837
|
+
get = requireDeveloper(async (ctx) => {
|
|
170838
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170839
|
+
const include = ctx.url.searchParams.getAll("include").flatMap((value) => value.split(",")).filter(Boolean);
|
|
170840
|
+
const unsupported = include.filter((value) => value !== "schemaSnapshot");
|
|
170841
|
+
if (unsupported.length > 0) {
|
|
170842
|
+
throw ApiError.badRequest(`Unsupported include value(s): ${unsupported.join(", ")}`);
|
|
170843
|
+
}
|
|
170844
|
+
return ctx.services.deploymentState.get(slug2, ctx.user, {
|
|
170845
|
+
includeSchemaSnapshot: include.includes("schemaSnapshot")
|
|
170846
|
+
});
|
|
170847
|
+
});
|
|
170848
|
+
baseline = requireDeveloper(async (ctx) => {
|
|
170849
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170850
|
+
const body2 = await parseRequestBody(ctx.request, DeploymentStateBaselineSchema);
|
|
170851
|
+
return ctx.services.deploymentState.baseline(slug2, body2, ctx.user);
|
|
170852
|
+
});
|
|
170853
|
+
realignMigration = requireDeveloper(async (ctx) => {
|
|
170854
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170855
|
+
const tag = ctx.params.tag;
|
|
170856
|
+
if (!tag) {
|
|
170857
|
+
throw ApiError.badRequest("Missing migration tag");
|
|
170858
|
+
}
|
|
170859
|
+
const body2 = await parseRequestBody(ctx.request, MigrationRealignSchema);
|
|
170860
|
+
return ctx.services.deploymentState.realignMigration(slug2, tag, body2.checksum, ctx.user);
|
|
170861
|
+
});
|
|
170862
|
+
resolveMigration = requireDeveloper(async (ctx) => {
|
|
170863
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170864
|
+
const tag = ctx.params.tag;
|
|
170865
|
+
if (!tag) {
|
|
170866
|
+
throw ApiError.badRequest("Missing migration tag");
|
|
170867
|
+
}
|
|
170868
|
+
const body2 = await parseRequestBody(ctx.request, MigrationResolveSchema);
|
|
170869
|
+
return ctx.services.deploymentState.resolveMigration(slug2, tag, body2, ctx.user);
|
|
170870
|
+
});
|
|
170871
|
+
history = requireDeveloper(async (ctx) => {
|
|
170872
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170873
|
+
const limitParam = ctx.url.searchParams.get("limit");
|
|
170874
|
+
let limit;
|
|
170875
|
+
if (limitParam !== null) {
|
|
170876
|
+
limit = Number(limitParam);
|
|
170877
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
170878
|
+
throw ApiError.badRequest("limit must be an integer between 1 and 100");
|
|
170879
|
+
}
|
|
170880
|
+
}
|
|
170881
|
+
return ctx.services.deploymentState.history(slug2, ctx.user, { limit });
|
|
170882
|
+
});
|
|
170883
|
+
restorePoints = requireDeveloper(async (ctx) => {
|
|
170884
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170885
|
+
return ctx.services.deploymentState.restorePoints(slug2, ctx.user);
|
|
170886
|
+
});
|
|
170887
|
+
restore = requireDeveloper(async (ctx) => {
|
|
170888
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170889
|
+
const body2 = await parseRequestBody(ctx.request, DatabaseRestoreSchema);
|
|
170890
|
+
return ctx.services.deploymentState.restoreToBookmark(slug2, body2, ctx.user);
|
|
170891
|
+
});
|
|
170892
|
+
reportBlocked = requireDeveloper(async (ctx) => {
|
|
170893
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170894
|
+
const body2 = await parseRequestBody(ctx.request, DeployBlockedReportSchema);
|
|
170895
|
+
await ctx.services.deploymentState.reportDeployBlocked(slug2, ctx.user, body2);
|
|
170896
|
+
return { recorded: true };
|
|
170897
|
+
});
|
|
170898
|
+
deploymentState = defineControllerNames("deploymentState", {
|
|
170899
|
+
get,
|
|
170900
|
+
baseline,
|
|
170901
|
+
realignMigration,
|
|
170902
|
+
resolveMigration,
|
|
170903
|
+
history,
|
|
170904
|
+
restorePoints,
|
|
170905
|
+
restore,
|
|
170906
|
+
reportBlocked
|
|
170907
|
+
});
|
|
170908
|
+
});
|
|
168467
170909
|
var apply;
|
|
168468
170910
|
var getStatus;
|
|
168469
170911
|
var developer;
|
|
@@ -168953,6 +171395,7 @@ var init_lti_controller = __esm(() => {
|
|
|
168953
171395
|
var listKeys2;
|
|
168954
171396
|
var setSecrets;
|
|
168955
171397
|
var deleteSecret;
|
|
171398
|
+
var diff;
|
|
168956
171399
|
var secrets;
|
|
168957
171400
|
var init_secrets_controller = __esm(() => {
|
|
168958
171401
|
init_esm();
|
|
@@ -168999,10 +171442,19 @@ var init_secrets_controller = __esm(() => {
|
|
|
168999
171442
|
await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
|
|
169000
171443
|
return { success: true };
|
|
169001
171444
|
});
|
|
171445
|
+
diff = requireDeveloper(async (ctx) => {
|
|
171446
|
+
const slug2 = ctx.params.slug;
|
|
171447
|
+
if (!slug2) {
|
|
171448
|
+
throw ApiError.badRequest("Missing game slug");
|
|
171449
|
+
}
|
|
171450
|
+
const body2 = await parseRequestBody(ctx.request, SecretsDiffRequestSchema);
|
|
171451
|
+
return ctx.services.secrets.diff(slug2, body2.secrets, ctx.user);
|
|
171452
|
+
});
|
|
169002
171453
|
secrets = defineControllerNames("secrets", {
|
|
169003
171454
|
listKeys: listKeys2,
|
|
169004
171455
|
setSecrets,
|
|
169005
|
-
deleteSecret
|
|
171456
|
+
deleteSecret,
|
|
171457
|
+
diff
|
|
169006
171458
|
});
|
|
169007
171459
|
});
|
|
169008
171460
|
var seed2;
|
|
@@ -169126,7 +171578,7 @@ var timeback2;
|
|
|
169126
171578
|
var init_timeback_controller = __esm(() => {
|
|
169127
171579
|
init_esm();
|
|
169128
171580
|
init_schemas_index();
|
|
169129
|
-
|
|
171581
|
+
init_src5();
|
|
169130
171582
|
init_timeback3();
|
|
169131
171583
|
init_errors();
|
|
169132
171584
|
init_utils11();
|
|
@@ -169889,6 +172341,7 @@ var init_controllers = __esm(() => {
|
|
|
169889
172341
|
init_dashboard_controller();
|
|
169890
172342
|
init_database_controller();
|
|
169891
172343
|
init_deploy_controller();
|
|
172344
|
+
init_deployment_state_controller();
|
|
169892
172345
|
init_developer_controller();
|
|
169893
172346
|
init_domain_controller();
|
|
169894
172347
|
init_game_member_controller();
|
|
@@ -170409,6 +172862,21 @@ var init_deploy = __esm(async () => {
|
|
|
170409
172862
|
});
|
|
170410
172863
|
});
|
|
170411
172864
|
});
|
|
172865
|
+
var gameDeploymentStateRouter;
|
|
172866
|
+
var init_deployment_state = __esm(async () => {
|
|
172867
|
+
init_dist7();
|
|
172868
|
+
init_controllers();
|
|
172869
|
+
await init_api3();
|
|
172870
|
+
gameDeploymentStateRouter = new Hono2;
|
|
172871
|
+
gameDeploymentStateRouter.get("/:slug/deployment-state", handle2(deploymentState.get));
|
|
172872
|
+
gameDeploymentStateRouter.post("/:slug/deployment-state/baseline", handle2(deploymentState.baseline));
|
|
172873
|
+
gameDeploymentStateRouter.post("/:slug/deployment-state/migrations/:tag/realign", handle2(deploymentState.realignMigration));
|
|
172874
|
+
gameDeploymentStateRouter.post("/:slug/deployment-state/migrations/:tag/resolve", handle2(deploymentState.resolveMigration));
|
|
172875
|
+
gameDeploymentStateRouter.get("/:slug/deployments", handle2(deploymentState.history));
|
|
172876
|
+
gameDeploymentStateRouter.post("/:slug/deployments/blocked", handle2(deploymentState.reportBlocked));
|
|
172877
|
+
gameDeploymentStateRouter.get("/:slug/database/restore-points", handle2(deploymentState.restorePoints));
|
|
172878
|
+
gameDeploymentStateRouter.post("/:slug/database/restore", handle2(deploymentState.restore));
|
|
172879
|
+
});
|
|
170412
172880
|
var gameDomainsRouter;
|
|
170413
172881
|
var init_domains2 = __esm(async () => {
|
|
170414
172882
|
init_dist7();
|
|
@@ -170447,6 +172915,7 @@ var init_secrets = __esm(async () => {
|
|
|
170447
172915
|
gameSecretsRouter = new Hono2;
|
|
170448
172916
|
gameSecretsRouter.get("/:slug/secrets", handle2(secrets.listKeys));
|
|
170449
172917
|
gameSecretsRouter.post("/:slug/secrets", handle2(secrets.setSecrets));
|
|
172918
|
+
gameSecretsRouter.post("/:slug/secrets/diff", handle2(secrets.diff));
|
|
170450
172919
|
gameSecretsRouter.delete("/:slug/secrets/:key", handle2(secrets.deleteSecret));
|
|
170451
172920
|
});
|
|
170452
172921
|
var gameSeedRouter;
|
|
@@ -170641,6 +173110,7 @@ var init_games2 = __esm(async () => {
|
|
|
170641
173110
|
await __promiseAll([
|
|
170642
173111
|
init_crud(),
|
|
170643
173112
|
init_deploy(),
|
|
173113
|
+
init_deployment_state(),
|
|
170644
173114
|
init_domains2(),
|
|
170645
173115
|
init_logs(),
|
|
170646
173116
|
init_scores(),
|
|
@@ -170655,6 +173125,7 @@ var init_games2 = __esm(async () => {
|
|
|
170655
173125
|
gamesRouter.route("/", gameVerifyRouter);
|
|
170656
173126
|
gamesRouter.route("/", gameUploadsRouter);
|
|
170657
173127
|
gamesRouter.route("/", gameDeployRouter);
|
|
173128
|
+
gamesRouter.route("/", gameDeploymentStateRouter);
|
|
170658
173129
|
gamesRouter.route("/", gameDomainsRouter);
|
|
170659
173130
|
gamesRouter.route("/", gameLogsRouter);
|
|
170660
173131
|
gamesRouter.route("/", gameScoresRouter);
|
|
@@ -171586,7 +174057,7 @@ var init_domains3 = __esm3(() => {
|
|
|
171586
174057
|
});
|
|
171587
174058
|
var init_env_vars2 = () => {};
|
|
171588
174059
|
var GAME_MEMBER_ROLES2;
|
|
171589
|
-
var
|
|
174060
|
+
var init_game4 = __esm3(() => {
|
|
171590
174061
|
GAME_MEMBER_ROLES2 = {
|
|
171591
174062
|
OWNER: "owner",
|
|
171592
174063
|
COLLABORATOR: "collaborator"
|
|
@@ -171661,13 +174132,13 @@ var init_cloudflare2 = __esm3(() => {
|
|
|
171661
174132
|
LOCAL_PREFIX: "local-"
|
|
171662
174133
|
};
|
|
171663
174134
|
});
|
|
171664
|
-
var
|
|
174135
|
+
var init_src6 = __esm3(() => {
|
|
171665
174136
|
init_auth3();
|
|
171666
174137
|
init_dashboard2();
|
|
171667
174138
|
init_typescript2();
|
|
171668
174139
|
init_domains3();
|
|
171669
174140
|
init_env_vars2();
|
|
171670
|
-
|
|
174141
|
+
init_game4();
|
|
171671
174142
|
init_platform3();
|
|
171672
174143
|
init_timeback8();
|
|
171673
174144
|
init_cloudflare2();
|
|
@@ -171677,7 +174148,7 @@ var DEMO_USER_IDS2;
|
|
|
171677
174148
|
var DEMO_USERS2;
|
|
171678
174149
|
var DEMO_USER2;
|
|
171679
174150
|
var init_demo_users2 = __esm3(() => {
|
|
171680
|
-
|
|
174151
|
+
init_src6();
|
|
171681
174152
|
now2 = new Date;
|
|
171682
174153
|
DEMO_USER_IDS2 = {
|
|
171683
174154
|
player: "00000000-0000-0000-0000-000000000001",
|