@playcademy/vite-plugin 1.1.3-beta.6 → 1.1.3-beta.8
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 +2720 -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.8",
|
|
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.8",
|
|
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,374 @@ 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 expected = replayEvidence(evidence, claimedIndex);
|
|
52787
|
+
const live = { tables, indexes: indexes2, views };
|
|
52788
|
+
const verdicts = [];
|
|
52789
|
+
const unverified = [];
|
|
52790
|
+
evidence.forEach((entry2, index2) => {
|
|
52791
|
+
if (claimedIndex === -1 || index2 > claimedIndex) {
|
|
52792
|
+
verdicts.push(judgeBeyond(entry2, expected, live));
|
|
52793
|
+
return;
|
|
52794
|
+
}
|
|
52795
|
+
const judged = judgeClaimed(entry2, expected, live, lastDeployAt);
|
|
52796
|
+
verdicts.push(judged.verdict);
|
|
52797
|
+
if (judged.unverifiable) {
|
|
52798
|
+
unverified.push(judged.verdict);
|
|
52799
|
+
}
|
|
52800
|
+
});
|
|
52801
|
+
return {
|
|
52802
|
+
verdicts,
|
|
52803
|
+
contradictions: verdicts.filter((verdict) => verdict.verdict === "contradicted"),
|
|
52804
|
+
unverified,
|
|
52805
|
+
suggestedTag: suggestTag(evidence, tables)
|
|
52806
|
+
};
|
|
52079
52807
|
}
|
|
52080
|
-
|
|
52081
|
-
|
|
52082
|
-
|
|
52808
|
+
function replayEvidence(evidence, claimedIndex) {
|
|
52809
|
+
const state = { tables: new Map, indexes: new Map, views: new Map };
|
|
52810
|
+
for (let index2 = 0;index2 <= claimedIndex; index2++) {
|
|
52811
|
+
applyEvidenceEntry(state, evidence[index2]);
|
|
52812
|
+
}
|
|
52813
|
+
return state;
|
|
52814
|
+
}
|
|
52815
|
+
function applyEvidenceEntry(state, entry2) {
|
|
52816
|
+
for (const table8 of entry2.dropsTables) {
|
|
52817
|
+
state.tables.delete(table8);
|
|
52818
|
+
}
|
|
52819
|
+
for (const dropped of entry2.dropsColumns) {
|
|
52820
|
+
state.tables.get(dropped.table)?.columns.delete(dropped.column);
|
|
52821
|
+
}
|
|
52822
|
+
for (const droppedIndex of entry2.dropsIndexes) {
|
|
52823
|
+
state.indexes.delete(droppedIndex);
|
|
52824
|
+
}
|
|
52825
|
+
for (const view2 of entry2.dropsViews) {
|
|
52826
|
+
state.views.delete(view2);
|
|
52827
|
+
}
|
|
52828
|
+
for (const table8 of entry2.createsTables) {
|
|
52829
|
+
state.tables.set(table8.name, {
|
|
52830
|
+
creator: entry2.tag,
|
|
52831
|
+
columns: new Map(table8.columns.map((column2) => [column2, entry2.tag]))
|
|
52832
|
+
});
|
|
52833
|
+
}
|
|
52834
|
+
for (const added of entry2.addsColumns) {
|
|
52835
|
+
state.tables.get(added.table)?.columns.set(added.column, entry2.tag);
|
|
52836
|
+
}
|
|
52837
|
+
for (const createdIndex of entry2.createsIndexes) {
|
|
52838
|
+
state.indexes.set(createdIndex, entry2.tag);
|
|
52839
|
+
}
|
|
52840
|
+
for (const view2 of entry2.createsViews) {
|
|
52841
|
+
state.views.set(view2, entry2.tag);
|
|
52842
|
+
}
|
|
52843
|
+
}
|
|
52844
|
+
function judgeClaimed(entry2, expected, live, lastDeployAt) {
|
|
52845
|
+
let surviving = 0;
|
|
52846
|
+
for (const table8 of entry2.createsTables) {
|
|
52847
|
+
const expectation = expected.tables.get(table8.name);
|
|
52848
|
+
if (expectation?.creator === entry2.tag) {
|
|
52849
|
+
surviving++;
|
|
52850
|
+
const columns2 = live.tables.get(table8.name);
|
|
52851
|
+
if (!columns2) {
|
|
52852
|
+
return {
|
|
52853
|
+
verdict: {
|
|
52854
|
+
tag: entry2.tag,
|
|
52855
|
+
verdict: "contradicted",
|
|
52856
|
+
detail: `creates table \`${table8.name}\`, which is not in the live database`
|
|
52857
|
+
},
|
|
52858
|
+
unverifiable: false
|
|
52859
|
+
};
|
|
52860
|
+
}
|
|
52861
|
+
const missing = [...expectation.columns.entries()].filter(([, creator]) => creator === entry2.tag).map(([column2]) => column2).filter((column2) => !columns2.includes(column2));
|
|
52862
|
+
if (missing.length > 0) {
|
|
52863
|
+
return {
|
|
52864
|
+
verdict: {
|
|
52865
|
+
tag: entry2.tag,
|
|
52866
|
+
verdict: "contradicted",
|
|
52867
|
+
detail: `table \`${table8.name}\` exists but is missing column(s) ${missing.map((column2) => `\`${column2}\``).join(", ")} this migration defines`
|
|
52868
|
+
},
|
|
52869
|
+
unverifiable: false
|
|
52870
|
+
};
|
|
52871
|
+
}
|
|
52872
|
+
}
|
|
52873
|
+
}
|
|
52874
|
+
for (const added of entry2.addsColumns) {
|
|
52875
|
+
if (expected.tables.get(added.table)?.columns.get(added.column) === entry2.tag) {
|
|
52876
|
+
surviving++;
|
|
52877
|
+
const columns2 = live.tables.get(added.table);
|
|
52878
|
+
if (columns2 && !columns2.includes(added.column)) {
|
|
52879
|
+
return {
|
|
52880
|
+
verdict: {
|
|
52881
|
+
tag: entry2.tag,
|
|
52882
|
+
verdict: "contradicted",
|
|
52883
|
+
detail: `adds column \`${added.column}\` to \`${added.table}\`, which the live table does not have`
|
|
52884
|
+
},
|
|
52885
|
+
unverifiable: false
|
|
52886
|
+
};
|
|
52887
|
+
}
|
|
52888
|
+
}
|
|
52889
|
+
}
|
|
52890
|
+
for (const name2 of entry2.createsIndexes) {
|
|
52891
|
+
if (expected.indexes.get(name2) === entry2.tag && live.indexes.has(name2)) {
|
|
52892
|
+
surviving++;
|
|
52893
|
+
}
|
|
52894
|
+
}
|
|
52895
|
+
for (const name2 of entry2.createsViews) {
|
|
52896
|
+
if (expected.views.get(name2) === entry2.tag && live.views.has(name2)) {
|
|
52897
|
+
surviving++;
|
|
52898
|
+
}
|
|
52899
|
+
}
|
|
52900
|
+
if (surviving === 0) {
|
|
52901
|
+
const generated = new Date(entry2.generatedAt);
|
|
52902
|
+
if (lastDeployAt && generated > lastDeployAt) {
|
|
52903
|
+
return {
|
|
52904
|
+
verdict: {
|
|
52905
|
+
tag: entry2.tag,
|
|
52906
|
+
verdict: "no-signal",
|
|
52907
|
+
detail: `no schema footprint to verify, and it was generated after the last deploy (${lastDeployAt.toISOString()})`
|
|
52908
|
+
},
|
|
52909
|
+
unverifiable: true
|
|
52910
|
+
};
|
|
52911
|
+
}
|
|
52912
|
+
return { verdict: { tag: entry2.tag, verdict: "no-signal" }, unverifiable: false };
|
|
52913
|
+
}
|
|
52914
|
+
return { verdict: { tag: entry2.tag, verdict: "verified" }, unverifiable: false };
|
|
52915
|
+
}
|
|
52916
|
+
function judgeBeyond(entry2, expected, live) {
|
|
52917
|
+
for (const table8 of entry2.createsTables) {
|
|
52918
|
+
if (live.tables.has(table8.name) && !expected.tables.has(table8.name)) {
|
|
52919
|
+
return {
|
|
52920
|
+
tag: entry2.tag,
|
|
52921
|
+
verdict: "contradicted",
|
|
52922
|
+
detail: `is beyond the claim, but the table it creates (\`${table8.name}\`) already exists in the live database — the claim looks too old`
|
|
52923
|
+
};
|
|
52924
|
+
}
|
|
52925
|
+
}
|
|
52926
|
+
for (const added of entry2.addsColumns) {
|
|
52927
|
+
const columns2 = live.tables.get(added.table);
|
|
52928
|
+
const explained = expected.tables.get(added.table)?.columns.has(added.column);
|
|
52929
|
+
if (columns2?.includes(added.column) && !explained) {
|
|
52930
|
+
return {
|
|
52931
|
+
tag: entry2.tag,
|
|
52932
|
+
verdict: "contradicted",
|
|
52933
|
+
detail: `is beyond the claim, but the column it adds (\`${added.table}.${added.column}\`) already exists — the claim looks too old`
|
|
52934
|
+
};
|
|
52935
|
+
}
|
|
52936
|
+
}
|
|
52937
|
+
for (const index2 of entry2.createsIndexes) {
|
|
52938
|
+
if (live.indexes.has(index2) && !expected.indexes.has(index2)) {
|
|
52939
|
+
return {
|
|
52940
|
+
tag: entry2.tag,
|
|
52941
|
+
verdict: "contradicted",
|
|
52942
|
+
detail: `is beyond the claim, but the index it creates (\`${index2}\`) already exists — the claim looks too old`
|
|
52943
|
+
};
|
|
52944
|
+
}
|
|
52945
|
+
}
|
|
52946
|
+
for (const view2 of entry2.createsViews) {
|
|
52947
|
+
if (live.views.has(view2) && !expected.views.has(view2)) {
|
|
52948
|
+
return {
|
|
52949
|
+
tag: entry2.tag,
|
|
52950
|
+
verdict: "contradicted",
|
|
52951
|
+
detail: `is beyond the claim, but the view it creates (\`${view2}\`) already exists — the claim looks too old`
|
|
52952
|
+
};
|
|
52953
|
+
}
|
|
52954
|
+
}
|
|
52955
|
+
return { tag: entry2.tag, verdict: "verified" };
|
|
52956
|
+
}
|
|
52957
|
+
function suggestTag(evidence, tables) {
|
|
52958
|
+
const liveCreated = [
|
|
52959
|
+
...new Set(evidence.flatMap((entry2) => entry2.createsTables.map((table8) => table8.name)))
|
|
52960
|
+
].filter((name2) => tables.has(name2));
|
|
52961
|
+
const state = { tables: new Map, indexes: new Map, views: new Map };
|
|
52962
|
+
let best = null;
|
|
52963
|
+
for (const entry2 of evidence) {
|
|
52964
|
+
applyEvidenceEntry(state, entry2);
|
|
52965
|
+
const allPresent = [...state.tables.keys()].every((name2) => tables.has(name2));
|
|
52966
|
+
const noStray = liveCreated.every((name2) => state.tables.has(name2));
|
|
52967
|
+
if (allPresent && noStray) {
|
|
52968
|
+
best = entry2.tag;
|
|
52969
|
+
}
|
|
52970
|
+
}
|
|
52971
|
+
return best;
|
|
52972
|
+
}
|
|
52973
|
+
var init_baseline_validation_util = __esm(() => {
|
|
52974
|
+
init_spans();
|
|
52975
|
+
init_errors();
|
|
52083
52976
|
});
|
|
52084
|
-
function
|
|
52085
|
-
|
|
52086
|
-
|
|
52977
|
+
function sliceJournalToTag(journal, lastAppliedMigrationTag) {
|
|
52978
|
+
const index2 = journal.findIndex((entry2) => entry2.tag === lastAppliedMigrationTag);
|
|
52979
|
+
return index2 === -1 ? null : journal.slice(0, index2 + 1);
|
|
52980
|
+
}
|
|
52981
|
+
function evaluateBaselineGuardrails(input) {
|
|
52982
|
+
if (input.ledgerTags.length > 0) {
|
|
52983
|
+
return "ledger-not-empty";
|
|
52087
52984
|
}
|
|
52088
|
-
if (
|
|
52089
|
-
return
|
|
52985
|
+
if (input.liveTables.length === 0) {
|
|
52986
|
+
return "database-empty";
|
|
52090
52987
|
}
|
|
52091
|
-
return
|
|
52988
|
+
return "ok";
|
|
52092
52989
|
}
|
|
52093
|
-
function
|
|
52094
|
-
|
|
52990
|
+
function planMigrations(journal, ledger) {
|
|
52991
|
+
const ledgerByTag = new Map(ledger.map((row) => [row.tag, row]));
|
|
52992
|
+
const journalTags = new Set(journal.map((entry2) => entry2.tag));
|
|
52993
|
+
const pendingTags = [];
|
|
52994
|
+
const checksumMismatches = [];
|
|
52995
|
+
for (const entry2 of journal) {
|
|
52996
|
+
const applied = ledgerByTag.get(entry2.tag);
|
|
52997
|
+
if (!applied) {
|
|
52998
|
+
pendingTags.push(entry2.tag);
|
|
52999
|
+
} else {
|
|
53000
|
+
const comparable = applied.checksumAlgo === MIGRATION_CHECKSUM_ALGO;
|
|
53001
|
+
if (comparable && applied.checksum !== entry2.checksum) {
|
|
53002
|
+
checksumMismatches.push({
|
|
53003
|
+
tag: entry2.tag,
|
|
53004
|
+
ledgerChecksum: applied.checksum,
|
|
53005
|
+
journalChecksum: entry2.checksum
|
|
53006
|
+
});
|
|
53007
|
+
}
|
|
53008
|
+
}
|
|
53009
|
+
}
|
|
53010
|
+
const outOfOrderTags = findOutOfOrderTags(journal.map((entry2) => entry2.tag), new Set(ledgerByTag.keys()));
|
|
53011
|
+
const missingFromJournalTags = ledger.filter((row) => !journalTags.has(row.tag)).map((row) => row.tag);
|
|
53012
|
+
return { pendingTags, checksumMismatches, outOfOrderTags, missingFromJournalTags };
|
|
52095
53013
|
}
|
|
52096
|
-
function
|
|
52097
|
-
|
|
53014
|
+
function assessMigrationAlreadyApplied(statements, liveTables) {
|
|
53015
|
+
const created = extractCreatedObjects(statements);
|
|
53016
|
+
const present = [];
|
|
53017
|
+
const missing = [];
|
|
53018
|
+
for (const table8 of created.tables) {
|
|
53019
|
+
(liveTables.has(table8) ? present : missing).push(`table ${table8}`);
|
|
53020
|
+
}
|
|
53021
|
+
for (const added of created.columns) {
|
|
53022
|
+
const live = Boolean(liveTables.get(added.table)?.includes(added.column));
|
|
53023
|
+
(live ? present : missing).push(`column ${added.table}.${added.column}`);
|
|
53024
|
+
}
|
|
53025
|
+
if (present.length === 0) {
|
|
53026
|
+
return { verdict: "no-signal", present, missing };
|
|
53027
|
+
}
|
|
53028
|
+
return {
|
|
53029
|
+
verdict: missing.length === 0 ? "all-present" : "partial",
|
|
53030
|
+
present,
|
|
53031
|
+
missing
|
|
53032
|
+
};
|
|
52098
53033
|
}
|
|
52099
|
-
function
|
|
52100
|
-
|
|
53034
|
+
function parseMigrationFailure(events) {
|
|
53035
|
+
if (!events) {
|
|
53036
|
+
return null;
|
|
53037
|
+
}
|
|
53038
|
+
for (let i2 = events.length - 1;i2 >= 0; i2--) {
|
|
53039
|
+
const event = events[i2];
|
|
53040
|
+
const failure = event ? parseFailureEvent(event) : null;
|
|
53041
|
+
if (failure) {
|
|
53042
|
+
return failure;
|
|
53043
|
+
}
|
|
53044
|
+
}
|
|
53045
|
+
return null;
|
|
52101
53046
|
}
|
|
52102
|
-
function
|
|
52103
|
-
|
|
53047
|
+
function parseFailureEvent(event) {
|
|
53048
|
+
const details = event.details;
|
|
53049
|
+
if (!details) {
|
|
53050
|
+
return null;
|
|
53051
|
+
}
|
|
53052
|
+
const { code, tag, statementIndex, error, d1Message } = details;
|
|
53053
|
+
if (code !== DEPLOY_ERROR_CODES.migrationFailed || typeof tag !== "string") {
|
|
53054
|
+
return null;
|
|
53055
|
+
}
|
|
53056
|
+
return {
|
|
53057
|
+
tag,
|
|
53058
|
+
error: failureMessage(error, d1Message),
|
|
53059
|
+
statementIndex: typeof statementIndex === "number" ? statementIndex : null,
|
|
53060
|
+
at: event.createdAt
|
|
53061
|
+
};
|
|
52104
53062
|
}
|
|
52105
|
-
function
|
|
52106
|
-
|
|
53063
|
+
function failureMessage(error, d1Message) {
|
|
53064
|
+
if (typeof error === "string") {
|
|
53065
|
+
return error;
|
|
53066
|
+
}
|
|
53067
|
+
if (typeof d1Message === "string") {
|
|
53068
|
+
return d1Message;
|
|
53069
|
+
}
|
|
53070
|
+
return "Migration failed";
|
|
52107
53071
|
}
|
|
52108
|
-
var
|
|
52109
|
-
|
|
52110
|
-
|
|
52111
|
-
init_src();
|
|
52112
|
-
init_stages();
|
|
53072
|
+
var init_migration_util = __esm(() => {
|
|
53073
|
+
init_src4();
|
|
53074
|
+
init_errors();
|
|
52113
53075
|
});
|
|
53076
|
+
async function deriveSecretsManifestPepper(platformSecret) {
|
|
53077
|
+
const encoder = new TextEncoder;
|
|
53078
|
+
const keyMaterial = await crypto.subtle.importKey("raw", encoder.encode(platformSecret), "HKDF", false, ["deriveBits"]);
|
|
53079
|
+
const bits = await crypto.subtle.deriveBits({
|
|
53080
|
+
name: "HKDF",
|
|
53081
|
+
hash: "SHA-256",
|
|
53082
|
+
salt: new Uint8Array(32),
|
|
53083
|
+
info: encoder.encode(SECRETS_MANIFEST_HKDF_INFO)
|
|
53084
|
+
}, keyMaterial, 256);
|
|
53085
|
+
return new Uint8Array(bits);
|
|
53086
|
+
}
|
|
53087
|
+
async function computeSecretDigest(pepper, input) {
|
|
53088
|
+
const key = await crypto.subtle.importKey("raw", new Uint8Array(pepper), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
53089
|
+
const message = JSON.stringify([input.gameId, input.key, input.value]);
|
|
53090
|
+
const signature = await crypto.subtle.sign("HMAC", key, new TextEncoder().encode(message));
|
|
53091
|
+
return [...new Uint8Array(signature)].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
53092
|
+
}
|
|
53093
|
+
function computeSecretsDiff(input) {
|
|
53094
|
+
const { localDigests, manifest, remoteKeys } = input;
|
|
53095
|
+
const added = [];
|
|
53096
|
+
const changed = [];
|
|
53097
|
+
const unchanged = [];
|
|
53098
|
+
for (const [key, digest] of Object.entries(localDigests)) {
|
|
53099
|
+
const recorded = manifest[key];
|
|
53100
|
+
if (recorded === undefined) {
|
|
53101
|
+
added.push(key);
|
|
53102
|
+
} else if (recorded === digest) {
|
|
53103
|
+
unchanged.push(key);
|
|
53104
|
+
} else {
|
|
53105
|
+
changed.push(key);
|
|
53106
|
+
}
|
|
53107
|
+
}
|
|
53108
|
+
const localKeys = new Set(Object.keys(localDigests));
|
|
53109
|
+
const managedKeys = new Set(Object.keys(manifest));
|
|
53110
|
+
const remoteOnlyManaged = [...managedKeys].filter((key) => !localKeys.has(key));
|
|
53111
|
+
const remoteOnlyUnmanaged = remoteKeys.filter((key) => !managedKeys.has(key) && !localKeys.has(key));
|
|
53112
|
+
return {
|
|
53113
|
+
added: added.toSorted(),
|
|
53114
|
+
changed: changed.toSorted(),
|
|
53115
|
+
unchanged: unchanged.toSorted(),
|
|
53116
|
+
remoteOnlyManaged: remoteOnlyManaged.toSorted(),
|
|
53117
|
+
remoteOnlyUnmanaged: remoteOnlyUnmanaged.toSorted()
|
|
53118
|
+
};
|
|
53119
|
+
}
|
|
53120
|
+
function findUnmanagedPruneKeys(pruneSecrets, manifest) {
|
|
53121
|
+
const managed = new Set(Object.keys(manifest ?? {}));
|
|
53122
|
+
return pruneSecrets.filter((key) => !managed.has(key));
|
|
53123
|
+
}
|
|
53124
|
+
var SECRETS_MANIFEST_HKDF_INFO = "playcademy:secrets-manifest:v1";
|
|
52114
53125
|
async function sweepDashboardWorkerKeys(deleteApiKeysByName, slug) {
|
|
52115
53126
|
try {
|
|
52116
53127
|
await deleteApiKeysByName(getDashboardWorkerApiKeyName(slug));
|
|
@@ -52125,6 +53136,9 @@ var init_worker_keys_util = __esm(() => {
|
|
|
52125
53136
|
init_spans();
|
|
52126
53137
|
init_deployment_util();
|
|
52127
53138
|
});
|
|
53139
|
+
function hasBinding(binding) {
|
|
53140
|
+
return binding === true || Array.isArray(binding) && binding.length > 0;
|
|
53141
|
+
}
|
|
52128
53142
|
function readDashboardTheme(config2) {
|
|
52129
53143
|
const theme = config2?.dashboard?.theme;
|
|
52130
53144
|
return {
|
|
@@ -52192,7 +53206,7 @@ class DeployService {
|
|
|
52192
53206
|
data: { message: hasBackend ? "Deploying backend code" : "Deploying to platform" }
|
|
52193
53207
|
};
|
|
52194
53208
|
const keepAssets = hasBackend && !hasFrontend;
|
|
52195
|
-
const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings
|
|
53209
|
+
const deploymentOptions = this.mapGameBindingsToOptions(deploymentId, request.bindings);
|
|
52196
53210
|
const bindings = deploymentOptions?.bindings;
|
|
52197
53211
|
setAttributes({
|
|
52198
53212
|
"app.deploy.has_d1": Boolean(bindings?.d1?.length),
|
|
@@ -52201,13 +53215,49 @@ class DeployService {
|
|
|
52201
53215
|
"app.deploy.queue_count": bindings?.queues?.length ?? 0,
|
|
52202
53216
|
"app.deploy.dead_letter_queue_count": DeployService.countDeadLetterQueues(bindings)
|
|
52203
53217
|
});
|
|
52204
|
-
const activeDeployment = await
|
|
52205
|
-
|
|
52206
|
-
|
|
52207
|
-
|
|
52208
|
-
|
|
53218
|
+
const [activeDeployment, deploymentState] = await Promise.all([
|
|
53219
|
+
db2.query.gameDeployments.findFirst({
|
|
53220
|
+
where: activeDeploymentWhere(game2.id, "game"),
|
|
53221
|
+
columns: { resources: true }
|
|
53222
|
+
}),
|
|
53223
|
+
db2.query.gameDeploymentState.findFirst({
|
|
53224
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
53225
|
+
})
|
|
53226
|
+
]);
|
|
53227
|
+
if (request.pruneSecrets?.length) {
|
|
53228
|
+
const unmanaged = findUnmanagedPruneKeys(request.pruneSecrets, deploymentState?.secretsManifest);
|
|
53229
|
+
if (unmanaged.length > 0) {
|
|
53230
|
+
throw new SecretsPruneUnmanagedError(unmanaged);
|
|
53231
|
+
}
|
|
53232
|
+
}
|
|
53233
|
+
let state = deploymentState;
|
|
53234
|
+
if (request.baseline) {
|
|
53235
|
+
if (isSchemaAdopted(state)) {
|
|
53236
|
+
addEvent("deploy.baseline_skipped", {
|
|
53237
|
+
"app.deploy.baseline_source": state?.baselineSource ?? "unknown"
|
|
53238
|
+
});
|
|
53239
|
+
} else {
|
|
53240
|
+
state = yield* this.adoptClientBaseline({
|
|
53241
|
+
game: game2,
|
|
53242
|
+
request,
|
|
53243
|
+
user,
|
|
53244
|
+
deploymentId,
|
|
53245
|
+
baseline: request.baseline,
|
|
53246
|
+
existingResources: activeDeployment?.resources ?? undefined
|
|
53247
|
+
});
|
|
53248
|
+
}
|
|
53249
|
+
}
|
|
53250
|
+
if (deploymentOptions?.bindings?.d1?.length && !isSchemaAdopted(state)) {
|
|
52209
53251
|
await this.cleanupOrphanD1Databases(cf, deploymentOptions.bindings.d1, slug, game2.id);
|
|
52210
53252
|
}
|
|
53253
|
+
const databaseOutcome = yield* this.executeDatabaseStep({
|
|
53254
|
+
game: game2,
|
|
53255
|
+
request,
|
|
53256
|
+
user,
|
|
53257
|
+
deploymentId,
|
|
53258
|
+
state,
|
|
53259
|
+
existingResources: activeDeployment?.resources ?? undefined
|
|
53260
|
+
});
|
|
52211
53261
|
const result = await this.deployToCloudflare({
|
|
52212
53262
|
deploymentId,
|
|
52213
53263
|
code: request.code,
|
|
@@ -52215,6 +53265,7 @@ class DeployService {
|
|
|
52215
53265
|
tempDir,
|
|
52216
53266
|
options: {
|
|
52217
53267
|
...deploymentOptions,
|
|
53268
|
+
...databaseOutcome.legacySchema && { schema: databaseOutcome.legacySchema },
|
|
52218
53269
|
compatibilityDate: request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
|
|
52219
53270
|
compatibilityFlags: request.compatibilityFlags,
|
|
52220
53271
|
existingResources: activeDeployment?.resources ?? undefined,
|
|
@@ -52230,8 +53281,12 @@ class DeployService {
|
|
|
52230
53281
|
result,
|
|
52231
53282
|
request,
|
|
52232
53283
|
user,
|
|
52233
|
-
flags: flags2
|
|
53284
|
+
flags: flags2,
|
|
53285
|
+
database: databaseOutcome
|
|
52234
53286
|
});
|
|
53287
|
+
if (request.pruneSecrets?.length) {
|
|
53288
|
+
yield* this.pruneManagedSecretsStep(game2.id, result.deploymentId, request.pruneSecrets);
|
|
53289
|
+
}
|
|
52235
53290
|
yield { type: "complete", data: updatedGame };
|
|
52236
53291
|
}
|
|
52237
53292
|
async* deployDashboard(context2) {
|
|
@@ -52358,7 +53413,10 @@ class DeployService {
|
|
|
52358
53413
|
"app.deploy.code_size": request.code?.length ?? 0,
|
|
52359
53414
|
"app.deploy.compatibility_date": request.compatibilityDate ?? CLOUDFLARE_COMPATIBILITY_DATE,
|
|
52360
53415
|
"app.deploy.compatibility_flag_count": request.compatibilityFlags?.length ?? 0,
|
|
52361
|
-
"app.deploy.has_schema": Boolean(request.schema)
|
|
53416
|
+
"app.deploy.has_schema": Boolean(request.schema),
|
|
53417
|
+
"app.deploy.has_database_payload": Boolean(request.database),
|
|
53418
|
+
"app.deploy.has_baseline": Boolean(request.baseline),
|
|
53419
|
+
"app.deploy.prune_secret_count": request.pruneSecrets?.length ?? 0
|
|
52362
53420
|
});
|
|
52363
53421
|
if (!hasBackend && !hasFrontend && !hasMetadata) {
|
|
52364
53422
|
throw new ValidationError("Must provide at least one of: uploadToken (frontend), code (backend), or metadata");
|
|
@@ -52395,18 +53453,18 @@ class DeployService {
|
|
|
52395
53453
|
}
|
|
52396
53454
|
return "metadata_only";
|
|
52397
53455
|
}
|
|
52398
|
-
mapGameBindingsToOptions(deploymentId, bindings
|
|
52399
|
-
if (!bindings
|
|
53456
|
+
mapGameBindingsToOptions(deploymentId, bindings) {
|
|
53457
|
+
if (!bindings) {
|
|
52400
53458
|
return;
|
|
52401
53459
|
}
|
|
52402
53460
|
const workerBindings = {};
|
|
52403
|
-
if (bindings
|
|
53461
|
+
if (hasBinding(bindings.database)) {
|
|
52404
53462
|
workerBindings.d1 = [deploymentId];
|
|
52405
53463
|
}
|
|
52406
|
-
if (bindings
|
|
53464
|
+
if (hasBinding(bindings.keyValue)) {
|
|
52407
53465
|
workerBindings.kv = [deploymentId];
|
|
52408
53466
|
}
|
|
52409
|
-
if (bindings
|
|
53467
|
+
if (hasBinding(bindings.bucket)) {
|
|
52410
53468
|
workerBindings.r2 = [deploymentId];
|
|
52411
53469
|
}
|
|
52412
53470
|
if (bindings?.queues) {
|
|
@@ -52435,10 +53493,7 @@ class DeployService {
|
|
|
52435
53493
|
});
|
|
52436
53494
|
}
|
|
52437
53495
|
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
|
-
};
|
|
53496
|
+
return hasBindings ? { bindings: workerBindings } : undefined;
|
|
52442
53497
|
}
|
|
52443
53498
|
static countDeadLetterQueues(bindings) {
|
|
52444
53499
|
return bindings?.queues?.filter((queue) => Boolean(queue.deadLetterQueue)).length ?? 0;
|
|
@@ -52462,6 +53517,473 @@ class DeployService {
|
|
|
52462
53517
|
}
|
|
52463
53518
|
}
|
|
52464
53519
|
}
|
|
53520
|
+
async* executeDatabaseStep(context2) {
|
|
53521
|
+
const { game: game2, request, user, deploymentId, state } = context2;
|
|
53522
|
+
if (request.schema) {
|
|
53523
|
+
if (isSchemaAdopted(state)) {
|
|
53524
|
+
throw new LegacySchemaUpgradeRequiredError;
|
|
53525
|
+
}
|
|
53526
|
+
const legacyDbId = context2.existingResources?.d1?.find((db2) => db2.name === deploymentId)?.id;
|
|
53527
|
+
if (legacyDbId) {
|
|
53528
|
+
const ledger = await this.getCloudflare().d1.readMigrationLedger(legacyDbId);
|
|
53529
|
+
if (ledger.length > 0) {
|
|
53530
|
+
throw new LegacySchemaUpgradeRequiredError;
|
|
53531
|
+
}
|
|
53532
|
+
}
|
|
53533
|
+
setAttribute("app.deploy.db_mode", "legacy");
|
|
53534
|
+
return { ...NO_DATABASE_WORK, legacySchema: request.schema };
|
|
53535
|
+
}
|
|
53536
|
+
const database = request.database;
|
|
53537
|
+
if (!database) {
|
|
53538
|
+
return NO_DATABASE_WORK;
|
|
53539
|
+
}
|
|
53540
|
+
if (!hasBinding(request.bindings?.database)) {
|
|
53541
|
+
throw new ValidationError("The database payload requires a database binding");
|
|
53542
|
+
}
|
|
53543
|
+
if (!request.deployId) {
|
|
53544
|
+
throw new ValidationError("deployId is required when a database payload is present");
|
|
53545
|
+
}
|
|
53546
|
+
setAttribute("app.deploy.db_mode", database.mode);
|
|
53547
|
+
const cf = this.getCloudflare();
|
|
53548
|
+
const persistedId = context2.existingResources?.d1?.find((db2) => db2.name === deploymentId)?.id;
|
|
53549
|
+
const databaseId = persistedId ?? await cf.d1.create(deploymentId);
|
|
53550
|
+
if (database.mode === "migrate") {
|
|
53551
|
+
return yield* this.runMigrateMode({
|
|
53552
|
+
game: game2,
|
|
53553
|
+
user,
|
|
53554
|
+
deployId: request.deployId,
|
|
53555
|
+
databaseId,
|
|
53556
|
+
migrations: database.migrations,
|
|
53557
|
+
state
|
|
53558
|
+
});
|
|
53559
|
+
}
|
|
53560
|
+
return yield* this.runPushMode({ game: game2, databaseId, payload: database, state });
|
|
53561
|
+
}
|
|
53562
|
+
async* runMigrateMode(args2) {
|
|
53563
|
+
const { game: game2, user, deployId, databaseId, migrations, state } = args2;
|
|
53564
|
+
const cf = this.getCloudflare();
|
|
53565
|
+
yield { type: "status", data: { message: "Preparing database migrations" } };
|
|
53566
|
+
await this.assertNoSchemaDrift(databaseId, state);
|
|
53567
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
53568
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
53569
|
+
const plan = planMigrations(migrations.map((migration) => ({ tag: migration.tag, checksum: migration.checksum })), ledger.map((row) => ({
|
|
53570
|
+
tag: row.tag,
|
|
53571
|
+
checksum: row.checksum,
|
|
53572
|
+
checksumAlgo: row.checksum_algo
|
|
53573
|
+
})));
|
|
53574
|
+
setAttributes({
|
|
53575
|
+
"app.deploy.migrations_total": migrations.length,
|
|
53576
|
+
"app.deploy.migrations_applied_before": ledger.length,
|
|
53577
|
+
"app.deploy.migrations_pending": plan.pendingTags.length
|
|
53578
|
+
});
|
|
53579
|
+
if (plan.checksumMismatches.length > 0) {
|
|
53580
|
+
throw new MigrationChecksumMismatchError(plan.checksumMismatches);
|
|
53581
|
+
}
|
|
53582
|
+
if (plan.missingFromJournalTags.length > 0) {
|
|
53583
|
+
throw new MigrationJournalDivergenceError(plan.missingFromJournalTags);
|
|
53584
|
+
}
|
|
53585
|
+
if (plan.outOfOrderTags.length > 0) {
|
|
53586
|
+
throw new MigrationOrderError(plan.outOfOrderTags);
|
|
53587
|
+
}
|
|
53588
|
+
if (plan.pendingTags.length === 0) {
|
|
53589
|
+
yield { type: "status", data: { message: "Database schema is up to date" } };
|
|
53590
|
+
if (ledger.length > 0 && !state?.schemaFingerprint) {
|
|
53591
|
+
const fingerprint2 = await cf.d1.fingerprintSchema(databaseId);
|
|
53592
|
+
await this.persistDeploymentState(game2.id, {
|
|
53593
|
+
schemaFingerprint: fingerprint2.fingerprint
|
|
53594
|
+
});
|
|
53595
|
+
return {
|
|
53596
|
+
...NO_DATABASE_WORK,
|
|
53597
|
+
schemaFingerprint: fingerprint2.fingerprint
|
|
53598
|
+
};
|
|
53599
|
+
}
|
|
53600
|
+
return {
|
|
53601
|
+
...NO_DATABASE_WORK,
|
|
53602
|
+
schemaFingerprint: state?.schemaFingerprint ?? null
|
|
53603
|
+
};
|
|
53604
|
+
}
|
|
53605
|
+
const capture = yield* this.captureBookmarkStep(databaseId);
|
|
53606
|
+
const migrationsByTag = new Map(migrations.map((migration) => [migration.tag, migration]));
|
|
53607
|
+
let appliedCount = 0;
|
|
53608
|
+
for (const tag of plan.pendingTags) {
|
|
53609
|
+
const migration = migrationsByTag.get(tag);
|
|
53610
|
+
const count = migration.statements.length;
|
|
53611
|
+
yield {
|
|
53612
|
+
type: "status",
|
|
53613
|
+
data: { message: `Applying ${tag} (${count} statement${count === 1 ? "" : "s"})` }
|
|
53614
|
+
};
|
|
53615
|
+
const startedAt = Date.now();
|
|
53616
|
+
try {
|
|
53617
|
+
await withSpan("deploy.apply_migration", () => cf.d1.applyMigration(databaseId, {
|
|
53618
|
+
tag,
|
|
53619
|
+
statements: migration.statements,
|
|
53620
|
+
checksum: migration.checksum,
|
|
53621
|
+
deployId,
|
|
53622
|
+
appliedBy: user.id
|
|
53623
|
+
}));
|
|
53624
|
+
} catch (error) {
|
|
53625
|
+
if (appliedCount > 0) {
|
|
53626
|
+
await this.recordAppliedPrefixFingerprint(game2.id, databaseId);
|
|
53627
|
+
}
|
|
53628
|
+
throw await this.toMigrationStepError(databaseId, error, migration);
|
|
53629
|
+
}
|
|
53630
|
+
appliedCount++;
|
|
53631
|
+
const seconds = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
53632
|
+
yield { type: "status", data: { message: `Applied ${tag} (${seconds}s)` } };
|
|
53633
|
+
}
|
|
53634
|
+
setAttribute("app.deploy.migrations_applied", plan.pendingTags.length);
|
|
53635
|
+
const fingerprint = await cf.d1.fingerprintSchema(databaseId);
|
|
53636
|
+
await this.persistDeploymentState(game2.id, {
|
|
53637
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
53638
|
+
schemaHash: null,
|
|
53639
|
+
schemaSnapshot: null
|
|
53640
|
+
});
|
|
53641
|
+
return {
|
|
53642
|
+
schemaHash: null,
|
|
53643
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
53644
|
+
timeTravelBookmark: capture?.bookmark ?? null,
|
|
53645
|
+
bookmarkCapturedAt: capture?.capturedAt ?? null
|
|
53646
|
+
};
|
|
53647
|
+
}
|
|
53648
|
+
async* runPushMode(args2) {
|
|
53649
|
+
const { game: game2, databaseId, payload, state } = args2;
|
|
53650
|
+
const cf = this.getCloudflare();
|
|
53651
|
+
yield { type: "status", data: { message: "Verifying database schema state" } };
|
|
53652
|
+
if (typeof payload.baselineHash !== "string" && payload.baselineHash !== null) {
|
|
53653
|
+
throw new ValidationError("Push deploys require baselineHash (null on first deploy)");
|
|
53654
|
+
}
|
|
53655
|
+
if (isMigrateManaged(state)) {
|
|
53656
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
53657
|
+
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 });
|
|
53658
|
+
}
|
|
53659
|
+
const storedHash = state?.schemaHash ?? null;
|
|
53660
|
+
if (payload.baselineHash !== storedHash) {
|
|
53661
|
+
throw await this.buildStateConflictError(game2.id, payload.baselineHash);
|
|
53662
|
+
}
|
|
53663
|
+
const statements = splitSqlStatements(payload.sql);
|
|
53664
|
+
const oversized = findOversizedStatement(statements);
|
|
53665
|
+
if (oversized) {
|
|
53666
|
+
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.", {
|
|
53667
|
+
code: DEPLOY_ERROR_CODES.pushFailed,
|
|
53668
|
+
statementIndex: oversized.index,
|
|
53669
|
+
offset: null
|
|
53670
|
+
});
|
|
53671
|
+
}
|
|
53672
|
+
const destructive = detectDestructiveStatements(statements);
|
|
53673
|
+
setAttributes({
|
|
53674
|
+
"app.deploy.push_statement_count": statements.length,
|
|
53675
|
+
"app.deploy.push_destructive_count": destructive.length,
|
|
53676
|
+
"app.deploy.push_accept_data_loss": Boolean(payload.acceptDataLoss)
|
|
53677
|
+
});
|
|
53678
|
+
if (destructive.length > 0 && !payload.acceptDataLoss) {
|
|
53679
|
+
throw new DestructiveSchemaError(destructive);
|
|
53680
|
+
}
|
|
53681
|
+
await this.assertNoSchemaDrift(databaseId, state);
|
|
53682
|
+
const reserved = await this.persistPushDeploymentState(game2.id, payload.baselineHash, {
|
|
53683
|
+
schemaFingerprint: PUSH_RESERVATION_FINGERPRINT,
|
|
53684
|
+
schemaHash: payload.nextHash,
|
|
53685
|
+
schemaSnapshot: payload.nextSnapshot
|
|
53686
|
+
});
|
|
53687
|
+
if (!reserved) {
|
|
53688
|
+
throw await this.buildStateConflictError(game2.id, payload.baselineHash);
|
|
53689
|
+
}
|
|
53690
|
+
let capture = null;
|
|
53691
|
+
if (statements.length > 0) {
|
|
53692
|
+
capture = yield* this.captureBookmarkStep(databaseId);
|
|
53693
|
+
yield {
|
|
53694
|
+
type: "status",
|
|
53695
|
+
data: {
|
|
53696
|
+
message: `Applying database schema changes (${statements.length} statements)`
|
|
53697
|
+
}
|
|
53698
|
+
};
|
|
53699
|
+
try {
|
|
53700
|
+
await cf.d1.batch(databaseId, [
|
|
53701
|
+
{ sql: "PRAGMA defer_foreign_keys = on" },
|
|
53702
|
+
...statements.map((statement) => ({ sql: statement })),
|
|
53703
|
+
{ sql: "PRAGMA defer_foreign_keys = off" }
|
|
53704
|
+
]);
|
|
53705
|
+
} catch (error) {
|
|
53706
|
+
await this.persistPushDeploymentState(game2.id, payload.nextHash, {
|
|
53707
|
+
schemaFingerprint: state?.schemaFingerprint ?? null,
|
|
53708
|
+
schemaHash: state?.schemaHash ?? null,
|
|
53709
|
+
schemaSnapshot: state?.schemaSnapshot ?? null
|
|
53710
|
+
});
|
|
53711
|
+
throw this.toPushStepError(error);
|
|
53712
|
+
}
|
|
53713
|
+
}
|
|
53714
|
+
const fingerprint = await cf.d1.fingerprintSchema(databaseId);
|
|
53715
|
+
await this.persistDeploymentState(game2.id, {
|
|
53716
|
+
schemaFingerprint: fingerprint.fingerprint
|
|
53717
|
+
});
|
|
53718
|
+
return {
|
|
53719
|
+
schemaHash: payload.nextHash,
|
|
53720
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
53721
|
+
timeTravelBookmark: capture?.bookmark ?? null,
|
|
53722
|
+
bookmarkCapturedAt: capture?.capturedAt ?? null
|
|
53723
|
+
};
|
|
53724
|
+
}
|
|
53725
|
+
async* captureBookmarkStep(databaseId) {
|
|
53726
|
+
const cf = this.getCloudflare();
|
|
53727
|
+
const bookmark = await cf.d1.captureBookmark(databaseId);
|
|
53728
|
+
const capturedAt = new Date;
|
|
53729
|
+
setAttribute("app.deploy.db_bookmark_captured", bookmark.captured);
|
|
53730
|
+
if (!bookmark.captured) {
|
|
53731
|
+
yield {
|
|
53732
|
+
type: "status",
|
|
53733
|
+
data: { message: `Time Travel bookmark unavailable (${bookmark.error})` }
|
|
53734
|
+
};
|
|
53735
|
+
return null;
|
|
53736
|
+
}
|
|
53737
|
+
yield {
|
|
53738
|
+
type: "status",
|
|
53739
|
+
data: {
|
|
53740
|
+
message: "Captured Time Travel bookmark",
|
|
53741
|
+
details: { bookmark: bookmark.bookmark }
|
|
53742
|
+
}
|
|
53743
|
+
};
|
|
53744
|
+
return { bookmark: bookmark.bookmark, capturedAt };
|
|
53745
|
+
}
|
|
53746
|
+
async toMigrationStepError(databaseId, error, migration) {
|
|
53747
|
+
if (error instanceof D1StatementTooLargeError) {
|
|
53748
|
+
return new ValidationError(error.message, {
|
|
53749
|
+
code: DEPLOY_ERROR_CODES.migrationFailed,
|
|
53750
|
+
tag: migration.tag,
|
|
53751
|
+
statementIndex: error.statementIndex,
|
|
53752
|
+
offset: null,
|
|
53753
|
+
d1Message: error.message
|
|
53754
|
+
});
|
|
53755
|
+
}
|
|
53756
|
+
if (error instanceof D1MigrationError) {
|
|
53757
|
+
addEvent("deploy.migration_failed", {
|
|
53758
|
+
"app.d1.migration_tag": migration.tag,
|
|
53759
|
+
"app.error.message": error.d1Message,
|
|
53760
|
+
...error.offset !== null && { "app.d1.error_offset": error.offset }
|
|
53761
|
+
});
|
|
53762
|
+
const alreadyApplied = isAlreadyExistsSqlError(error.d1Message) ? await this.assessAlreadyApplied(databaseId, migration.statements) : null;
|
|
53763
|
+
return new MigrationExecutionError({
|
|
53764
|
+
tag: migration.tag,
|
|
53765
|
+
d1Message: error.d1Message,
|
|
53766
|
+
offset: error.offset,
|
|
53767
|
+
...alreadyApplied ? { alreadyApplied } : {}
|
|
53768
|
+
});
|
|
53769
|
+
}
|
|
53770
|
+
return error;
|
|
53771
|
+
}
|
|
53772
|
+
async assessAlreadyApplied(databaseId, statements) {
|
|
53773
|
+
try {
|
|
53774
|
+
const cf = this.getCloudflare();
|
|
53775
|
+
const live = await cf.d1.fingerprintSchema(databaseId);
|
|
53776
|
+
const tables = await cf.d1.readTableColumns(databaseId, live.tables);
|
|
53777
|
+
const assessment = assessMigrationAlreadyApplied(statements, tables);
|
|
53778
|
+
addEvent("deploy.already_applied_assessment", {
|
|
53779
|
+
"app.deploy.already_applied_verdict": assessment.verdict,
|
|
53780
|
+
"app.deploy.already_applied_present": assessment.present.length,
|
|
53781
|
+
"app.deploy.already_applied_missing": assessment.missing.length
|
|
53782
|
+
});
|
|
53783
|
+
return assessment.verdict === "no-signal" ? null : assessment;
|
|
53784
|
+
} catch (assessError) {
|
|
53785
|
+
addEvent("deploy.already_applied_check_failed", {
|
|
53786
|
+
"app.error.message": errorMessage2(assessError)
|
|
53787
|
+
});
|
|
53788
|
+
return null;
|
|
53789
|
+
}
|
|
53790
|
+
}
|
|
53791
|
+
toPushStepError(error) {
|
|
53792
|
+
if (error instanceof D1BatchError) {
|
|
53793
|
+
addEvent("deploy.push_failed", {
|
|
53794
|
+
"app.error.message": error.d1Message,
|
|
53795
|
+
...error.offset !== null && { "app.d1.error_offset": error.offset }
|
|
53796
|
+
});
|
|
53797
|
+
return new PushExecutionError({ d1Message: error.d1Message, offset: error.offset });
|
|
53798
|
+
}
|
|
53799
|
+
return error;
|
|
53800
|
+
}
|
|
53801
|
+
async assertNoSchemaDrift(databaseId, state) {
|
|
53802
|
+
if (!state?.schemaFingerprint) {
|
|
53803
|
+
return;
|
|
53804
|
+
}
|
|
53805
|
+
const live = await this.getCloudflare().d1.fingerprintSchema(databaseId);
|
|
53806
|
+
if (live.fingerprint !== state.schemaFingerprint) {
|
|
53807
|
+
addEvent("deploy.state_drift", {
|
|
53808
|
+
"app.deploy.expected_fingerprint": state.schemaFingerprint,
|
|
53809
|
+
"app.deploy.actual_fingerprint": live.fingerprint
|
|
53810
|
+
});
|
|
53811
|
+
throw new DeploymentStateDriftError({
|
|
53812
|
+
expectedFingerprint: state.schemaFingerprint,
|
|
53813
|
+
actualFingerprint: live.fingerprint
|
|
53814
|
+
});
|
|
53815
|
+
}
|
|
53816
|
+
}
|
|
53817
|
+
async buildStateConflictError(gameId, claimedHash) {
|
|
53818
|
+
const [row, lastDeploy] = await Promise.all([
|
|
53819
|
+
this.deps.db.query.gameDeploymentState.findFirst({
|
|
53820
|
+
where: eq(gameDeploymentState.gameId, gameId),
|
|
53821
|
+
columns: { schemaHash: true }
|
|
53822
|
+
}),
|
|
53823
|
+
findLastSuccessfulDeployWithEmail(this.deps.db, gameId)
|
|
53824
|
+
]);
|
|
53825
|
+
const currentHash = row?.schemaHash ?? null;
|
|
53826
|
+
addEvent("deploy.state_conflict", {
|
|
53827
|
+
"app.deploy.baseline_hash": claimedHash ?? "null",
|
|
53828
|
+
"app.deploy.current_hash": currentHash ?? "null"
|
|
53829
|
+
});
|
|
53830
|
+
return new DeploymentStateConflictError({
|
|
53831
|
+
currentHash,
|
|
53832
|
+
lastDeployAt: lastDeploy?.at.toISOString() ?? null,
|
|
53833
|
+
lastDeployBy: lastDeploy?.email ?? lastDeploy?.userId ?? null
|
|
53834
|
+
});
|
|
53835
|
+
}
|
|
53836
|
+
async recordAppliedPrefixFingerprint(gameId, databaseId) {
|
|
53837
|
+
try {
|
|
53838
|
+
const fingerprint = await this.getCloudflare().d1.fingerprintSchema(databaseId);
|
|
53839
|
+
await this.persistDeploymentState(gameId, {
|
|
53840
|
+
schemaFingerprint: fingerprint.fingerprint
|
|
53841
|
+
});
|
|
53842
|
+
} catch (error) {
|
|
53843
|
+
addEvent("deploy.fingerprint_persist_failed", {
|
|
53844
|
+
"exception.type": errorType(error),
|
|
53845
|
+
"app.error.message": errorMessage2(error)
|
|
53846
|
+
});
|
|
53847
|
+
}
|
|
53848
|
+
}
|
|
53849
|
+
async persistDeploymentState(gameId, patch) {
|
|
53850
|
+
const set = {
|
|
53851
|
+
...patch,
|
|
53852
|
+
updatedAt: new Date
|
|
53853
|
+
};
|
|
53854
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, baselineSource: "deploy", ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
53855
|
+
}
|
|
53856
|
+
async persistArtifactHashes(gameId, patch) {
|
|
53857
|
+
const set = {
|
|
53858
|
+
...patch.buildHash !== undefined && { buildHash: patch.buildHash },
|
|
53859
|
+
...patch.integrationsHash !== undefined && {
|
|
53860
|
+
integrationsHash: patch.integrationsHash
|
|
53861
|
+
}
|
|
53862
|
+
};
|
|
53863
|
+
if (Object.keys(set).length === 0) {
|
|
53864
|
+
return;
|
|
53865
|
+
}
|
|
53866
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, ...set, updatedAt: new Date }).onConflictDoUpdate({
|
|
53867
|
+
target: gameDeploymentState.gameId,
|
|
53868
|
+
set: { ...set, updatedAt: new Date }
|
|
53869
|
+
});
|
|
53870
|
+
}
|
|
53871
|
+
async persistPushDeploymentState(gameId, baselineHash, patch) {
|
|
53872
|
+
const set = { ...patch, updatedAt: new Date };
|
|
53873
|
+
if (baselineHash === null) {
|
|
53874
|
+
const claimed2 = await this.deps.db.insert(gameDeploymentState).values({ gameId, baselineSource: "deploy", ...set }).onConflictDoUpdate({
|
|
53875
|
+
target: gameDeploymentState.gameId,
|
|
53876
|
+
set,
|
|
53877
|
+
setWhere: isNull(gameDeploymentState.schemaHash)
|
|
53878
|
+
}).returning({ gameId: gameDeploymentState.gameId });
|
|
53879
|
+
return claimed2.length > 0;
|
|
53880
|
+
}
|
|
53881
|
+
const claimed = await this.deps.db.update(gameDeploymentState).set(set).where(and(eq(gameDeploymentState.gameId, gameId), eq(gameDeploymentState.schemaHash, baselineHash))).returning({ gameId: gameDeploymentState.gameId });
|
|
53882
|
+
return claimed.length > 0;
|
|
53883
|
+
}
|
|
53884
|
+
async* adoptClientBaseline(context2) {
|
|
53885
|
+
const { game: game2, request, user, deploymentId, baseline } = context2;
|
|
53886
|
+
const cf = this.getCloudflare();
|
|
53887
|
+
const persistedId = context2.existingResources?.d1?.find((db2) => db2.name === deploymentId)?.id;
|
|
53888
|
+
const databaseId = persistedId ?? (hasBinding(request.bindings?.database) ? await cf.d1.create(deploymentId) : null);
|
|
53889
|
+
if (baseline.lastAppliedMigrationTag && !databaseId) {
|
|
53890
|
+
throw new ValidationError("Baseline claims applied migrations, but the deploy has no database binding");
|
|
53891
|
+
}
|
|
53892
|
+
const live = databaseId ? await cf.d1.fingerprintSchema(databaseId) : null;
|
|
53893
|
+
let recordedRows = 0;
|
|
53894
|
+
if (databaseId && baseline.lastAppliedMigrationTag) {
|
|
53895
|
+
if (live.tables.length === 0) {
|
|
53896
|
+
throw new BaselineDatabaseEmptyError;
|
|
53897
|
+
}
|
|
53898
|
+
const slice = sliceJournalToTag(baseline.journal ?? [], baseline.lastAppliedMigrationTag);
|
|
53899
|
+
if (!slice) {
|
|
53900
|
+
throw new ValidationError(`Baseline lastAppliedMigrationTag '${baseline.lastAppliedMigrationTag}' ` + "is not in the submitted journal");
|
|
53901
|
+
}
|
|
53902
|
+
if (baseline.evidence?.length) {
|
|
53903
|
+
const [tables, lastDeploy] = await Promise.all([
|
|
53904
|
+
cf.d1.readTableColumns(databaseId, live.tables),
|
|
53905
|
+
findLastSuccessfulDeploy(this.deps.db, game2.id)
|
|
53906
|
+
]);
|
|
53907
|
+
assertBaselineClaimValid({
|
|
53908
|
+
claimedTag: baseline.lastAppliedMigrationTag,
|
|
53909
|
+
evidence: baseline.evidence,
|
|
53910
|
+
tables,
|
|
53911
|
+
indexes: new Set(live.indexes),
|
|
53912
|
+
views: new Set(live.views),
|
|
53913
|
+
lastDeployAt: lastDeploy?.at ?? null,
|
|
53914
|
+
allowUnverified: false,
|
|
53915
|
+
source: "seed",
|
|
53916
|
+
gameId: game2.id,
|
|
53917
|
+
userId: user.id
|
|
53918
|
+
});
|
|
53919
|
+
}
|
|
53920
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
53921
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
53922
|
+
const plan = planMigrations(slice, ledger.map((row) => ({
|
|
53923
|
+
tag: row.tag,
|
|
53924
|
+
checksum: row.checksum,
|
|
53925
|
+
checksumAlgo: row.checksum_algo
|
|
53926
|
+
})));
|
|
53927
|
+
if (plan.checksumMismatches.length > 0) {
|
|
53928
|
+
throw new MigrationChecksumMismatchError(plan.checksumMismatches);
|
|
53929
|
+
}
|
|
53930
|
+
if (plan.missingFromJournalTags.length > 0) {
|
|
53931
|
+
throw new MigrationJournalDivergenceError(plan.missingFromJournalTags);
|
|
53932
|
+
}
|
|
53933
|
+
if (plan.outOfOrderTags.length > 0) {
|
|
53934
|
+
throw new MigrationOrderError(plan.outOfOrderTags);
|
|
53935
|
+
}
|
|
53936
|
+
const checksumByTag = new Map(slice.map((entry2) => [entry2.tag, entry2.checksum]));
|
|
53937
|
+
const rows = plan.pendingTags.map((tag) => ({
|
|
53938
|
+
tag,
|
|
53939
|
+
checksum: checksumByTag.get(tag)
|
|
53940
|
+
}));
|
|
53941
|
+
if (rows.length > 0) {
|
|
53942
|
+
await cf.d1.recordLedgerRows(databaseId, {
|
|
53943
|
+
rows,
|
|
53944
|
+
deployId: `baseline:${request.deployId ?? crypto.randomUUID()}`,
|
|
53945
|
+
appliedBy: user.id,
|
|
53946
|
+
source: "baseline"
|
|
53947
|
+
});
|
|
53948
|
+
}
|
|
53949
|
+
recordedRows = rows.length;
|
|
53950
|
+
}
|
|
53951
|
+
const fingerprint = live?.fingerprint ?? null;
|
|
53952
|
+
const set = {
|
|
53953
|
+
...baseline.schemaHash !== undefined && { schemaHash: baseline.schemaHash },
|
|
53954
|
+
...baseline.schemaSnapshot !== undefined && {
|
|
53955
|
+
schemaSnapshot: baseline.schemaSnapshot
|
|
53956
|
+
},
|
|
53957
|
+
...baseline.integrationsHash !== undefined && {
|
|
53958
|
+
integrationsHash: baseline.integrationsHash
|
|
53959
|
+
},
|
|
53960
|
+
...baseline.buildHash !== undefined && { buildHash: baseline.buildHash },
|
|
53961
|
+
...fingerprint !== null && { schemaFingerprint: fingerprint },
|
|
53962
|
+
baselineSource: "client-baseline",
|
|
53963
|
+
updatedAt: new Date
|
|
53964
|
+
};
|
|
53965
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId: game2.id, ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
53966
|
+
setAttributes({
|
|
53967
|
+
"app.deploy.baseline_adopted": true,
|
|
53968
|
+
"app.deploy.baseline_ledger_rows": recordedRows,
|
|
53969
|
+
"app.deploy.baseline_has_snapshot": baseline.schemaSnapshot !== undefined
|
|
53970
|
+
});
|
|
53971
|
+
yield {
|
|
53972
|
+
type: "status",
|
|
53973
|
+
data: {
|
|
53974
|
+
message: "Adopted deployment state from client baseline",
|
|
53975
|
+
details: {
|
|
53976
|
+
ledgerRows: recordedRows,
|
|
53977
|
+
...baseline.lastAppliedMigrationTag && {
|
|
53978
|
+
lastAppliedMigrationTag: baseline.lastAppliedMigrationTag
|
|
53979
|
+
}
|
|
53980
|
+
}
|
|
53981
|
+
}
|
|
53982
|
+
};
|
|
53983
|
+
return this.deps.db.query.gameDeploymentState.findFirst({
|
|
53984
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
53985
|
+
});
|
|
53986
|
+
}
|
|
52465
53987
|
async applyGameMetadata(gameId, request, hasFrontend, hasMetadata, deploymentUrl) {
|
|
52466
53988
|
const updates = { updatedAt: new Date };
|
|
52467
53989
|
if (hasFrontend) {
|
|
@@ -52487,7 +54009,8 @@ class DeployService {
|
|
|
52487
54009
|
result,
|
|
52488
54010
|
request,
|
|
52489
54011
|
user,
|
|
52490
|
-
flags: flags2
|
|
54012
|
+
flags: flags2,
|
|
54013
|
+
database
|
|
52491
54014
|
}) {
|
|
52492
54015
|
const { hasBackend, hasFrontend, hasMetadata } = flags2;
|
|
52493
54016
|
const db2 = this.deps.db;
|
|
@@ -52497,9 +54020,17 @@ class DeployService {
|
|
|
52497
54020
|
deploymentId: result.deploymentId,
|
|
52498
54021
|
url: result.url,
|
|
52499
54022
|
codeHash,
|
|
54023
|
+
schemaHash: database.schemaHash,
|
|
54024
|
+
schemaFingerprint: database.schemaFingerprint,
|
|
54025
|
+
timeTravelBookmark: database.timeTravelBookmark,
|
|
54026
|
+
bookmarkCapturedAt: database.bookmarkCapturedAt,
|
|
52500
54027
|
resources: result.resources,
|
|
52501
54028
|
target: "game"
|
|
52502
54029
|
});
|
|
54030
|
+
await this.persistArtifactHashes(game2.id, {
|
|
54031
|
+
buildHash: hasFrontend ? request.buildHash : undefined,
|
|
54032
|
+
integrationsHash: request.integrationsHash
|
|
54033
|
+
});
|
|
52503
54034
|
if (hasBackend) {
|
|
52504
54035
|
await withSpan("deploy.configure_worker_secrets", async () => {
|
|
52505
54036
|
await this.ensureWorkerApiKeyOnWorker(user, DeployService.gameWorkerKeySpec(slug), result.deploymentId);
|
|
@@ -52636,6 +54167,29 @@ class DeployService {
|
|
|
52636
54167
|
const cf = this.getCloudflare();
|
|
52637
54168
|
await cf.setSecrets(deploymentId, { QUEUE_INGRESS_SECRET: secret });
|
|
52638
54169
|
}
|
|
54170
|
+
async* pruneManagedSecretsStep(gameId, deploymentId, pruneSecrets) {
|
|
54171
|
+
const cf = this.getCloudflare();
|
|
54172
|
+
const keys = [...new Set(pruneSecrets)];
|
|
54173
|
+
yield {
|
|
54174
|
+
type: "status",
|
|
54175
|
+
data: {
|
|
54176
|
+
message: `Pruning ${keys.length} managed secret(s)`,
|
|
54177
|
+
details: { keys }
|
|
54178
|
+
}
|
|
54179
|
+
};
|
|
54180
|
+
await withSpan("deploy.prune_secrets", async () => {
|
|
54181
|
+
const existing = await cf.listSecrets(deploymentId);
|
|
54182
|
+
for (const key of keys) {
|
|
54183
|
+
const prefixedKey = `${SECRETS_PREFIX}${key}`;
|
|
54184
|
+
if (existing.includes(prefixedKey)) {
|
|
54185
|
+
await cf.deleteSecret(deploymentId, prefixedKey);
|
|
54186
|
+
}
|
|
54187
|
+
}
|
|
54188
|
+
const pruned = keys.reduce((expr, key) => sql`${expr} - ${key}::text`, sql`coalesce(${gameDeploymentState.secretsManifest}, '{}'::jsonb)`);
|
|
54189
|
+
await this.deps.db.update(gameDeploymentState).set({ secretsManifest: pruned, updatedAt: new Date }).where(eq(gameDeploymentState.gameId, gameId));
|
|
54190
|
+
});
|
|
54191
|
+
setAttribute("app.deploy.pruned_secret_count", keys.length);
|
|
54192
|
+
}
|
|
52639
54193
|
async ensureDashboardSessionSecret(deploymentId, existingSecrets, hasPriorDeployment) {
|
|
52640
54194
|
if (existingSecrets === null && hasPriorDeployment) {
|
|
52641
54195
|
setAttribute("app.deploy.session_secret_outcome", "check_failed_kept");
|
|
@@ -52740,6 +54294,10 @@ class DeployService {
|
|
|
52740
54294
|
target: record.target,
|
|
52741
54295
|
url: record.url,
|
|
52742
54296
|
codeHash: record.codeHash,
|
|
54297
|
+
schemaHash: record.schemaHash ?? null,
|
|
54298
|
+
schemaFingerprint: record.schemaFingerprint ?? null,
|
|
54299
|
+
timeTravelBookmark: record.timeTravelBookmark ?? null,
|
|
54300
|
+
bookmarkCapturedAt: record.bookmarkCapturedAt ?? null,
|
|
52743
54301
|
resources: record.resources,
|
|
52744
54302
|
isActive: true
|
|
52745
54303
|
});
|
|
@@ -52749,8 +54307,11 @@ class DeployService {
|
|
|
52749
54307
|
await this.deps.alerts.notifyDeploymentFailure(failure).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "deployment_failure" }));
|
|
52750
54308
|
}
|
|
52751
54309
|
}
|
|
54310
|
+
var PUSH_RESERVATION_FINGERPRINT = "reserved:push-in-flight";
|
|
54311
|
+
var NO_DATABASE_WORK;
|
|
52752
54312
|
var init_deploy_service = __esm(() => {
|
|
52753
54313
|
init_drizzle_orm();
|
|
54314
|
+
init_src4();
|
|
52754
54315
|
init_playcademy();
|
|
52755
54316
|
init_src();
|
|
52756
54317
|
init_helpers_index();
|
|
@@ -52758,9 +54319,17 @@ var init_deploy_service = __esm(() => {
|
|
|
52758
54319
|
init_spans();
|
|
52759
54320
|
init_tunnel();
|
|
52760
54321
|
init_errors();
|
|
54322
|
+
init_baseline_validation_util();
|
|
52761
54323
|
init_dashboard_util();
|
|
52762
54324
|
init_deployment_util();
|
|
54325
|
+
init_migration_util();
|
|
52763
54326
|
init_worker_keys_util();
|
|
54327
|
+
NO_DATABASE_WORK = {
|
|
54328
|
+
schemaHash: null,
|
|
54329
|
+
schemaFingerprint: null,
|
|
54330
|
+
timeTravelBookmark: null,
|
|
54331
|
+
bookmarkCapturedAt: null
|
|
54332
|
+
};
|
|
52764
54333
|
});
|
|
52765
54334
|
|
|
52766
54335
|
class DeveloperService {
|
|
@@ -54205,7 +55774,7 @@ function createGameServices(deps) {
|
|
|
54205
55774
|
}
|
|
54206
55775
|
};
|
|
54207
55776
|
}
|
|
54208
|
-
var
|
|
55777
|
+
var init_game3 = __esm(() => {
|
|
54209
55778
|
init_dashboard_service();
|
|
54210
55779
|
init_deploy_job_service();
|
|
54211
55780
|
init_deploy_service();
|
|
@@ -54395,16 +55964,37 @@ class AlertsService {
|
|
|
54395
55964
|
await this.sendAlert(discord, embed.build());
|
|
54396
55965
|
}
|
|
54397
55966
|
async notifyDeploymentFailure(failure) {
|
|
54398
|
-
const
|
|
55967
|
+
const refused = DEPLOY_REFUSAL_CODES.has(failure.errorCode ?? "");
|
|
55968
|
+
const discord = this.recordAlert(refused ? "deployment_blocked" : "deployment_failure");
|
|
54399
55969
|
if (!discord) {
|
|
54400
55970
|
return;
|
|
54401
55971
|
}
|
|
54402
|
-
const [
|
|
54403
|
-
const
|
|
55972
|
+
const [noun, subject] = failure.target === "dashboard" ? ["Dashboard Deployment", "Dashboard deployment"] : ["Deployment", "Deployment"];
|
|
55973
|
+
const title = refused ? `\uD83D\uDEA7 ${noun} Blocked` : `❌ ${noun} Failed`;
|
|
55974
|
+
const verb = refused ? "was blocked" : "failed";
|
|
55975
|
+
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
55976
|
if (failure.developer) {
|
|
54405
55977
|
embed.addField("Developer", failure.developer.email || failure.developer.id, true);
|
|
54406
55978
|
}
|
|
54407
|
-
embed.addField("Error", failure.error, false)
|
|
55979
|
+
embed.addField(refused ? "Reason" : "Error", failure.error, false);
|
|
55980
|
+
if (refused) {
|
|
55981
|
+
embed.addField("Next step", "The developer received repair guidance in their terminal", false);
|
|
55982
|
+
}
|
|
55983
|
+
embed.setFooter("Playcademy Developer Platform").setTimestamp();
|
|
55984
|
+
await this.sendAlert(discord, embed.build());
|
|
55985
|
+
}
|
|
55986
|
+
async notifyDeployBlocked(blocked) {
|
|
55987
|
+
const discord = this.recordAlert("deployment_blocked");
|
|
55988
|
+
if (!discord) {
|
|
55989
|
+
return;
|
|
55990
|
+
}
|
|
55991
|
+
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);
|
|
55992
|
+
if (blocked.developer) {
|
|
55993
|
+
embed.addField("Developer", blocked.developer.email || blocked.developer.id, true);
|
|
55994
|
+
}
|
|
55995
|
+
embed.addField("Reason", blocked.reason, false);
|
|
55996
|
+
embed.addField("Next step", "The developer received repair guidance in their terminal", false);
|
|
55997
|
+
embed.setFooter("Playcademy Developer Platform").setTimestamp();
|
|
54408
55998
|
await this.sendAlert(discord, embed.build());
|
|
54409
55999
|
}
|
|
54410
56000
|
async notifyGameDeletion(game2) {
|
|
@@ -54465,6 +56055,7 @@ var DISCORD_FIELD_LIMIT = 1024;
|
|
|
54465
56055
|
var init_alerts_service = __esm(() => {
|
|
54466
56056
|
init_discord();
|
|
54467
56057
|
init_spans();
|
|
56058
|
+
init_game2();
|
|
54468
56059
|
});
|
|
54469
56060
|
|
|
54470
56061
|
class KVBackupService {
|
|
@@ -54893,6 +56484,15 @@ class DatabaseService {
|
|
|
54893
56484
|
constructor(deps) {
|
|
54894
56485
|
this.deps = deps;
|
|
54895
56486
|
}
|
|
56487
|
+
static remapD1Resource(resources, d1ResourceName, databaseId) {
|
|
56488
|
+
return {
|
|
56489
|
+
resources: {
|
|
56490
|
+
...resources,
|
|
56491
|
+
d1: resources.d1?.map((dbResource) => dbResource.name === d1ResourceName ? { ...dbResource, id: databaseId } : dbResource)
|
|
56492
|
+
},
|
|
56493
|
+
timeTravelBookmark: null
|
|
56494
|
+
};
|
|
56495
|
+
}
|
|
54896
56496
|
getD1() {
|
|
54897
56497
|
const d1 = this.deps.cloudflare?.d1;
|
|
54898
56498
|
if (!d1) {
|
|
@@ -54911,11 +56511,7 @@ class DatabaseService {
|
|
|
54911
56511
|
try {
|
|
54912
56512
|
await this.deps.cloudflare.updateD1Binding(dashboardDeployment.deploymentId, databaseId);
|
|
54913
56513
|
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));
|
|
56514
|
+
await this.deps.db.update(gameDeployments).set(DatabaseService.remapD1Resource(dashboardDeployment.resources, d1ResourceName, databaseId)).where(eq(gameDeployments.id, dashboardDeployment.id));
|
|
54919
56515
|
}
|
|
54920
56516
|
setAttribute("app.database.dashboard_binding", "updated");
|
|
54921
56517
|
} catch (error) {
|
|
@@ -54926,20 +56522,36 @@ class DatabaseService {
|
|
|
54926
56522
|
});
|
|
54927
56523
|
}
|
|
54928
56524
|
}
|
|
54929
|
-
async reset(slug, user,
|
|
56525
|
+
async reset(slug, user, request = {}) {
|
|
56526
|
+
const { schema: schema2, database } = request;
|
|
54930
56527
|
setAttributes({
|
|
54931
56528
|
"app.database.operation": "reset",
|
|
56529
|
+
"app.database.mode": database?.mode ?? (schema2 ? "legacy" : "none"),
|
|
54932
56530
|
"app.database.schema_size": schema2?.sql.length,
|
|
54933
56531
|
"app.database.schema_version": schema2?.hash
|
|
54934
56532
|
});
|
|
54935
56533
|
const d1 = this.getD1();
|
|
54936
56534
|
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56535
|
+
const state = await this.deps.db.query.gameDeploymentState.findFirst({
|
|
56536
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
56537
|
+
});
|
|
56538
|
+
if (isSchemaAdopted(state) && !database) {
|
|
56539
|
+
if (schema2) {
|
|
56540
|
+
throw new LegacySchemaUpgradeRequiredError;
|
|
56541
|
+
}
|
|
56542
|
+
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.");
|
|
56543
|
+
}
|
|
54937
56544
|
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56545
|
+
let resetDatabaseId = null;
|
|
54938
56546
|
try {
|
|
54939
56547
|
const databaseId = await d1.reset(deploymentId);
|
|
56548
|
+
resetDatabaseId = databaseId;
|
|
54940
56549
|
setAttribute("app.database.id", databaseId);
|
|
54941
56550
|
let schemaPushed = false;
|
|
54942
|
-
if (
|
|
56551
|
+
if (database) {
|
|
56552
|
+
await this.rebuildDatabase(databaseId, database, game2.id, user);
|
|
56553
|
+
schemaPushed = true;
|
|
56554
|
+
} else if (schema2?.sql) {
|
|
54943
56555
|
await d1.executeSchema(databaseId, schema2);
|
|
54944
56556
|
schemaPushed = true;
|
|
54945
56557
|
}
|
|
@@ -54955,11 +56567,7 @@ class DatabaseService {
|
|
|
54955
56567
|
});
|
|
54956
56568
|
setAttribute("app.database.active_deployment_found", Boolean(activeDeployment));
|
|
54957
56569
|
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));
|
|
56570
|
+
await this.deps.db.update(gameDeployments).set(DatabaseService.remapD1Resource(activeDeployment.resources, deploymentId, databaseId)).where(eq(gameDeployments.id, activeDeployment.id));
|
|
54963
56571
|
}
|
|
54964
56572
|
await this.syncDashboardD1Binding(game2.id, deploymentId, databaseId);
|
|
54965
56573
|
setAttributes({
|
|
@@ -54973,24 +56581,604 @@ class DatabaseService {
|
|
|
54973
56581
|
schemaPushed
|
|
54974
56582
|
};
|
|
54975
56583
|
} catch (error) {
|
|
56584
|
+
if (database && resetDatabaseId) {
|
|
56585
|
+
await this.recordLiveFingerprint(game2.id, resetDatabaseId);
|
|
56586
|
+
}
|
|
54976
56587
|
this.deps.alerts.notifyDatabaseResetFailure({
|
|
54977
56588
|
slug,
|
|
54978
56589
|
displayName: game2.displayName,
|
|
54979
56590
|
error: errorMessage2(error),
|
|
54980
56591
|
developer: { id: user.id, email: user.email }
|
|
54981
56592
|
}).catch(catchAttrs("alerts.delivery", { "app.alerts.type": "database_reset" }));
|
|
56593
|
+
if (error instanceof DomainError) {
|
|
56594
|
+
throw error;
|
|
56595
|
+
}
|
|
54982
56596
|
throw new ValidationError(`Database reset failed: ${errorMessage2(error)}`);
|
|
54983
56597
|
}
|
|
54984
56598
|
}
|
|
56599
|
+
async rebuildDatabase(databaseId, payload, gameId, user) {
|
|
56600
|
+
const d1 = this.getD1();
|
|
56601
|
+
if (payload.mode === "migrate") {
|
|
56602
|
+
const deployId = `reset:${crypto.randomUUID()}`;
|
|
56603
|
+
await d1.ensureMigrationLedger(databaseId);
|
|
56604
|
+
for (const migration of payload.migrations) {
|
|
56605
|
+
await d1.applyMigration(databaseId, {
|
|
56606
|
+
tag: migration.tag,
|
|
56607
|
+
statements: migration.statements,
|
|
56608
|
+
checksum: migration.checksum,
|
|
56609
|
+
deployId,
|
|
56610
|
+
appliedBy: user.id
|
|
56611
|
+
});
|
|
56612
|
+
}
|
|
56613
|
+
setAttribute("app.database.migrations_replayed", payload.migrations.length);
|
|
56614
|
+
const fingerprint2 = await d1.fingerprintSchema(databaseId);
|
|
56615
|
+
await this.persistDeploymentState(gameId, {
|
|
56616
|
+
schemaFingerprint: fingerprint2.fingerprint,
|
|
56617
|
+
schemaHash: null,
|
|
56618
|
+
schemaSnapshot: null
|
|
56619
|
+
});
|
|
56620
|
+
return;
|
|
56621
|
+
}
|
|
56622
|
+
const statements = splitSqlStatements(payload.sql);
|
|
56623
|
+
if (statements.length > 0) {
|
|
56624
|
+
await d1.batch(databaseId, [
|
|
56625
|
+
{ sql: "PRAGMA defer_foreign_keys = on" },
|
|
56626
|
+
...statements.map((statement) => ({ sql: statement })),
|
|
56627
|
+
{ sql: "PRAGMA defer_foreign_keys = off" }
|
|
56628
|
+
]);
|
|
56629
|
+
}
|
|
56630
|
+
setAttribute("app.database.push_statement_count", statements.length);
|
|
56631
|
+
const fingerprint = await d1.fingerprintSchema(databaseId);
|
|
56632
|
+
await this.persistDeploymentState(gameId, {
|
|
56633
|
+
schemaFingerprint: fingerprint.fingerprint,
|
|
56634
|
+
schemaHash: payload.nextHash,
|
|
56635
|
+
schemaSnapshot: payload.nextSnapshot
|
|
56636
|
+
});
|
|
56637
|
+
}
|
|
56638
|
+
async persistDeploymentState(gameId, patch) {
|
|
56639
|
+
const set = { ...patch, updatedAt: new Date };
|
|
56640
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, baselineSource: "deploy", ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
56641
|
+
}
|
|
56642
|
+
async recordLiveFingerprint(gameId, databaseId) {
|
|
56643
|
+
try {
|
|
56644
|
+
const fingerprint = await this.getD1().fingerprintSchema(databaseId);
|
|
56645
|
+
await this.persistDeploymentState(gameId, {
|
|
56646
|
+
schemaFingerprint: fingerprint.fingerprint
|
|
56647
|
+
});
|
|
56648
|
+
} catch (error) {
|
|
56649
|
+
addEvent("database.fingerprint_persist_failed", {
|
|
56650
|
+
"exception.type": errorType(error),
|
|
56651
|
+
"app.error.message": errorMessage2(error)
|
|
56652
|
+
});
|
|
56653
|
+
}
|
|
56654
|
+
}
|
|
54985
56655
|
}
|
|
54986
56656
|
var init_database_service = __esm(() => {
|
|
54987
56657
|
init_drizzle_orm();
|
|
56658
|
+
init_src4();
|
|
54988
56659
|
init_helpers_index();
|
|
54989
56660
|
init_tables_index();
|
|
54990
56661
|
init_spans();
|
|
54991
56662
|
init_errors();
|
|
54992
56663
|
init_deployment_util();
|
|
54993
56664
|
});
|
|
56665
|
+
async function listUserSecretKeys(cloudflare2, deploymentId) {
|
|
56666
|
+
try {
|
|
56667
|
+
const allKeys = await cloudflare2.listSecrets(deploymentId);
|
|
56668
|
+
return allKeys.filter((key) => key.startsWith(SECRETS_PREFIX)).map((key) => key.slice(SECRETS_PREFIX.length));
|
|
56669
|
+
} catch (error) {
|
|
56670
|
+
const message = errorMessage2(error);
|
|
56671
|
+
if (message.includes("not found") || message.includes("10007")) {
|
|
56672
|
+
return null;
|
|
56673
|
+
}
|
|
56674
|
+
throw error;
|
|
56675
|
+
}
|
|
56676
|
+
}
|
|
56677
|
+
var init_secrets_util = __esm(() => {
|
|
56678
|
+
init_src();
|
|
56679
|
+
});
|
|
56680
|
+
function historyEventEntry(event) {
|
|
56681
|
+
const base = { at: event.createdAt.toISOString(), by: event.email ?? DELETED_ACCOUNT_LABEL };
|
|
56682
|
+
if (event.kind === "restore" && "restoredTo" in event.payload) {
|
|
56683
|
+
return [{ kind: "restore", ...base, restoredTo: event.payload.restoredTo }];
|
|
56684
|
+
}
|
|
56685
|
+
if (event.kind === "blocked" && "code" in event.payload) {
|
|
56686
|
+
return [
|
|
56687
|
+
{ kind: "blocked", ...base, code: event.payload.code, reason: event.payload.reason }
|
|
56688
|
+
];
|
|
56689
|
+
}
|
|
56690
|
+
return [];
|
|
56691
|
+
}
|
|
56692
|
+
function databaseIdFromResources(resources, deploymentId) {
|
|
56693
|
+
const database = resources?.d1?.find((db2) => db2.name === deploymentId) ?? resources?.d1?.[0];
|
|
56694
|
+
return database?.id ?? null;
|
|
56695
|
+
}
|
|
56696
|
+
function restorePointCapturedAt(row) {
|
|
56697
|
+
return row.bookmarkCapturedAt ?? row.deployedAt;
|
|
56698
|
+
}
|
|
56699
|
+
|
|
56700
|
+
class DeploymentStateService {
|
|
56701
|
+
deps;
|
|
56702
|
+
constructor(deps) {
|
|
56703
|
+
this.deps = deps;
|
|
56704
|
+
}
|
|
56705
|
+
async partitionSecretKeys(slug, manifest) {
|
|
56706
|
+
const managedKeys = Object.keys(manifest ?? {});
|
|
56707
|
+
if (!this.deps.cloudflare) {
|
|
56708
|
+
addEvent("deployment_state.secret_keys_unavailable", {
|
|
56709
|
+
"app.error.message": "Cloudflare provider not configured"
|
|
56710
|
+
});
|
|
56711
|
+
return { managedKeys, unmanagedKeys: null };
|
|
56712
|
+
}
|
|
56713
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56714
|
+
const remoteKeys = await listUserSecretKeys(this.deps.cloudflare, deploymentId);
|
|
56715
|
+
if (remoteKeys === null) {
|
|
56716
|
+
return { managedKeys, unmanagedKeys: [] };
|
|
56717
|
+
}
|
|
56718
|
+
const managed = new Set(managedKeys);
|
|
56719
|
+
return {
|
|
56720
|
+
managedKeys,
|
|
56721
|
+
unmanagedKeys: remoteKeys.filter((key) => !managed.has(key))
|
|
56722
|
+
};
|
|
56723
|
+
}
|
|
56724
|
+
async readAppliedMigrations(slug, resources) {
|
|
56725
|
+
if (!this.deps.cloudflare || !resources?.d1?.length) {
|
|
56726
|
+
return null;
|
|
56727
|
+
}
|
|
56728
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
56729
|
+
const databaseId = databaseIdFromResources(resources, deploymentId);
|
|
56730
|
+
if (!databaseId) {
|
|
56731
|
+
return null;
|
|
56732
|
+
}
|
|
56733
|
+
const ledger = await this.deps.cloudflare.d1.readMigrationLedger(databaseId);
|
|
56734
|
+
setAttributes({ "app.deployment_state.applied_migrations": ledger.length });
|
|
56735
|
+
return ledger.map((row) => ({
|
|
56736
|
+
tag: row.tag,
|
|
56737
|
+
checksum: row.checksum,
|
|
56738
|
+
checksumAlgo: row.checksum_algo
|
|
56739
|
+
}));
|
|
56740
|
+
}
|
|
56741
|
+
async get(slug, user, options = {}) {
|
|
56742
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56743
|
+
const [state, gameDeployment, dashboardDeployment, lastSucceededJob, lastFailedJob] = await Promise.all([
|
|
56744
|
+
this.deps.db.query.gameDeploymentState.findFirst({
|
|
56745
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
56746
|
+
}),
|
|
56747
|
+
this.deps.db.query.gameDeployments.findFirst({
|
|
56748
|
+
where: activeDeploymentWhere(game2.id, "game"),
|
|
56749
|
+
columns: { codeHash: true, url: true, deployedAt: true, resources: true }
|
|
56750
|
+
}),
|
|
56751
|
+
this.deps.db.query.gameDeployments.findFirst({
|
|
56752
|
+
where: activeDeploymentWhere(game2.id, "dashboard"),
|
|
56753
|
+
columns: { url: true, deployedAt: true }
|
|
56754
|
+
}),
|
|
56755
|
+
findLastSuccessfulDeployWithEmail(this.deps.db, game2.id),
|
|
56756
|
+
this.deps.db.query.gameDeployJobs.findFirst({
|
|
56757
|
+
where: and(eq(gameDeployJobs.gameId, game2.id), eq(gameDeployJobs.status, "failed")),
|
|
56758
|
+
orderBy: desc(deployJobInstant()),
|
|
56759
|
+
columns: { events: true }
|
|
56760
|
+
})
|
|
56761
|
+
]);
|
|
56762
|
+
const [secrets, appliedMigrations] = await Promise.all([
|
|
56763
|
+
this.partitionSecretKeys(slug, state?.secretsManifest ?? null),
|
|
56764
|
+
this.readAppliedMigrations(slug, gameDeployment?.resources ?? null)
|
|
56765
|
+
]);
|
|
56766
|
+
setAttributes({
|
|
56767
|
+
"app.deployment_state.seeded": Boolean(state),
|
|
56768
|
+
"app.deployment_state.game_deployed": Boolean(gameDeployment),
|
|
56769
|
+
"app.deployment_state.dashboard_deployed": Boolean(dashboardDeployment)
|
|
56770
|
+
});
|
|
56771
|
+
return {
|
|
56772
|
+
gameId: game2.id,
|
|
56773
|
+
seeded: Boolean(state),
|
|
56774
|
+
game: gameDeployment ? {
|
|
56775
|
+
codeHash: gameDeployment.codeHash,
|
|
56776
|
+
buildHash: state?.buildHash ?? null,
|
|
56777
|
+
url: gameDeployment.url,
|
|
56778
|
+
deployedAt: gameDeployment.deployedAt.toISOString()
|
|
56779
|
+
} : null,
|
|
56780
|
+
dashboard: dashboardDeployment ? {
|
|
56781
|
+
url: dashboardDeployment.url,
|
|
56782
|
+
deployedAt: dashboardDeployment.deployedAt.toISOString()
|
|
56783
|
+
} : null,
|
|
56784
|
+
database: {
|
|
56785
|
+
appliedMigrations,
|
|
56786
|
+
lastFailure: parseMigrationFailure(lastFailedJob?.events ?? null),
|
|
56787
|
+
schemaHash: state?.schemaHash ?? null,
|
|
56788
|
+
schemaFingerprint: state?.schemaFingerprint ?? null,
|
|
56789
|
+
...options.includeSchemaSnapshot && {
|
|
56790
|
+
schemaSnapshot: state?.schemaSnapshot ?? null
|
|
56791
|
+
}
|
|
56792
|
+
},
|
|
56793
|
+
secrets,
|
|
56794
|
+
integrationsHash: state?.integrationsHash ?? null,
|
|
56795
|
+
compatibilityDate: state?.compatibilityDate ?? null,
|
|
56796
|
+
lastDeploy: lastSucceededJob ? {
|
|
56797
|
+
at: lastSucceededJob.at.toISOString(),
|
|
56798
|
+
by: lastSucceededJob.email ?? DELETED_ACCOUNT_LABEL
|
|
56799
|
+
} : null
|
|
56800
|
+
};
|
|
56801
|
+
}
|
|
56802
|
+
requireCloudflare() {
|
|
56803
|
+
if (!this.deps.cloudflare) {
|
|
56804
|
+
throw new ValidationError("Deployment-state operations are not available in this environment");
|
|
56805
|
+
}
|
|
56806
|
+
return this.deps.cloudflare;
|
|
56807
|
+
}
|
|
56808
|
+
async resolveDatabaseId(slug, gameId) {
|
|
56809
|
+
const databaseId = await this.findDatabaseId(slug, gameId);
|
|
56810
|
+
if (!databaseId) {
|
|
56811
|
+
throw new ValidationError("Game has no deployed database — deploy with a database binding first");
|
|
56812
|
+
}
|
|
56813
|
+
return databaseId;
|
|
56814
|
+
}
|
|
56815
|
+
findSchemaHashState(gameId) {
|
|
56816
|
+
return this.deps.db.query.gameDeploymentState.findFirst({
|
|
56817
|
+
where: eq(gameDeploymentState.gameId, gameId),
|
|
56818
|
+
columns: { schemaHash: true }
|
|
56819
|
+
});
|
|
56820
|
+
}
|
|
56821
|
+
async findDatabaseId(slug, gameId) {
|
|
56822
|
+
const deployment = await this.deps.db.query.gameDeployments.findFirst({
|
|
56823
|
+
where: activeDeploymentWhere(gameId, "game"),
|
|
56824
|
+
columns: { resources: true }
|
|
56825
|
+
});
|
|
56826
|
+
return databaseIdFromResources(deployment?.resources, getGameDeploymentId(slug, this.deps.config.sstStage));
|
|
56827
|
+
}
|
|
56828
|
+
async baseline(slug, input, user) {
|
|
56829
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56830
|
+
const cf = this.requireCloudflare();
|
|
56831
|
+
const state = await this.deps.db.query.gameDeploymentState.findFirst({
|
|
56832
|
+
where: eq(gameDeploymentState.gameId, game2.id)
|
|
56833
|
+
});
|
|
56834
|
+
const schemaAdopted = isSchemaAdopted(state);
|
|
56835
|
+
const migrateOnlyClaim = Boolean(input.lastAppliedMigrationTag) && input.schemaHash === undefined && input.schemaSnapshot === undefined;
|
|
56836
|
+
if (schemaAdopted && !migrateOnlyClaim) {
|
|
56837
|
+
throw new BaselineAlreadyAdoptedError(state?.baselineSource ?? null);
|
|
56838
|
+
}
|
|
56839
|
+
const databaseId = await this.resolveDatabaseId(slug, game2.id);
|
|
56840
|
+
const [ledger, live] = await Promise.all([
|
|
56841
|
+
cf.d1.readMigrationLedger(databaseId),
|
|
56842
|
+
cf.d1.fingerprintSchema(databaseId)
|
|
56843
|
+
]);
|
|
56844
|
+
const verdict = evaluateBaselineGuardrails({
|
|
56845
|
+
ledgerTags: ledger.map((row) => row.tag),
|
|
56846
|
+
liveTables: live.tables
|
|
56847
|
+
});
|
|
56848
|
+
if (verdict === "ledger-not-empty") {
|
|
56849
|
+
throw new BaselineLedgerNotEmptyError(ledger.map((row) => row.tag));
|
|
56850
|
+
}
|
|
56851
|
+
if (verdict === "database-empty") {
|
|
56852
|
+
throw new BaselineDatabaseEmptyError;
|
|
56853
|
+
}
|
|
56854
|
+
if (schemaAdopted && state?.schemaFingerprint && live.fingerprint !== state.schemaFingerprint) {
|
|
56855
|
+
throw new DeploymentStateDriftError({
|
|
56856
|
+
expectedFingerprint: state.schemaFingerprint,
|
|
56857
|
+
actualFingerprint: live.fingerprint
|
|
56858
|
+
});
|
|
56859
|
+
}
|
|
56860
|
+
if (input.lastAppliedMigrationTag && input.evidence?.length) {
|
|
56861
|
+
const [tables, lastDeploy] = await Promise.all([
|
|
56862
|
+
cf.d1.readTableColumns(databaseId, live.tables),
|
|
56863
|
+
findLastSuccessfulDeploy(this.deps.db, game2.id)
|
|
56864
|
+
]);
|
|
56865
|
+
assertBaselineClaimValid({
|
|
56866
|
+
claimedTag: input.lastAppliedMigrationTag,
|
|
56867
|
+
evidence: input.evidence,
|
|
56868
|
+
tables,
|
|
56869
|
+
indexes: new Set(live.indexes),
|
|
56870
|
+
views: new Set(live.views),
|
|
56871
|
+
lastDeployAt: lastDeploy?.at ?? null,
|
|
56872
|
+
allowUnverified: Boolean(input.allowUnverified),
|
|
56873
|
+
source: "manual",
|
|
56874
|
+
gameId: game2.id,
|
|
56875
|
+
userId: user.id
|
|
56876
|
+
});
|
|
56877
|
+
}
|
|
56878
|
+
let recordedTags = [];
|
|
56879
|
+
if (input.lastAppliedMigrationTag) {
|
|
56880
|
+
const slice = sliceJournalToTag(input.journal ?? [], input.lastAppliedMigrationTag);
|
|
56881
|
+
if (!slice) {
|
|
56882
|
+
throw new ValidationError(`lastAppliedMigrationTag '${input.lastAppliedMigrationTag}' is not in the submitted journal`);
|
|
56883
|
+
}
|
|
56884
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
56885
|
+
await cf.d1.recordLedgerRows(databaseId, {
|
|
56886
|
+
rows: slice,
|
|
56887
|
+
deployId: `baseline:${crypto.randomUUID()}`,
|
|
56888
|
+
appliedBy: user.id,
|
|
56889
|
+
source: "baseline"
|
|
56890
|
+
});
|
|
56891
|
+
recordedTags = slice.map((entry2) => entry2.tag);
|
|
56892
|
+
}
|
|
56893
|
+
const graduating = migrateOnlyClaim && isPushAdopted(state);
|
|
56894
|
+
const set = {
|
|
56895
|
+
...graduating ? { schemaHash: null, schemaSnapshot: null } : {
|
|
56896
|
+
...input.schemaHash !== undefined && { schemaHash: input.schemaHash },
|
|
56897
|
+
...input.schemaSnapshot !== undefined && {
|
|
56898
|
+
schemaSnapshot: input.schemaSnapshot
|
|
56899
|
+
}
|
|
56900
|
+
},
|
|
56901
|
+
schemaFingerprint: live.fingerprint,
|
|
56902
|
+
baselineSource: "manual-baseline",
|
|
56903
|
+
updatedAt: new Date
|
|
56904
|
+
};
|
|
56905
|
+
await this.persistDeploymentState(game2.id, set);
|
|
56906
|
+
addEvent("deployment_state.baseline_recorded", {
|
|
56907
|
+
"app.game.id": game2.id,
|
|
56908
|
+
"app.deployment_state.baseline_source": "manual-baseline",
|
|
56909
|
+
"app.deployment_state.baseline_ledger_rows": recordedTags.length,
|
|
56910
|
+
"app.deployment_state.baseline_has_snapshot": input.schemaSnapshot !== undefined,
|
|
56911
|
+
"app.deployment_state.baseline_graduated_from_push": graduating
|
|
56912
|
+
});
|
|
56913
|
+
return this.get(slug, user);
|
|
56914
|
+
}
|
|
56915
|
+
async realignMigration(slug, tag, checksum, user) {
|
|
56916
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56917
|
+
const cf = this.requireCloudflare();
|
|
56918
|
+
const databaseId = await this.resolveDatabaseId(slug, game2.id);
|
|
56919
|
+
const ledger = await cf.d1.readMigrationLedger(databaseId);
|
|
56920
|
+
if (!ledger.some((row) => row.tag === tag)) {
|
|
56921
|
+
throw new NotFoundError("Applied migration", tag);
|
|
56922
|
+
}
|
|
56923
|
+
const updated = await cf.d1.updateLedgerChecksum(databaseId, { tag, checksum });
|
|
56924
|
+
if (!updated) {
|
|
56925
|
+
throw new NotFoundError("Applied migration", tag);
|
|
56926
|
+
}
|
|
56927
|
+
addEvent("deployment_state.migration_realigned", {
|
|
56928
|
+
"app.game.id": game2.id,
|
|
56929
|
+
"app.d1.migration_tag": tag,
|
|
56930
|
+
"app.user.id": user.id
|
|
56931
|
+
});
|
|
56932
|
+
return { tag, checksum, checksumAlgo: MIGRATION_CHECKSUM_ALGO };
|
|
56933
|
+
}
|
|
56934
|
+
async persistDeploymentState(gameId, set) {
|
|
56935
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, ...set }).onConflictDoUpdate({ target: gameDeploymentState.gameId, set });
|
|
56936
|
+
}
|
|
56937
|
+
async updateDeploymentState(gameId, set) {
|
|
56938
|
+
const updated = await this.deps.db.update(gameDeploymentState).set(set).where(eq(gameDeploymentState.gameId, gameId)).returning({ gameId: gameDeploymentState.gameId });
|
|
56939
|
+
return updated.length > 0;
|
|
56940
|
+
}
|
|
56941
|
+
async resolveMigration(slug, tag, input, user) {
|
|
56942
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56943
|
+
const cf = this.requireCloudflare();
|
|
56944
|
+
const databaseId = await this.resolveDatabaseId(slug, game2.id);
|
|
56945
|
+
const [ledger, live] = await Promise.all([
|
|
56946
|
+
cf.d1.readMigrationLedger(databaseId),
|
|
56947
|
+
cf.d1.fingerprintSchema(databaseId)
|
|
56948
|
+
]);
|
|
56949
|
+
const exists2 = ledger.some((row) => row.tag === tag);
|
|
56950
|
+
if (input.resolution === "applied") {
|
|
56951
|
+
if (!input.checksum) {
|
|
56952
|
+
throw new ValidationError("Resolving a migration as 'applied' requires its checksum");
|
|
56953
|
+
}
|
|
56954
|
+
if (exists2) {
|
|
56955
|
+
throw new AlreadyExistsError(`Migration '${tag}' is already recorded as applied`);
|
|
56956
|
+
}
|
|
56957
|
+
await cf.d1.ensureMigrationLedger(databaseId);
|
|
56958
|
+
await cf.d1.recordLedgerRows(databaseId, {
|
|
56959
|
+
rows: [{ tag, checksum: input.checksum }],
|
|
56960
|
+
deployId: `resolve:${crypto.randomUUID()}`,
|
|
56961
|
+
appliedBy: user.id,
|
|
56962
|
+
source: "resolve"
|
|
56963
|
+
});
|
|
56964
|
+
} else {
|
|
56965
|
+
if (!exists2) {
|
|
56966
|
+
throw new NotFoundError("Migration ledger row", tag);
|
|
56967
|
+
}
|
|
56968
|
+
await cf.d1.deleteLedgerRow(databaseId, tag);
|
|
56969
|
+
}
|
|
56970
|
+
const fingerprintRecorded = await this.updateDeploymentState(game2.id, {
|
|
56971
|
+
schemaFingerprint: live.fingerprint,
|
|
56972
|
+
updatedAt: new Date
|
|
56973
|
+
});
|
|
56974
|
+
addEvent("deployment_state.migration_resolved", {
|
|
56975
|
+
"app.game.id": game2.id,
|
|
56976
|
+
"app.d1.migration_tag": tag,
|
|
56977
|
+
"app.deployment_state.resolution": input.resolution,
|
|
56978
|
+
"app.deployment_state.fingerprint_rerecorded": fingerprintRecorded,
|
|
56979
|
+
"app.user.id": user.id
|
|
56980
|
+
});
|
|
56981
|
+
return {
|
|
56982
|
+
tag,
|
|
56983
|
+
resolution: input.resolution,
|
|
56984
|
+
schemaFingerprint: live.fingerprint,
|
|
56985
|
+
fingerprintRecorded
|
|
56986
|
+
};
|
|
56987
|
+
}
|
|
56988
|
+
async history(slug, user, options = {}) {
|
|
56989
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
56990
|
+
const limit = options.limit ?? 20;
|
|
56991
|
+
const [jobs, events] = await Promise.all([
|
|
56992
|
+
this.deps.db.select({
|
|
56993
|
+
status: gameDeployJobs.status,
|
|
56994
|
+
createdAt: gameDeployJobs.createdAt,
|
|
56995
|
+
completedAt: gameDeployJobs.completedAt,
|
|
56996
|
+
deployId: gameDeployJobs.deployId,
|
|
56997
|
+
error: gameDeployJobs.error,
|
|
56998
|
+
email: users.email
|
|
56999
|
+
}).from(gameDeployJobs).leftJoin(users, eq(gameDeployJobs.userId, users.id)).where(eq(gameDeployJobs.gameId, game2.id)).orderBy(desc(deployJobInstant())).limit(limit),
|
|
57000
|
+
this.deps.db.select({
|
|
57001
|
+
kind: gameDeployEvents.kind,
|
|
57002
|
+
payload: gameDeployEvents.payload,
|
|
57003
|
+
createdAt: gameDeployEvents.createdAt,
|
|
57004
|
+
email: users.email
|
|
57005
|
+
}).from(gameDeployEvents).leftJoin(users, eq(gameDeployEvents.userId, users.id)).where(eq(gameDeployEvents.gameId, game2.id)).orderBy(desc(gameDeployEvents.createdAt)).limit(limit)
|
|
57006
|
+
]);
|
|
57007
|
+
setAttributes({
|
|
57008
|
+
"app.deployment_state.history_jobs": jobs.length,
|
|
57009
|
+
"app.deployment_state.history_events": events.length
|
|
57010
|
+
});
|
|
57011
|
+
const deploys = jobs.map((job) => ({
|
|
57012
|
+
kind: "deploy",
|
|
57013
|
+
status: job.status,
|
|
57014
|
+
at: (job.completedAt ?? job.createdAt).toISOString(),
|
|
57015
|
+
completedAt: job.completedAt?.toISOString() ?? null,
|
|
57016
|
+
by: job.email ?? DELETED_ACCOUNT_LABEL,
|
|
57017
|
+
deployId: job.deployId,
|
|
57018
|
+
error: job.error
|
|
57019
|
+
}));
|
|
57020
|
+
const merged = [...deploys, ...events.flatMap(historyEventEntry)].toSorted((a, b) => a.at < b.at ? 1 : -1).slice(0, limit);
|
|
57021
|
+
return { deploys: merged };
|
|
57022
|
+
}
|
|
57023
|
+
async restorePoints(slug, user) {
|
|
57024
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57025
|
+
const retentionCutoff = new Date(Date.now() - D1_TIME_TRAVEL_RETENTION_DAYS * 24 * 60 * 60 * 1000);
|
|
57026
|
+
const [currentDatabaseId, rows, state] = await Promise.all([
|
|
57027
|
+
this.findDatabaseId(slug, game2.id),
|
|
57028
|
+
this.deps.db.query.gameDeployments.findMany({
|
|
57029
|
+
where: and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "game"), isNotNull(gameDeployments.timeTravelBookmark), gte(gameDeployments.deployedAt, retentionCutoff)),
|
|
57030
|
+
orderBy: desc(gameDeployments.deployedAt),
|
|
57031
|
+
limit: 100,
|
|
57032
|
+
columns: {
|
|
57033
|
+
id: true,
|
|
57034
|
+
deployedAt: true,
|
|
57035
|
+
bookmarkCapturedAt: true,
|
|
57036
|
+
isActive: true,
|
|
57037
|
+
resources: true
|
|
57038
|
+
}
|
|
57039
|
+
}),
|
|
57040
|
+
this.findSchemaHashState(game2.id)
|
|
57041
|
+
]);
|
|
57042
|
+
if (isPushAdopted(state)) {
|
|
57043
|
+
return { restorePoints: [], restoreUnsupported: true };
|
|
57044
|
+
}
|
|
57045
|
+
if (!currentDatabaseId) {
|
|
57046
|
+
return { restorePoints: [], restoreUnsupported: false };
|
|
57047
|
+
}
|
|
57048
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
57049
|
+
const restorable = rows.filter((row) => databaseIdFromResources(row.resources, deploymentId) === currentDatabaseId && restorePointCapturedAt(row) >= retentionCutoff).slice(0, 20);
|
|
57050
|
+
setAttributes({ "app.deployment_state.restore_points": restorable.length });
|
|
57051
|
+
return {
|
|
57052
|
+
restorePoints: restorable.map((row) => ({
|
|
57053
|
+
id: row.id,
|
|
57054
|
+
capturedAt: restorePointCapturedAt(row).toISOString(),
|
|
57055
|
+
active: row.isActive
|
|
57056
|
+
})),
|
|
57057
|
+
restoreUnsupported: false
|
|
57058
|
+
};
|
|
57059
|
+
}
|
|
57060
|
+
async restoreToBookmark(slug, input, user) {
|
|
57061
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57062
|
+
const cf = this.requireCloudflare();
|
|
57063
|
+
const [row, state, currentDatabaseId, runningJob] = await Promise.all([
|
|
57064
|
+
this.deps.db.query.gameDeployments.findFirst({
|
|
57065
|
+
where: and(eq(gameDeployments.id, input.restorePointId), eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "game")),
|
|
57066
|
+
columns: {
|
|
57067
|
+
id: true,
|
|
57068
|
+
timeTravelBookmark: true,
|
|
57069
|
+
deployedAt: true,
|
|
57070
|
+
bookmarkCapturedAt: true,
|
|
57071
|
+
resources: true
|
|
57072
|
+
}
|
|
57073
|
+
}),
|
|
57074
|
+
this.findSchemaHashState(game2.id),
|
|
57075
|
+
this.findDatabaseId(slug, game2.id),
|
|
57076
|
+
this.deps.db.query.gameDeployJobs.findFirst({
|
|
57077
|
+
where: and(eq(gameDeployJobs.gameId, game2.id), or(eq(gameDeployJobs.status, "pending"), and(eq(gameDeployJobs.status, "running"), gt(gameDeployJobs.leaseExpiresAt, new Date)))),
|
|
57078
|
+
columns: { id: true }
|
|
57079
|
+
})
|
|
57080
|
+
]);
|
|
57081
|
+
if (!row?.timeTravelBookmark) {
|
|
57082
|
+
throw new NotFoundError("Bookmark", input.restorePointId);
|
|
57083
|
+
}
|
|
57084
|
+
if (isPushAdopted(state)) {
|
|
57085
|
+
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 });
|
|
57086
|
+
}
|
|
57087
|
+
if (!currentDatabaseId) {
|
|
57088
|
+
throw new ValidationError("Game has no deployed database — deploy with a database binding first");
|
|
57089
|
+
}
|
|
57090
|
+
const databaseId = currentDatabaseId;
|
|
57091
|
+
const deploymentId = getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
57092
|
+
if (databaseIdFromResources(row.resources, deploymentId) !== databaseId) {
|
|
57093
|
+
throw new ValidationError("This bookmark was captured on a previous database (a reset replaced it since) and can no longer be restored");
|
|
57094
|
+
}
|
|
57095
|
+
const retentionCutoff = new Date(Date.now() - D1_TIME_TRAVEL_RETENTION_DAYS * 24 * 60 * 60 * 1000);
|
|
57096
|
+
if (restorePointCapturedAt(row) < retentionCutoff) {
|
|
57097
|
+
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 });
|
|
57098
|
+
}
|
|
57099
|
+
if (runningJob) {
|
|
57100
|
+
throw new ValidationError("A deploy is currently running for this game. Wait for it to finish, then restore.", { code: DEPLOY_ERROR_CODES.restoreBlockedByDeploy });
|
|
57101
|
+
}
|
|
57102
|
+
const restored = await cf.d1.restoreBookmark(databaseId, row.timeTravelBookmark);
|
|
57103
|
+
const restoredTo = restorePointCapturedAt(row).toISOString();
|
|
57104
|
+
let live;
|
|
57105
|
+
let fingerprintRecorded;
|
|
57106
|
+
try {
|
|
57107
|
+
live = await cf.d1.fingerprintSchema(databaseId);
|
|
57108
|
+
fingerprintRecorded = await this.updateDeploymentState(game2.id, {
|
|
57109
|
+
schemaFingerprint: live.fingerprint,
|
|
57110
|
+
updatedAt: new Date
|
|
57111
|
+
});
|
|
57112
|
+
await this.deps.db.insert(gameDeployEvents).values({
|
|
57113
|
+
gameId: game2.id,
|
|
57114
|
+
userId: user.id,
|
|
57115
|
+
kind: "restore",
|
|
57116
|
+
payload: { restoredTo, previousBookmark: restored.previousBookmark }
|
|
57117
|
+
});
|
|
57118
|
+
} catch (error) {
|
|
57119
|
+
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 });
|
|
57120
|
+
}
|
|
57121
|
+
addEvent("deployment_state.bookmark_restored", {
|
|
57122
|
+
"app.game.id": game2.id,
|
|
57123
|
+
"app.deployment_state.restore_point": row.id,
|
|
57124
|
+
"app.deployment_state.fingerprint_rerecorded": fingerprintRecorded,
|
|
57125
|
+
"app.user.id": user.id
|
|
57126
|
+
});
|
|
57127
|
+
return {
|
|
57128
|
+
restorePointId: row.id,
|
|
57129
|
+
restoredTo,
|
|
57130
|
+
schemaFingerprint: live.fingerprint,
|
|
57131
|
+
fingerprintRecorded,
|
|
57132
|
+
previousBookmark: restored.previousBookmark
|
|
57133
|
+
};
|
|
57134
|
+
}
|
|
57135
|
+
async reportDeployBlocked(slug, user, report) {
|
|
57136
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57137
|
+
const recent = await this.deps.db.query.gameDeployEvents.findMany({
|
|
57138
|
+
where: and(eq(gameDeployEvents.gameId, game2.id), eq(gameDeployEvents.kind, "blocked"), gt(gameDeployEvents.createdAt, new Date(Date.now() - BLOCKED_ALERT_DEDUP_MS))),
|
|
57139
|
+
orderBy: desc(gameDeployEvents.createdAt),
|
|
57140
|
+
limit: 10,
|
|
57141
|
+
columns: { payload: true }
|
|
57142
|
+
});
|
|
57143
|
+
const duplicate = recent.some((event) => ("code" in event.payload) && event.payload.code === report.code);
|
|
57144
|
+
await this.deps.db.insert(gameDeployEvents).values({
|
|
57145
|
+
gameId: game2.id,
|
|
57146
|
+
userId: user.id,
|
|
57147
|
+
kind: "blocked",
|
|
57148
|
+
payload: { code: report.code, reason: report.reason }
|
|
57149
|
+
});
|
|
57150
|
+
addEvent("deployment_state.deploy_blocked", {
|
|
57151
|
+
"app.game.id": game2.id,
|
|
57152
|
+
"app.deploy.blocked_code": report.code,
|
|
57153
|
+
"app.user.id": user.id,
|
|
57154
|
+
"app.alerts.deduped": duplicate
|
|
57155
|
+
});
|
|
57156
|
+
if (!duplicate) {
|
|
57157
|
+
await this.deps.alerts.notifyDeployBlocked({
|
|
57158
|
+
slug,
|
|
57159
|
+
displayName: game2.displayName,
|
|
57160
|
+
reason: report.reason,
|
|
57161
|
+
developer: { id: user.id, email: user.email ?? null }
|
|
57162
|
+
});
|
|
57163
|
+
}
|
|
57164
|
+
}
|
|
57165
|
+
}
|
|
57166
|
+
var BLOCKED_ALERT_DEDUP_MS;
|
|
57167
|
+
var init_deployment_state_service = __esm(() => {
|
|
57168
|
+
init_drizzle_orm();
|
|
57169
|
+
init_src4();
|
|
57170
|
+
init_src();
|
|
57171
|
+
init_helpers_index();
|
|
57172
|
+
init_tables_index();
|
|
57173
|
+
init_spans();
|
|
57174
|
+
init_game2();
|
|
57175
|
+
init_errors();
|
|
57176
|
+
init_baseline_validation_util();
|
|
57177
|
+
init_deployment_util();
|
|
57178
|
+
init_migration_util();
|
|
57179
|
+
init_secrets_util();
|
|
57180
|
+
BLOCKED_ALERT_DEDUP_MS = 600000;
|
|
57181
|
+
});
|
|
54994
57182
|
|
|
54995
57183
|
class DomainService {
|
|
54996
57184
|
deps;
|
|
@@ -55355,6 +57543,7 @@ var init_kv_service = __esm(() => {
|
|
|
55355
57543
|
|
|
55356
57544
|
class SecretsService {
|
|
55357
57545
|
deps;
|
|
57546
|
+
pepperPromise = null;
|
|
55358
57547
|
constructor(deps) {
|
|
55359
57548
|
this.deps = deps;
|
|
55360
57549
|
}
|
|
@@ -55367,36 +57556,69 @@ class SecretsService {
|
|
|
55367
57556
|
getGameDeploymentId(slug) {
|
|
55368
57557
|
return getGameDeploymentId(slug, this.deps.config.sstStage);
|
|
55369
57558
|
}
|
|
55370
|
-
|
|
55371
|
-
|
|
55372
|
-
|
|
55373
|
-
|
|
55374
|
-
|
|
55375
|
-
|
|
55376
|
-
|
|
55377
|
-
|
|
55378
|
-
|
|
55379
|
-
|
|
55380
|
-
|
|
55381
|
-
|
|
55382
|
-
|
|
55383
|
-
}
|
|
55384
|
-
|
|
55385
|
-
|
|
57559
|
+
getPepper() {
|
|
57560
|
+
const pepperSecret = this.deps.config.secretsManifestPepper;
|
|
57561
|
+
if (!pepperSecret) {
|
|
57562
|
+
throw new ValidationError("Secrets manifest is not configured (missing manifest pepper secret)");
|
|
57563
|
+
}
|
|
57564
|
+
this.pepperPromise ??= deriveSecretsManifestPepper(pepperSecret);
|
|
57565
|
+
return this.pepperPromise;
|
|
57566
|
+
}
|
|
57567
|
+
async computeManifestEntries(gameId, secrets) {
|
|
57568
|
+
const pepper = await this.getPepper();
|
|
57569
|
+
const entries = {};
|
|
57570
|
+
for (const [key, value] of Object.entries(secrets)) {
|
|
57571
|
+
entries[key] = await computeSecretDigest(pepper, { gameId, key, value });
|
|
57572
|
+
}
|
|
57573
|
+
return entries;
|
|
57574
|
+
}
|
|
57575
|
+
async readManifest(gameId) {
|
|
57576
|
+
const state = await this.deps.db.query.gameDeploymentState.findFirst({
|
|
57577
|
+
where: eq(gameDeploymentState.gameId, gameId),
|
|
57578
|
+
columns: { secretsManifest: true }
|
|
57579
|
+
});
|
|
57580
|
+
return state?.secretsManifest ?? {};
|
|
57581
|
+
}
|
|
57582
|
+
async upsertManifestEntries(gameId, entries) {
|
|
57583
|
+
const merged = sql`coalesce(${gameDeploymentState.secretsManifest}, '{}'::jsonb) || ${JSON.stringify(entries)}::jsonb`;
|
|
57584
|
+
await this.deps.db.insert(gameDeploymentState).values({ gameId, secretsManifest: entries, updatedAt: new Date }).onConflictDoUpdate({
|
|
57585
|
+
target: gameDeploymentState.gameId,
|
|
57586
|
+
set: { secretsManifest: merged, updatedAt: new Date }
|
|
57587
|
+
});
|
|
57588
|
+
}
|
|
57589
|
+
async removeManifestKey(gameId, key) {
|
|
57590
|
+
await this.deps.db.update(gameDeploymentState).set({
|
|
57591
|
+
secretsManifest: sql`coalesce(${gameDeploymentState.secretsManifest}, '{}'::jsonb) - ${key}::text`,
|
|
57592
|
+
updatedAt: new Date
|
|
57593
|
+
}).where(eq(gameDeploymentState.gameId, gameId));
|
|
57594
|
+
}
|
|
57595
|
+
assertNoReservedKeys(keys, operation) {
|
|
57596
|
+
for (const key of keys) {
|
|
57597
|
+
if (INTERNAL_SECRET_KEYS.includes(key)) {
|
|
55386
57598
|
setAttributes({
|
|
55387
|
-
"app.secrets.operation":
|
|
55388
|
-
"app.secrets.
|
|
55389
|
-
"app.secrets.game_deployed": false
|
|
57599
|
+
"app.secrets.operation": operation,
|
|
57600
|
+
"app.secrets.reserved_key_rejected": true
|
|
55390
57601
|
});
|
|
55391
|
-
|
|
57602
|
+
throw new ValidationError(operation === "set" ? `Cannot set reserved secret "${key}"` : `Reserved secret "${key}" cannot be managed — remove it locally`);
|
|
55392
57603
|
}
|
|
55393
|
-
throw error;
|
|
55394
57604
|
}
|
|
55395
57605
|
}
|
|
55396
|
-
async
|
|
57606
|
+
async listKeys(slug, user) {
|
|
55397
57607
|
await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
55398
57608
|
const cf = this.getCloudflare();
|
|
55399
57609
|
const deploymentId = this.getGameDeploymentId(slug);
|
|
57610
|
+
const userKeys = await listUserSecretKeys(cf, deploymentId);
|
|
57611
|
+
setAttributes({
|
|
57612
|
+
"app.secrets.operation": "list",
|
|
57613
|
+
"app.secrets.count": userKeys?.length ?? 0,
|
|
57614
|
+
"app.secrets.game_deployed": userKeys !== null
|
|
57615
|
+
});
|
|
57616
|
+
return userKeys ?? [];
|
|
57617
|
+
}
|
|
57618
|
+
async setSecrets(slug, newSecrets, user) {
|
|
57619
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57620
|
+
const cf = this.getCloudflare();
|
|
57621
|
+
const deploymentId = this.getGameDeploymentId(slug);
|
|
55400
57622
|
const secretKeys = Object.keys(newSecrets);
|
|
55401
57623
|
if (secretKeys.length === 0) {
|
|
55402
57624
|
throw new ValidationError("At least one secret must be provided");
|
|
@@ -55405,14 +57627,9 @@ class SecretsService {
|
|
|
55405
57627
|
if (typeof value !== "string") {
|
|
55406
57628
|
throw new ValidationError(`Secret value for "${key}" must be a string`);
|
|
55407
57629
|
}
|
|
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
57630
|
}
|
|
57631
|
+
this.assertNoReservedKeys(secretKeys, "set");
|
|
57632
|
+
const manifestEntries = await this.computeManifestEntries(game2.id, newSecrets);
|
|
55416
57633
|
try {
|
|
55417
57634
|
const prefixedSecrets = {};
|
|
55418
57635
|
for (const [key, value] of Object.entries(newSecrets)) {
|
|
@@ -55425,8 +57642,6 @@ class SecretsService {
|
|
|
55425
57642
|
"app.secrets.game_deployed": true,
|
|
55426
57643
|
"app.secrets.reserved_key_rejected": false
|
|
55427
57644
|
});
|
|
55428
|
-
const allKeys = await cf.listSecrets(deploymentId);
|
|
55429
|
-
return allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
|
|
55430
57645
|
} catch (error) {
|
|
55431
57646
|
const message = errorMessage2(error);
|
|
55432
57647
|
if (message.includes("not found") || message.includes("10007")) {
|
|
@@ -55438,6 +57653,9 @@ class SecretsService {
|
|
|
55438
57653
|
}
|
|
55439
57654
|
throw error;
|
|
55440
57655
|
}
|
|
57656
|
+
await this.upsertManifestEntries(game2.id, manifestEntries);
|
|
57657
|
+
const allKeys = await cf.listSecrets(deploymentId);
|
|
57658
|
+
return allKeys.filter((k) => k.startsWith(SECRETS_PREFIX)).map((k) => k.slice(SECRETS_PREFIX.length));
|
|
55441
57659
|
}
|
|
55442
57660
|
async deleteSecret(slug, key, user) {
|
|
55443
57661
|
if (INTERNAL_SECRET_KEYS.includes(key)) {
|
|
@@ -55447,19 +57665,25 @@ class SecretsService {
|
|
|
55447
57665
|
});
|
|
55448
57666
|
throw new ValidationError(`Cannot delete reserved secret "${key}"`);
|
|
55449
57667
|
}
|
|
55450
|
-
await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57668
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
55451
57669
|
const cf = this.getCloudflare();
|
|
55452
57670
|
const deploymentId = this.getGameDeploymentId(slug);
|
|
57671
|
+
const manifest = await this.readManifest(game2.id);
|
|
57672
|
+
const managed = key in manifest;
|
|
55453
57673
|
try {
|
|
55454
57674
|
const prefixedKey = `${SECRETS_PREFIX}${key}`;
|
|
55455
57675
|
const existingKeys = await cf.listSecrets(deploymentId);
|
|
55456
|
-
|
|
57676
|
+
const onWorker = existingKeys.includes(prefixedKey);
|
|
57677
|
+
if (!onWorker && !managed) {
|
|
55457
57678
|
throw new NotFoundError("Secret", key);
|
|
55458
57679
|
}
|
|
55459
|
-
|
|
57680
|
+
if (onWorker) {
|
|
57681
|
+
await cf.deleteSecret(deploymentId, prefixedKey);
|
|
57682
|
+
}
|
|
55460
57683
|
setAttributes({
|
|
55461
57684
|
"app.secrets.operation": "delete",
|
|
55462
57685
|
"app.secrets.game_deployed": true,
|
|
57686
|
+
"app.secrets.managed": managed,
|
|
55463
57687
|
"app.secrets.reserved_key_rejected": false
|
|
55464
57688
|
});
|
|
55465
57689
|
} catch (error) {
|
|
@@ -55476,14 +57700,45 @@ class SecretsService {
|
|
|
55476
57700
|
}
|
|
55477
57701
|
throw error;
|
|
55478
57702
|
}
|
|
57703
|
+
if (managed) {
|
|
57704
|
+
await this.removeManifestKey(game2.id, key);
|
|
57705
|
+
}
|
|
57706
|
+
}
|
|
57707
|
+
async diff(slug, localSecrets, user) {
|
|
57708
|
+
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
57709
|
+
const deploymentId = this.getGameDeploymentId(slug);
|
|
57710
|
+
this.assertNoReservedKeys(Object.keys(localSecrets), "diff");
|
|
57711
|
+
const [manifest, remoteKeys, localDigests] = await Promise.all([
|
|
57712
|
+
this.readManifest(game2.id),
|
|
57713
|
+
this.deps.cloudflare ? listUserSecretKeys(this.deps.cloudflare, deploymentId) : null,
|
|
57714
|
+
this.computeManifestEntries(game2.id, localSecrets)
|
|
57715
|
+
]);
|
|
57716
|
+
const verdicts = computeSecretsDiff({
|
|
57717
|
+
localDigests,
|
|
57718
|
+
manifest,
|
|
57719
|
+
remoteKeys: remoteKeys ?? []
|
|
57720
|
+
});
|
|
57721
|
+
setAttributes({
|
|
57722
|
+
"app.secrets.operation": "diff",
|
|
57723
|
+
"app.secrets.game_deployed": remoteKeys !== null,
|
|
57724
|
+
"app.secrets.diff_added": verdicts.added.length,
|
|
57725
|
+
"app.secrets.diff_changed": verdicts.changed.length,
|
|
57726
|
+
"app.secrets.diff_unchanged": verdicts.unchanged.length,
|
|
57727
|
+
"app.secrets.diff_remote_only_managed": verdicts.remoteOnlyManaged.length,
|
|
57728
|
+
"app.secrets.diff_remote_only_unmanaged": verdicts.remoteOnlyUnmanaged.length
|
|
57729
|
+
});
|
|
57730
|
+
return verdicts;
|
|
55479
57731
|
}
|
|
55480
57732
|
}
|
|
55481
57733
|
var INTERNAL_SECRET_KEYS;
|
|
55482
57734
|
var init_secrets_service = __esm(() => {
|
|
57735
|
+
init_drizzle_orm();
|
|
55483
57736
|
init_src();
|
|
57737
|
+
init_tables_index();
|
|
55484
57738
|
init_spans();
|
|
55485
57739
|
init_errors();
|
|
55486
57740
|
init_deployment_util();
|
|
57741
|
+
init_secrets_util();
|
|
55487
57742
|
INTERNAL_SECRET_KEYS = ["PLAYCADEMY_API_KEY", "GAME_ID", "PLAYCADEMY_BASE_URL"];
|
|
55488
57743
|
});
|
|
55489
57744
|
function prefixSecrets(secrets) {
|
|
@@ -56019,6 +58274,9 @@ var SINGLE_EMOJI_REGEX;
|
|
|
56019
58274
|
var init_emoji = __esm(() => {
|
|
56020
58275
|
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
58276
|
});
|
|
58277
|
+
function requestsBinding(binding) {
|
|
58278
|
+
return binding === true || Array.isArray(binding) && binding.length > 0;
|
|
58279
|
+
}
|
|
56022
58280
|
var HttpUrlSchema;
|
|
56023
58281
|
var GameEmojiSchema;
|
|
56024
58282
|
var GameMetadataRecordSchema;
|
|
@@ -56026,6 +58284,7 @@ var InsertGameSchema;
|
|
|
56026
58284
|
var UpdateGameSchema;
|
|
56027
58285
|
var InsertGameDeploymentSchema;
|
|
56028
58286
|
var InsertGameDeployJobSchema;
|
|
58287
|
+
var InsertGameDeploymentStateSchema;
|
|
56029
58288
|
var UpsertGameMetadataSchema;
|
|
56030
58289
|
var PatchGameMetadataSchema;
|
|
56031
58290
|
var AddGameMemberSchema;
|
|
@@ -56038,11 +58297,22 @@ var ALLOWED_UPLOAD_EXTENSIONS;
|
|
|
56038
58297
|
var InitiateUploadSchema;
|
|
56039
58298
|
var AddCustomHostnameSchema;
|
|
56040
58299
|
var SetSecretsRequestSchema;
|
|
58300
|
+
var SecretsDiffRequestSchema;
|
|
56041
58301
|
var SeedRequestSchema;
|
|
56042
58302
|
var SchemaInfoSchema;
|
|
56043
|
-
var DatabaseResetRequestSchema;
|
|
56044
58303
|
var VerifyTokenSchema;
|
|
56045
58304
|
var KVSeedRequestSchema;
|
|
58305
|
+
var DeployMigrationSchema;
|
|
58306
|
+
var DeployDatabaseSchema;
|
|
58307
|
+
var DatabaseResetDatabaseSchema;
|
|
58308
|
+
var DatabaseResetRequestSchema;
|
|
58309
|
+
var BaselineEvidenceSchema;
|
|
58310
|
+
var DeployBaselineSchema;
|
|
58311
|
+
var DeploymentStateBaselineSchema;
|
|
58312
|
+
var MigrationRealignSchema;
|
|
58313
|
+
var MigrationResolveSchema;
|
|
58314
|
+
var DatabaseRestoreSchema;
|
|
58315
|
+
var DeployBlockedReportSchema;
|
|
56046
58316
|
var DeployRequestSchema;
|
|
56047
58317
|
var init_schemas2 = __esm(() => {
|
|
56048
58318
|
init_drizzle_zod();
|
|
@@ -56122,6 +58392,9 @@ var init_schemas2 = __esm(() => {
|
|
|
56122
58392
|
InsertGameDeployJobSchema = createInsertSchema(gameDeployJobs, {
|
|
56123
58393
|
status: exports_external.enum(deployJobStatusEnum.enumValues)
|
|
56124
58394
|
});
|
|
58395
|
+
InsertGameDeploymentStateSchema = createInsertSchema(gameDeploymentState, {
|
|
58396
|
+
secretsManifest: exports_external.record(exports_external.string(), exports_external.string()).nullable().optional()
|
|
58397
|
+
});
|
|
56125
58398
|
UpsertGameMetadataSchema = exports_external.object({
|
|
56126
58399
|
displayName: exports_external.string().min(1),
|
|
56127
58400
|
platform: exports_external.enum(gamePlatformEnum.enumValues),
|
|
@@ -56179,6 +58452,9 @@ var init_schemas2 = __esm(() => {
|
|
|
56179
58452
|
hostname: exports_external.string().min(1).max(255)
|
|
56180
58453
|
});
|
|
56181
58454
|
SetSecretsRequestSchema = exports_external.record(exports_external.string().min(1), exports_external.string());
|
|
58455
|
+
SecretsDiffRequestSchema = exports_external.object({
|
|
58456
|
+
secrets: exports_external.record(exports_external.string().min(1), exports_external.string())
|
|
58457
|
+
});
|
|
56182
58458
|
SeedRequestSchema = exports_external.object({
|
|
56183
58459
|
code: exports_external.string().min(1, "Seed code is required"),
|
|
56184
58460
|
secrets: exports_external.record(exports_external.string(), exports_external.string()).optional()
|
|
@@ -56187,9 +58463,6 @@ var init_schemas2 = __esm(() => {
|
|
|
56187
58463
|
sql: exports_external.string(),
|
|
56188
58464
|
hash: exports_external.string()
|
|
56189
58465
|
});
|
|
56190
|
-
DatabaseResetRequestSchema = exports_external.object({
|
|
56191
|
-
schema: SchemaInfoSchema.optional()
|
|
56192
|
-
});
|
|
56193
58466
|
VerifyTokenSchema = exports_external.object({
|
|
56194
58467
|
token: exports_external.string().min(1, "Token is required")
|
|
56195
58468
|
});
|
|
@@ -56201,8 +58474,126 @@ var init_schemas2 = __esm(() => {
|
|
|
56201
58474
|
metadata: exports_external.record(exports_external.unknown()).optional()
|
|
56202
58475
|
}))
|
|
56203
58476
|
});
|
|
58477
|
+
DeployMigrationSchema = exports_external.object({
|
|
58478
|
+
tag: exports_external.string().min(1),
|
|
58479
|
+
statements: exports_external.array(exports_external.string().min(1)).min(1),
|
|
58480
|
+
checksum: exports_external.string().min(1)
|
|
58481
|
+
});
|
|
58482
|
+
DeployDatabaseSchema = exports_external.discriminatedUnion("mode", [
|
|
58483
|
+
exports_external.object({
|
|
58484
|
+
mode: exports_external.literal("push"),
|
|
58485
|
+
sql: exports_external.string(),
|
|
58486
|
+
baselineHash: exports_external.string().nullable(),
|
|
58487
|
+
nextSnapshot: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
58488
|
+
nextHash: exports_external.string().min(1),
|
|
58489
|
+
acceptDataLoss: exports_external.boolean().optional()
|
|
58490
|
+
}),
|
|
58491
|
+
exports_external.object({
|
|
58492
|
+
mode: exports_external.literal("migrate"),
|
|
58493
|
+
migrations: exports_external.array(DeployMigrationSchema)
|
|
58494
|
+
})
|
|
58495
|
+
]);
|
|
58496
|
+
DatabaseResetDatabaseSchema = exports_external.discriminatedUnion("mode", [
|
|
58497
|
+
exports_external.object({
|
|
58498
|
+
mode: exports_external.literal("push"),
|
|
58499
|
+
sql: exports_external.string(),
|
|
58500
|
+
nextSnapshot: exports_external.record(exports_external.string(), exports_external.unknown()),
|
|
58501
|
+
nextHash: exports_external.string().min(1)
|
|
58502
|
+
}),
|
|
58503
|
+
exports_external.object({
|
|
58504
|
+
mode: exports_external.literal("migrate"),
|
|
58505
|
+
migrations: exports_external.array(DeployMigrationSchema)
|
|
58506
|
+
})
|
|
58507
|
+
]);
|
|
58508
|
+
DatabaseResetRequestSchema = exports_external.object({
|
|
58509
|
+
schema: SchemaInfoSchema.optional(),
|
|
58510
|
+
database: DatabaseResetDatabaseSchema.optional()
|
|
58511
|
+
}).refine((data) => !(data.schema && data.database), {
|
|
58512
|
+
message: "Send either the database payload or the legacy schema field, not both",
|
|
58513
|
+
path: ["database"]
|
|
58514
|
+
});
|
|
58515
|
+
BaselineEvidenceSchema = exports_external.array(exports_external.object({
|
|
58516
|
+
tag: exports_external.string().min(1),
|
|
58517
|
+
generatedAt: exports_external.string().min(1),
|
|
58518
|
+
createsTables: exports_external.array(exports_external.object({
|
|
58519
|
+
name: exports_external.string().min(1),
|
|
58520
|
+
columns: exports_external.array(exports_external.string())
|
|
58521
|
+
})),
|
|
58522
|
+
addsColumns: exports_external.array(exports_external.object({
|
|
58523
|
+
table: exports_external.string().min(1),
|
|
58524
|
+
column: exports_external.string().min(1)
|
|
58525
|
+
})),
|
|
58526
|
+
createsIndexes: exports_external.array(exports_external.string()),
|
|
58527
|
+
createsViews: exports_external.array(exports_external.string()),
|
|
58528
|
+
dropsTables: exports_external.array(exports_external.string()),
|
|
58529
|
+
dropsColumns: exports_external.array(exports_external.object({
|
|
58530
|
+
table: exports_external.string().min(1),
|
|
58531
|
+
column: exports_external.string().min(1)
|
|
58532
|
+
})),
|
|
58533
|
+
dropsIndexes: exports_external.array(exports_external.string()),
|
|
58534
|
+
dropsViews: exports_external.array(exports_external.string())
|
|
58535
|
+
})).optional();
|
|
58536
|
+
DeployBaselineSchema = exports_external.object({
|
|
58537
|
+
lastAppliedMigrationTag: exports_external.string().min(1).optional(),
|
|
58538
|
+
journal: exports_external.array(exports_external.object({
|
|
58539
|
+
tag: exports_external.string().min(1),
|
|
58540
|
+
checksum: exports_external.string().min(1)
|
|
58541
|
+
})).optional(),
|
|
58542
|
+
evidence: BaselineEvidenceSchema,
|
|
58543
|
+
schemaSnapshot: exports_external.unknown().optional(),
|
|
58544
|
+
schemaHash: exports_external.string().min(1).optional(),
|
|
58545
|
+
integrationsHash: exports_external.string().min(1).optional(),
|
|
58546
|
+
buildHash: exports_external.string().min(1).optional()
|
|
58547
|
+
}).refine((data) => !data.lastAppliedMigrationTag === !data.journal, {
|
|
58548
|
+
message: "lastAppliedMigrationTag and journal must be provided together",
|
|
58549
|
+
path: ["journal"]
|
|
58550
|
+
}).refine((data) => data.schemaSnapshot === undefined === !data.schemaHash, {
|
|
58551
|
+
message: "schemaSnapshot and schemaHash must be provided together",
|
|
58552
|
+
path: ["schemaHash"]
|
|
58553
|
+
}).refine((data) => Boolean(data.lastAppliedMigrationTag || data.schemaHash || data.integrationsHash || data.buildHash), {
|
|
58554
|
+
message: "A baseline must claim something — migration history, a schema snapshot, or artifact hashes",
|
|
58555
|
+
path: ["lastAppliedMigrationTag"]
|
|
58556
|
+
});
|
|
58557
|
+
DeploymentStateBaselineSchema = exports_external.object({
|
|
58558
|
+
lastAppliedMigrationTag: exports_external.string().min(1).optional(),
|
|
58559
|
+
journal: exports_external.array(exports_external.object({
|
|
58560
|
+
tag: exports_external.string().min(1),
|
|
58561
|
+
checksum: exports_external.string().min(1)
|
|
58562
|
+
})).optional(),
|
|
58563
|
+
schemaSnapshot: exports_external.unknown().optional(),
|
|
58564
|
+
schemaHash: exports_external.string().min(1).optional(),
|
|
58565
|
+
evidence: BaselineEvidenceSchema,
|
|
58566
|
+
allowUnverified: exports_external.boolean().optional()
|
|
58567
|
+
}).refine((data) => !data.lastAppliedMigrationTag === !data.journal, {
|
|
58568
|
+
message: "lastAppliedMigrationTag and journal must be provided together",
|
|
58569
|
+
path: ["journal"]
|
|
58570
|
+
}).refine((data) => data.schemaSnapshot === undefined === !data.schemaHash, {
|
|
58571
|
+
message: "schemaSnapshot and schemaHash must be provided together",
|
|
58572
|
+
path: ["schemaHash"]
|
|
58573
|
+
}).refine((data) => Boolean(data.lastAppliedMigrationTag) || Boolean(data.schemaHash), {
|
|
58574
|
+
message: "A baseline needs a migration claim, a schema snapshot, or both",
|
|
58575
|
+
path: ["lastAppliedMigrationTag"]
|
|
58576
|
+
});
|
|
58577
|
+
MigrationRealignSchema = exports_external.object({
|
|
58578
|
+
checksum: exports_external.string().min(1)
|
|
58579
|
+
});
|
|
58580
|
+
MigrationResolveSchema = exports_external.object({
|
|
58581
|
+
resolution: exports_external.enum(["applied", "rolled-back"]),
|
|
58582
|
+
checksum: exports_external.string().min(1).optional()
|
|
58583
|
+
}).refine((data) => data.resolution !== "applied" || Boolean(data.checksum), {
|
|
58584
|
+
message: "Resolving a migration as 'applied' requires its checksum",
|
|
58585
|
+
path: ["checksum"]
|
|
58586
|
+
});
|
|
58587
|
+
DatabaseRestoreSchema = exports_external.object({
|
|
58588
|
+
restorePointId: exports_external.string().uuid()
|
|
58589
|
+
});
|
|
58590
|
+
DeployBlockedReportSchema = exports_external.object({
|
|
58591
|
+
code: exports_external.string().regex(/^blocked:[a-z-]+$/).max(64),
|
|
58592
|
+
reason: exports_external.string().min(1).max(2000)
|
|
58593
|
+
});
|
|
56204
58594
|
DeployRequestSchema = exports_external.object({
|
|
56205
58595
|
target: exports_external.enum(deploymentTargetEnum.enumValues).optional().default("game"),
|
|
58596
|
+
deployId: exports_external.string().min(1).optional(),
|
|
56206
58597
|
uploadToken: exports_external.string().optional(),
|
|
56207
58598
|
code: exports_external.string().optional(),
|
|
56208
58599
|
codeUploadToken: exports_external.string().optional(),
|
|
@@ -56229,6 +58620,11 @@ var init_schemas2 = __esm(() => {
|
|
|
56229
58620
|
sql: exports_external.string(),
|
|
56230
58621
|
hash: exports_external.string()
|
|
56231
58622
|
}).optional(),
|
|
58623
|
+
database: DeployDatabaseSchema.optional(),
|
|
58624
|
+
baseline: DeployBaselineSchema.optional(),
|
|
58625
|
+
buildHash: exports_external.string().min(1).optional(),
|
|
58626
|
+
integrationsHash: exports_external.string().min(1).optional(),
|
|
58627
|
+
pruneSecrets: exports_external.array(exports_external.string().min(1)).optional(),
|
|
56232
58628
|
metadata: exports_external.object({
|
|
56233
58629
|
displayName: exports_external.string().optional(),
|
|
56234
58630
|
description: exports_external.string().optional(),
|
|
@@ -56238,9 +58634,24 @@ var init_schemas2 = __esm(() => {
|
|
|
56238
58634
|
}).refine((data) => !(data.code && data.codeUploadToken), {
|
|
56239
58635
|
message: "Specify either code or codeUploadToken, not both",
|
|
56240
58636
|
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",
|
|
58637
|
+
}).refine((data) => !(data.target === "dashboard" && (data.schema || data.bindings || data.database)), {
|
|
58638
|
+
message: "Dashboard deployments cannot include schema, database, or bindings — they attach to the game deployment’s existing resources",
|
|
56243
58639
|
path: ["target"]
|
|
58640
|
+
}).refine((data) => !(data.target === "dashboard" && (data.baseline || data.pruneSecrets)), {
|
|
58641
|
+
message: "Dashboard deployments cannot adopt a baseline or prune secrets",
|
|
58642
|
+
path: ["target"]
|
|
58643
|
+
}).refine((data) => !(data.database && !data.deployId), {
|
|
58644
|
+
message: "deployId is required when a database payload is present",
|
|
58645
|
+
path: ["deployId"]
|
|
58646
|
+
}).refine((data) => !(data.database && data.schema), {
|
|
58647
|
+
message: "Send either the database payload or the legacy schema field, not both",
|
|
58648
|
+
path: ["database"]
|
|
58649
|
+
}).refine((data) => !(data.baseline && data.schema), {
|
|
58650
|
+
message: "The legacy schema field cannot ride a baseline-adopting deploy",
|
|
58651
|
+
path: ["baseline"]
|
|
58652
|
+
}).refine((data) => !(data.database && !requestsBinding(data.bindings?.database)), {
|
|
58653
|
+
message: "The database payload requires a database binding",
|
|
58654
|
+
path: ["database"]
|
|
56244
58655
|
});
|
|
56245
58656
|
});
|
|
56246
58657
|
var LeaderboardQuerySchema;
|
|
@@ -101209,7 +103620,7 @@ var init_pure = __esm(() => {
|
|
|
101209
103620
|
init_log();
|
|
101210
103621
|
init_spinner();
|
|
101211
103622
|
});
|
|
101212
|
-
var
|
|
103623
|
+
var init_src5 = __esm(() => {
|
|
101213
103624
|
init_pure();
|
|
101214
103625
|
});
|
|
101215
103626
|
function createOneRosterUrls(baseUrl) {
|
|
@@ -103185,7 +105596,7 @@ var init_dist5 = __esm(async () => {
|
|
|
103185
105596
|
init_spans();
|
|
103186
105597
|
init_src();
|
|
103187
105598
|
init_spans();
|
|
103188
|
-
|
|
105599
|
+
init_src5();
|
|
103189
105600
|
init_src();
|
|
103190
105601
|
init_spans();
|
|
103191
105602
|
init_spans();
|
|
@@ -103760,7 +106171,7 @@ function selectTimebackMetricDiscrepancyQueueItems(candidates, options) {
|
|
|
103760
106171
|
}
|
|
103761
106172
|
var DATE_INPUT_RE;
|
|
103762
106173
|
var init_timeback_discrepancy_queue_util = __esm(() => {
|
|
103763
|
-
|
|
106174
|
+
init_src5();
|
|
103764
106175
|
init_timeback_util();
|
|
103765
106176
|
DATE_INPUT_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
103766
106177
|
});
|
|
@@ -106273,7 +108684,7 @@ var init_timeback_admin_service = __esm(async () => {
|
|
|
106273
108684
|
init_constants3();
|
|
106274
108685
|
init_types2();
|
|
106275
108686
|
init_utils6();
|
|
106276
|
-
|
|
108687
|
+
init_src5();
|
|
106277
108688
|
init_timeback3();
|
|
106278
108689
|
init_errors();
|
|
106279
108690
|
init_timeback_admin_metrics_util();
|
|
@@ -108707,8 +111118,15 @@ function createPlatformServices(deps) {
|
|
|
108707
111118
|
validateDeveloperAccess
|
|
108708
111119
|
});
|
|
108709
111120
|
const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
108710
|
-
const secrets = new SecretsService({ config: config4, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
111121
|
+
const secrets = new SecretsService({ db: db2, config: config4, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
108711
111122
|
const domain3 = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
|
|
111123
|
+
const deploymentState = new DeploymentStateService({
|
|
111124
|
+
db: db2,
|
|
111125
|
+
config: config4,
|
|
111126
|
+
cloudflare: cloudflare2,
|
|
111127
|
+
alerts,
|
|
111128
|
+
validateDeveloperAccessBySlug
|
|
111129
|
+
});
|
|
108712
111130
|
const database = new DatabaseService({
|
|
108713
111131
|
db: db2,
|
|
108714
111132
|
config: config4,
|
|
@@ -108747,6 +111165,7 @@ function createPlatformServices(deps) {
|
|
|
108747
111165
|
kv,
|
|
108748
111166
|
secrets,
|
|
108749
111167
|
domain: domain3,
|
|
111168
|
+
deploymentState,
|
|
108750
111169
|
database,
|
|
108751
111170
|
seed,
|
|
108752
111171
|
timeback: timeback2,
|
|
@@ -108757,6 +111176,7 @@ function createPlatformServices(deps) {
|
|
|
108757
111176
|
var init_platform2 = __esm(async () => {
|
|
108758
111177
|
init_bucket_service();
|
|
108759
111178
|
init_database_service();
|
|
111179
|
+
init_deployment_state_service();
|
|
108760
111180
|
init_domain_service();
|
|
108761
111181
|
init_kv_service();
|
|
108762
111182
|
init_secrets_service();
|
|
@@ -109474,7 +111894,7 @@ function createServices(ctx) {
|
|
|
109474
111894
|
};
|
|
109475
111895
|
}
|
|
109476
111896
|
var init_factory = __esm(async () => {
|
|
109477
|
-
|
|
111897
|
+
init_game3();
|
|
109478
111898
|
init_infra2();
|
|
109479
111899
|
init_player();
|
|
109480
111900
|
init_standalone();
|
|
@@ -109767,6 +112187,7 @@ function buildConfig(options) {
|
|
|
109767
112187
|
gameDomain: "localhost",
|
|
109768
112188
|
uploadBucket: "sandbox-uploads",
|
|
109769
112189
|
ltiTestMode: true,
|
|
112190
|
+
secretsManifestPepper: "sandbox-secrets-manifest-pepper",
|
|
109770
112191
|
...options.config
|
|
109771
112192
|
});
|
|
109772
112193
|
}
|
|
@@ -127811,7 +130232,7 @@ var _a27;
|
|
|
127811
130232
|
var _b12;
|
|
127812
130233
|
var _c2;
|
|
127813
130234
|
var View3;
|
|
127814
|
-
var
|
|
130235
|
+
var init_sql4;
|
|
127815
130236
|
var _a28;
|
|
127816
130237
|
var ColumnAliasProxyHandler2;
|
|
127817
130238
|
var _a29;
|
|
@@ -128304,7 +130725,7 @@ var PgSequence;
|
|
|
128304
130725
|
var init_sequence2;
|
|
128305
130726
|
var _a164;
|
|
128306
130727
|
var PgSchema5;
|
|
128307
|
-
var
|
|
130728
|
+
var init_schema4;
|
|
128308
130729
|
var _a165;
|
|
128309
130730
|
var Cache;
|
|
128310
130731
|
var _a166;
|
|
@@ -149783,7 +152204,7 @@ globstar while`, file3, fr, pattern, pr2, swallowee);
|
|
|
149783
152204
|
});
|
|
149784
152205
|
}
|
|
149785
152206
|
});
|
|
149786
|
-
|
|
152207
|
+
init_sql4 = __esm2({
|
|
149787
152208
|
"../drizzle-orm/dist/sql/sql.js"() {
|
|
149788
152209
|
init_entity2();
|
|
149789
152210
|
init_enum2();
|
|
@@ -150136,7 +152557,7 @@ globstar while`, file3, fr, pattern, pr2, swallowee);
|
|
|
150136
152557
|
"../drizzle-orm/dist/alias.js"() {
|
|
150137
152558
|
init_column2();
|
|
150138
152559
|
init_entity2();
|
|
150139
|
-
|
|
152560
|
+
init_sql4();
|
|
150140
152561
|
init_table8();
|
|
150141
152562
|
init_view_common3();
|
|
150142
152563
|
_a28 = entityKind2;
|
|
@@ -150310,7 +152731,7 @@ params: ${params}`);
|
|
|
150310
152731
|
"../drizzle-orm/dist/utils.js"() {
|
|
150311
152732
|
init_column2();
|
|
150312
152733
|
init_entity2();
|
|
150313
|
-
|
|
152734
|
+
init_sql4();
|
|
150314
152735
|
init_subquery2();
|
|
150315
152736
|
init_table8();
|
|
150316
152737
|
init_view_common3();
|
|
@@ -150569,7 +152990,7 @@ params: ${params}`);
|
|
|
150569
152990
|
init_date_common2 = __esm2({
|
|
150570
152991
|
"../drizzle-orm/dist/pg-core/columns/date.common.js"() {
|
|
150571
152992
|
init_entity2();
|
|
150572
|
-
|
|
152993
|
+
init_sql4();
|
|
150573
152994
|
init_common22();
|
|
150574
152995
|
PgDateColumnBaseBuilder2 = class extends (_b33 = PgColumnBuilder2, _a54 = entityKind2, _b33) {
|
|
150575
152996
|
defaultNow() {
|
|
@@ -151354,7 +153775,7 @@ params: ${params}`);
|
|
|
151354
153775
|
init_uuid3 = __esm2({
|
|
151355
153776
|
"../drizzle-orm/dist/pg-core/columns/uuid.js"() {
|
|
151356
153777
|
init_entity2();
|
|
151357
|
-
|
|
153778
|
+
init_sql4();
|
|
151358
153779
|
init_common22();
|
|
151359
153780
|
PgUUIDBuilder2 = class extends (_b88 = PgColumnBuilder2, _a109 = entityKind2, _b88) {
|
|
151360
153781
|
constructor(name22) {
|
|
@@ -151625,7 +154046,7 @@ params: ${params}`);
|
|
|
151625
154046
|
init_column2();
|
|
151626
154047
|
init_entity2();
|
|
151627
154048
|
init_table8();
|
|
151628
|
-
|
|
154049
|
+
init_sql4();
|
|
151629
154050
|
eq2 = (left, right) => {
|
|
151630
154051
|
return sql4`${left} = ${bindIfParam2(right, left)}`;
|
|
151631
154052
|
};
|
|
@@ -151648,7 +154069,7 @@ params: ${params}`);
|
|
|
151648
154069
|
});
|
|
151649
154070
|
init_select3 = __esm2({
|
|
151650
154071
|
"../drizzle-orm/dist/sql/expressions/select.js"() {
|
|
151651
|
-
|
|
154072
|
+
init_sql4();
|
|
151652
154073
|
}
|
|
151653
154074
|
});
|
|
151654
154075
|
init_expressions2 = __esm2({
|
|
@@ -151664,7 +154085,7 @@ params: ${params}`);
|
|
|
151664
154085
|
init_entity2();
|
|
151665
154086
|
init_primary_keys2();
|
|
151666
154087
|
init_expressions2();
|
|
151667
|
-
|
|
154088
|
+
init_sql4();
|
|
151668
154089
|
_a124 = entityKind2;
|
|
151669
154090
|
Relation2 = class {
|
|
151670
154091
|
constructor(sourceTable, referencedTable, relationName) {
|
|
@@ -151718,12 +154139,12 @@ params: ${params}`);
|
|
|
151718
154139
|
"../drizzle-orm/dist/sql/functions/aggregate.js"() {
|
|
151719
154140
|
init_column2();
|
|
151720
154141
|
init_entity2();
|
|
151721
|
-
|
|
154142
|
+
init_sql4();
|
|
151722
154143
|
}
|
|
151723
154144
|
});
|
|
151724
154145
|
init_vector22 = __esm2({
|
|
151725
154146
|
"../drizzle-orm/dist/sql/functions/vector.js"() {
|
|
151726
|
-
|
|
154147
|
+
init_sql4();
|
|
151727
154148
|
}
|
|
151728
154149
|
});
|
|
151729
154150
|
init_functions2 = __esm2({
|
|
@@ -151736,7 +154157,7 @@ params: ${params}`);
|
|
|
151736
154157
|
"../drizzle-orm/dist/sql/index.js"() {
|
|
151737
154158
|
init_expressions2();
|
|
151738
154159
|
init_functions2();
|
|
151739
|
-
|
|
154160
|
+
init_sql4();
|
|
151740
154161
|
}
|
|
151741
154162
|
});
|
|
151742
154163
|
dist_exports = {};
|
|
@@ -151953,7 +154374,7 @@ params: ${params}`);
|
|
|
151953
154374
|
init_alias3();
|
|
151954
154375
|
init_column2();
|
|
151955
154376
|
init_entity2();
|
|
151956
|
-
|
|
154377
|
+
init_sql4();
|
|
151957
154378
|
init_subquery2();
|
|
151958
154379
|
init_view_common3();
|
|
151959
154380
|
_a130 = entityKind2;
|
|
@@ -152012,7 +154433,7 @@ params: ${params}`);
|
|
|
152012
154433
|
});
|
|
152013
154434
|
init_indexes2 = __esm2({
|
|
152014
154435
|
"../drizzle-orm/dist/pg-core/indexes.js"() {
|
|
152015
|
-
|
|
154436
|
+
init_sql4();
|
|
152016
154437
|
init_entity2();
|
|
152017
154438
|
init_columns2();
|
|
152018
154439
|
_a131 = entityKind2;
|
|
@@ -152175,7 +154596,7 @@ params: ${params}`);
|
|
|
152175
154596
|
init_view_base2 = __esm2({
|
|
152176
154597
|
"../drizzle-orm/dist/pg-core/view-base.js"() {
|
|
152177
154598
|
init_entity2();
|
|
152178
|
-
|
|
154599
|
+
init_sql4();
|
|
152179
154600
|
PgViewBase2 = class extends (_b103 = View3, _a136 = entityKind2, _b103) {
|
|
152180
154601
|
};
|
|
152181
154602
|
__publicField(PgViewBase2, _a136, "PgViewBase");
|
|
@@ -152192,7 +154613,7 @@ params: ${params}`);
|
|
|
152192
154613
|
init_table22();
|
|
152193
154614
|
init_relations2();
|
|
152194
154615
|
init_sql22();
|
|
152195
|
-
|
|
154616
|
+
init_sql4();
|
|
152196
154617
|
init_subquery2();
|
|
152197
154618
|
init_table8();
|
|
152198
154619
|
init_utils22();
|
|
@@ -152776,7 +155197,7 @@ params: ${params}`);
|
|
|
152776
155197
|
init_query_builder3();
|
|
152777
155198
|
init_query_promise2();
|
|
152778
155199
|
init_selection_proxy2();
|
|
152779
|
-
|
|
155200
|
+
init_sql4();
|
|
152780
155201
|
init_subquery2();
|
|
152781
155202
|
init_table8();
|
|
152782
155203
|
init_tracing2();
|
|
@@ -153397,7 +155818,7 @@ params: ${params}`);
|
|
|
153397
155818
|
"../drizzle-orm/dist/pg-core/utils.js"() {
|
|
153398
155819
|
init_entity2();
|
|
153399
155820
|
init_table22();
|
|
153400
|
-
|
|
155821
|
+
init_sql4();
|
|
153401
155822
|
init_subquery2();
|
|
153402
155823
|
init_table8();
|
|
153403
155824
|
init_view_common3();
|
|
@@ -153485,7 +155906,7 @@ params: ${params}`);
|
|
|
153485
155906
|
init_entity2();
|
|
153486
155907
|
init_query_promise2();
|
|
153487
155908
|
init_selection_proxy2();
|
|
153488
|
-
|
|
155909
|
+
init_sql4();
|
|
153489
155910
|
init_table8();
|
|
153490
155911
|
init_tracing2();
|
|
153491
155912
|
init_utils22();
|
|
@@ -153679,7 +156100,7 @@ params: ${params}`);
|
|
|
153679
156100
|
init_table22();
|
|
153680
156101
|
init_query_promise2();
|
|
153681
156102
|
init_selection_proxy2();
|
|
153682
|
-
|
|
156103
|
+
init_sql4();
|
|
153683
156104
|
init_subquery2();
|
|
153684
156105
|
init_table8();
|
|
153685
156106
|
init_utils22();
|
|
@@ -153853,7 +156274,7 @@ params: ${params}`);
|
|
|
153853
156274
|
init_count2 = __esm2({
|
|
153854
156275
|
"../drizzle-orm/dist/pg-core/query-builders/count.js"() {
|
|
153855
156276
|
init_entity2();
|
|
153856
|
-
|
|
156277
|
+
init_sql4();
|
|
153857
156278
|
_PgCountBuilder = class _PgCountBuilder2 extends (_c6 = SQL2, _b116 = entityKind2, _a157 = Symbol.toStringTag, _c6) {
|
|
153858
156279
|
constructor(params) {
|
|
153859
156280
|
super(_PgCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -154021,7 +156442,7 @@ params: ${params}`);
|
|
|
154021
156442
|
init_entity2();
|
|
154022
156443
|
init_query_builders2();
|
|
154023
156444
|
init_selection_proxy2();
|
|
154024
|
-
|
|
156445
|
+
init_sql4();
|
|
154025
156446
|
init_subquery2();
|
|
154026
156447
|
init_count2();
|
|
154027
156448
|
init_query2();
|
|
@@ -154193,10 +156614,10 @@ params: ${params}`);
|
|
|
154193
156614
|
__publicField(PgSequence, _a163, "PgSequence");
|
|
154194
156615
|
}
|
|
154195
156616
|
});
|
|
154196
|
-
|
|
156617
|
+
init_schema4 = __esm2({
|
|
154197
156618
|
"../drizzle-orm/dist/pg-core/schema.js"() {
|
|
154198
156619
|
init_entity2();
|
|
154199
|
-
|
|
156620
|
+
init_sql4();
|
|
154200
156621
|
init_enum2();
|
|
154201
156622
|
init_sequence2();
|
|
154202
156623
|
init_table22();
|
|
@@ -154412,7 +156833,7 @@ params: ${params}`);
|
|
|
154412
156833
|
init_primary_keys2();
|
|
154413
156834
|
init_query_builders2();
|
|
154414
156835
|
init_roles2();
|
|
154415
|
-
|
|
156836
|
+
init_schema4();
|
|
154416
156837
|
init_sequence2();
|
|
154417
156838
|
init_session3();
|
|
154418
156839
|
init_subquery22();
|
|
@@ -156220,7 +158641,7 @@ ORDER BY
|
|
|
156220
158641
|
init_integer22 = __esm2({
|
|
156221
158642
|
"../drizzle-orm/dist/sqlite-core/columns/integer.js"() {
|
|
156222
158643
|
init_entity2();
|
|
156223
|
-
|
|
158644
|
+
init_sql4();
|
|
156224
158645
|
init_utils22();
|
|
156225
158646
|
init_common3();
|
|
156226
158647
|
SQLiteBaseIntegerBuilder = class extends (_b131 = SQLiteColumnBuilder, _a187 = entityKind2, _b131) {
|
|
@@ -156583,7 +159004,7 @@ ORDER BY
|
|
|
156583
159004
|
init_utils72 = __esm2({
|
|
156584
159005
|
"../drizzle-orm/dist/sqlite-core/utils.js"() {
|
|
156585
159006
|
init_entity2();
|
|
156586
|
-
|
|
159007
|
+
init_sql4();
|
|
156587
159008
|
init_subquery2();
|
|
156588
159009
|
init_table8();
|
|
156589
159010
|
init_view_common3();
|
|
@@ -156677,7 +159098,7 @@ ORDER BY
|
|
|
156677
159098
|
init_view_base22 = __esm2({
|
|
156678
159099
|
"../drizzle-orm/dist/sqlite-core/view-base.js"() {
|
|
156679
159100
|
init_entity2();
|
|
156680
|
-
|
|
159101
|
+
init_sql4();
|
|
156681
159102
|
SQLiteViewBase = class extends (_b153 = View3, _a214 = entityKind2, _b153) {
|
|
156682
159103
|
};
|
|
156683
159104
|
__publicField(SQLiteViewBase, _a214, "SQLiteViewBase");
|
|
@@ -156692,7 +159113,7 @@ ORDER BY
|
|
|
156692
159113
|
init_errors22();
|
|
156693
159114
|
init_relations2();
|
|
156694
159115
|
init_sql22();
|
|
156695
|
-
|
|
159116
|
+
init_sql4();
|
|
156696
159117
|
init_columns22();
|
|
156697
159118
|
init_table32();
|
|
156698
159119
|
init_subquery2();
|
|
@@ -157272,7 +159693,7 @@ ORDER BY
|
|
|
157272
159693
|
init_query_builder3();
|
|
157273
159694
|
init_query_promise2();
|
|
157274
159695
|
init_selection_proxy2();
|
|
157275
|
-
|
|
159696
|
+
init_sql4();
|
|
157276
159697
|
init_subquery2();
|
|
157277
159698
|
init_table8();
|
|
157278
159699
|
init_utils22();
|
|
@@ -157635,7 +160056,7 @@ ORDER BY
|
|
|
157635
160056
|
"../drizzle-orm/dist/sqlite-core/query-builders/insert.js"() {
|
|
157636
160057
|
init_entity2();
|
|
157637
160058
|
init_query_promise2();
|
|
157638
|
-
|
|
160059
|
+
init_sql4();
|
|
157639
160060
|
init_table32();
|
|
157640
160061
|
init_table8();
|
|
157641
160062
|
init_utils22();
|
|
@@ -157882,7 +160303,7 @@ ORDER BY
|
|
|
157882
160303
|
init_count22 = __esm2({
|
|
157883
160304
|
"../drizzle-orm/dist/sqlite-core/query-builders/count.js"() {
|
|
157884
160305
|
init_entity2();
|
|
157885
|
-
|
|
160306
|
+
init_sql4();
|
|
157886
160307
|
_SQLiteCountBuilder = class _SQLiteCountBuilder2 extends (_c8 = SQL2, _b160 = entityKind2, _a226 = Symbol.toStringTag, _c8) {
|
|
157887
160308
|
constructor(params) {
|
|
157888
160309
|
super(_SQLiteCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -158051,7 +160472,7 @@ ORDER BY
|
|
|
158051
160472
|
"../drizzle-orm/dist/sqlite-core/db.js"() {
|
|
158052
160473
|
init_entity2();
|
|
158053
160474
|
init_selection_proxy2();
|
|
158054
|
-
|
|
160475
|
+
init_sql4();
|
|
158055
160476
|
init_query_builders22();
|
|
158056
160477
|
init_subquery2();
|
|
158057
160478
|
init_count22();
|
|
@@ -160022,7 +162443,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160022
162443
|
init_date_common22 = __esm2({
|
|
160023
162444
|
"../drizzle-orm/dist/mysql-core/columns/date.common.js"() {
|
|
160024
162445
|
init_entity2();
|
|
160025
|
-
|
|
162446
|
+
init_sql4();
|
|
160026
162447
|
init_common4();
|
|
160027
162448
|
MySqlDateColumnBaseBuilder = class extends (_b223 = MySqlColumnBuilder, _a301 = entityKind2, _b223) {
|
|
160028
162449
|
defaultNow() {
|
|
@@ -160248,7 +162669,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160248
162669
|
init_count3 = __esm2({
|
|
160249
162670
|
"../drizzle-orm/dist/mysql-core/query-builders/count.js"() {
|
|
160250
162671
|
init_entity2();
|
|
160251
|
-
|
|
162672
|
+
init_sql4();
|
|
160252
162673
|
_MySqlCountBuilder = class _MySqlCountBuilder2 extends (_c9 = SQL2, _b237 = entityKind2, _a315 = Symbol.toStringTag, _c9) {
|
|
160253
162674
|
constructor(params) {
|
|
160254
162675
|
super(_MySqlCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -160510,7 +162931,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160510
162931
|
init_view_base3 = __esm2({
|
|
160511
162932
|
"../drizzle-orm/dist/mysql-core/view-base.js"() {
|
|
160512
162933
|
init_entity2();
|
|
160513
|
-
|
|
162934
|
+
init_sql4();
|
|
160514
162935
|
MySqlViewBase = class extends (_b240 = View3, _a323 = entityKind2, _b240) {
|
|
160515
162936
|
};
|
|
160516
162937
|
__publicField(MySqlViewBase, _a323, "MySqlViewBase");
|
|
@@ -160525,7 +162946,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
160525
162946
|
init_errors22();
|
|
160526
162947
|
init_relations2();
|
|
160527
162948
|
init_expressions2();
|
|
160528
|
-
|
|
162949
|
+
init_sql4();
|
|
160529
162950
|
init_subquery2();
|
|
160530
162951
|
init_table8();
|
|
160531
162952
|
init_utils22();
|
|
@@ -161291,7 +163712,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
161291
163712
|
init_query_builder3();
|
|
161292
163713
|
init_query_promise2();
|
|
161293
163714
|
init_selection_proxy2();
|
|
161294
|
-
|
|
163715
|
+
init_sql4();
|
|
161295
163716
|
init_subquery2();
|
|
161296
163717
|
init_table8();
|
|
161297
163718
|
init_utils22();
|
|
@@ -161692,7 +164113,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
161692
164113
|
"../drizzle-orm/dist/mysql-core/query-builders/insert.js"() {
|
|
161693
164114
|
init_entity2();
|
|
161694
164115
|
init_query_promise2();
|
|
161695
|
-
|
|
164116
|
+
init_sql4();
|
|
161696
164117
|
init_table8();
|
|
161697
164118
|
init_utils22();
|
|
161698
164119
|
init_utils8();
|
|
@@ -161972,7 +164393,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
161972
164393
|
"../drizzle-orm/dist/mysql-core/db.js"() {
|
|
161973
164394
|
init_entity2();
|
|
161974
164395
|
init_selection_proxy2();
|
|
161975
|
-
|
|
164396
|
+
init_sql4();
|
|
161976
164397
|
init_subquery2();
|
|
161977
164398
|
init_count3();
|
|
161978
164399
|
init_query_builders3();
|
|
@@ -162201,7 +164622,7 @@ ${withStyle.errorWarning(`We've found duplicated view name across ${source_defau
|
|
|
162201
164622
|
init_cache();
|
|
162202
164623
|
init_entity2();
|
|
162203
164624
|
init_errors22();
|
|
162204
|
-
|
|
164625
|
+
init_sql4();
|
|
162205
164626
|
init_db3();
|
|
162206
164627
|
_a341 = entityKind2;
|
|
162207
164628
|
MySqlPreparedQuery = class {
|
|
@@ -164224,7 +166645,7 @@ AND
|
|
|
164224
166645
|
init_date_common3 = __esm2({
|
|
164225
166646
|
"../drizzle-orm/dist/singlestore-core/columns/date.common.js"() {
|
|
164226
166647
|
init_entity2();
|
|
164227
|
-
|
|
166648
|
+
init_sql4();
|
|
164228
166649
|
init_common5();
|
|
164229
166650
|
SingleStoreDateColumnBaseBuilder = class extends (_b302 = SingleStoreColumnBuilder, _a399 = entityKind2, _b302) {
|
|
164230
166651
|
defaultNow() {
|
|
@@ -164249,7 +166670,7 @@ AND
|
|
|
164249
166670
|
init_timestamp3 = __esm2({
|
|
164250
166671
|
"../drizzle-orm/dist/singlestore-core/columns/timestamp.js"() {
|
|
164251
166672
|
init_entity2();
|
|
164252
|
-
|
|
166673
|
+
init_sql4();
|
|
164253
166674
|
init_utils22();
|
|
164254
166675
|
init_date_common3();
|
|
164255
166676
|
SingleStoreTimestampBuilder = class extends (_b304 = SingleStoreDateColumnBaseBuilder, _a401 = entityKind2, _b304) {
|
|
@@ -164484,7 +166905,7 @@ AND
|
|
|
164484
166905
|
init_count4 = __esm2({
|
|
164485
166906
|
"../drizzle-orm/dist/singlestore-core/query-builders/count.js"() {
|
|
164486
166907
|
init_entity2();
|
|
164487
|
-
|
|
166908
|
+
init_sql4();
|
|
164488
166909
|
_SingleStoreCountBuilder = class _SingleStoreCountBuilder2 extends (_c12 = SQL2, _b318 = entityKind2, _a415 = Symbol.toStringTag, _c12) {
|
|
164489
166910
|
constructor(params) {
|
|
164490
166911
|
super(_SingleStoreCountBuilder2.buildEmbeddedCount(params.source, params.filters).queryChunks);
|
|
@@ -164654,7 +167075,7 @@ AND
|
|
|
164654
167075
|
init_utils10 = __esm2({
|
|
164655
167076
|
"../drizzle-orm/dist/singlestore-core/utils.js"() {
|
|
164656
167077
|
init_entity2();
|
|
164657
|
-
|
|
167078
|
+
init_sql4();
|
|
164658
167079
|
init_subquery2();
|
|
164659
167080
|
init_table8();
|
|
164660
167081
|
init_indexes4();
|
|
@@ -164732,7 +167153,7 @@ AND
|
|
|
164732
167153
|
"../drizzle-orm/dist/singlestore-core/query-builders/insert.js"() {
|
|
164733
167154
|
init_entity2();
|
|
164734
167155
|
init_query_promise2();
|
|
164735
|
-
|
|
167156
|
+
init_sql4();
|
|
164736
167157
|
init_table8();
|
|
164737
167158
|
init_utils22();
|
|
164738
167159
|
init_utils10();
|
|
@@ -164829,7 +167250,7 @@ AND
|
|
|
164829
167250
|
init_errors22();
|
|
164830
167251
|
init_relations2();
|
|
164831
167252
|
init_expressions2();
|
|
164832
|
-
|
|
167253
|
+
init_sql4();
|
|
164833
167254
|
init_subquery2();
|
|
164834
167255
|
init_table8();
|
|
164835
167256
|
init_utils22();
|
|
@@ -165360,7 +167781,7 @@ AND
|
|
|
165360
167781
|
init_query_builder3();
|
|
165361
167782
|
init_query_promise2();
|
|
165362
167783
|
init_selection_proxy2();
|
|
165363
|
-
|
|
167784
|
+
init_sql4();
|
|
165364
167785
|
init_subquery2();
|
|
165365
167786
|
init_table8();
|
|
165366
167787
|
init_utils22();
|
|
@@ -165818,7 +168239,7 @@ AND
|
|
|
165818
168239
|
"../drizzle-orm/dist/singlestore-core/db.js"() {
|
|
165819
168240
|
init_entity2();
|
|
165820
168241
|
init_selection_proxy2();
|
|
165821
|
-
|
|
168242
|
+
init_sql4();
|
|
165822
168243
|
init_subquery2();
|
|
165823
168244
|
init_count4();
|
|
165824
168245
|
init_query_builders4();
|
|
@@ -165932,7 +168353,7 @@ AND
|
|
|
165932
168353
|
init_cache();
|
|
165933
168354
|
init_entity2();
|
|
165934
168355
|
init_errors22();
|
|
165935
|
-
|
|
168356
|
+
init_sql4();
|
|
165936
168357
|
init_db4();
|
|
165937
168358
|
_a434 = entityKind2;
|
|
165938
168359
|
SingleStorePreparedQuery = class {
|
|
@@ -168168,7 +170589,7 @@ function requireUserId(userId) {
|
|
|
168168
170589
|
return userId;
|
|
168169
170590
|
}
|
|
168170
170591
|
var init_params_util = __esm(() => {
|
|
168171
|
-
|
|
170592
|
+
init_src5();
|
|
168172
170593
|
init_errors();
|
|
168173
170594
|
});
|
|
168174
170595
|
function formatZodError(error89) {
|
|
@@ -168218,6 +170639,7 @@ var init_utils11 = __esm(() => {
|
|
|
168218
170639
|
init_lti_util();
|
|
168219
170640
|
init_lti_provisioning();
|
|
168220
170641
|
init_params_util();
|
|
170642
|
+
init_secrets_util();
|
|
168221
170643
|
init_timeback_util();
|
|
168222
170644
|
init_validation_util();
|
|
168223
170645
|
});
|
|
@@ -168414,7 +170836,7 @@ var init_database_controller = __esm(() => {
|
|
|
168414
170836
|
throw ApiError.unprocessableEntity("Validation failed", details);
|
|
168415
170837
|
}
|
|
168416
170838
|
}
|
|
168417
|
-
return ctx.services.database.reset(slug2, ctx.user, body2
|
|
170839
|
+
return ctx.services.database.reset(slug2, ctx.user, body2);
|
|
168418
170840
|
});
|
|
168419
170841
|
database = defineControllerNames("database", {
|
|
168420
170842
|
reset
|
|
@@ -168464,6 +170886,91 @@ var init_deploy_controller = __esm(() => {
|
|
|
168464
170886
|
getJob: requireDeveloper(getJob)
|
|
168465
170887
|
});
|
|
168466
170888
|
});
|
|
170889
|
+
var get;
|
|
170890
|
+
var baseline;
|
|
170891
|
+
var realignMigration;
|
|
170892
|
+
var resolveMigration;
|
|
170893
|
+
var history;
|
|
170894
|
+
var restorePoints;
|
|
170895
|
+
var restore;
|
|
170896
|
+
var reportBlocked;
|
|
170897
|
+
var deploymentState;
|
|
170898
|
+
var init_deployment_state_controller = __esm(() => {
|
|
170899
|
+
init_schemas_index();
|
|
170900
|
+
init_errors();
|
|
170901
|
+
init_utils11();
|
|
170902
|
+
get = requireDeveloper(async (ctx) => {
|
|
170903
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170904
|
+
const include = ctx.url.searchParams.getAll("include").flatMap((value) => value.split(",")).filter(Boolean);
|
|
170905
|
+
const unsupported = include.filter((value) => value !== "schemaSnapshot");
|
|
170906
|
+
if (unsupported.length > 0) {
|
|
170907
|
+
throw ApiError.badRequest(`Unsupported include value(s): ${unsupported.join(", ")}`);
|
|
170908
|
+
}
|
|
170909
|
+
return ctx.services.deploymentState.get(slug2, ctx.user, {
|
|
170910
|
+
includeSchemaSnapshot: include.includes("schemaSnapshot")
|
|
170911
|
+
});
|
|
170912
|
+
});
|
|
170913
|
+
baseline = requireDeveloper(async (ctx) => {
|
|
170914
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170915
|
+
const body2 = await parseRequestBody(ctx.request, DeploymentStateBaselineSchema);
|
|
170916
|
+
return ctx.services.deploymentState.baseline(slug2, body2, ctx.user);
|
|
170917
|
+
});
|
|
170918
|
+
realignMigration = requireDeveloper(async (ctx) => {
|
|
170919
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170920
|
+
const tag = ctx.params.tag;
|
|
170921
|
+
if (!tag) {
|
|
170922
|
+
throw ApiError.badRequest("Missing migration tag");
|
|
170923
|
+
}
|
|
170924
|
+
const body2 = await parseRequestBody(ctx.request, MigrationRealignSchema);
|
|
170925
|
+
return ctx.services.deploymentState.realignMigration(slug2, tag, body2.checksum, ctx.user);
|
|
170926
|
+
});
|
|
170927
|
+
resolveMigration = requireDeveloper(async (ctx) => {
|
|
170928
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170929
|
+
const tag = ctx.params.tag;
|
|
170930
|
+
if (!tag) {
|
|
170931
|
+
throw ApiError.badRequest("Missing migration tag");
|
|
170932
|
+
}
|
|
170933
|
+
const body2 = await parseRequestBody(ctx.request, MigrationResolveSchema);
|
|
170934
|
+
return ctx.services.deploymentState.resolveMigration(slug2, tag, body2, ctx.user);
|
|
170935
|
+
});
|
|
170936
|
+
history = requireDeveloper(async (ctx) => {
|
|
170937
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170938
|
+
const limitParam = ctx.url.searchParams.get("limit");
|
|
170939
|
+
let limit;
|
|
170940
|
+
if (limitParam !== null) {
|
|
170941
|
+
limit = Number(limitParam);
|
|
170942
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
170943
|
+
throw ApiError.badRequest("limit must be an integer between 1 and 100");
|
|
170944
|
+
}
|
|
170945
|
+
}
|
|
170946
|
+
return ctx.services.deploymentState.history(slug2, ctx.user, { limit });
|
|
170947
|
+
});
|
|
170948
|
+
restorePoints = requireDeveloper(async (ctx) => {
|
|
170949
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170950
|
+
return ctx.services.deploymentState.restorePoints(slug2, ctx.user);
|
|
170951
|
+
});
|
|
170952
|
+
restore = requireDeveloper(async (ctx) => {
|
|
170953
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170954
|
+
const body2 = await parseRequestBody(ctx.request, DatabaseRestoreSchema);
|
|
170955
|
+
return ctx.services.deploymentState.restoreToBookmark(slug2, body2, ctx.user);
|
|
170956
|
+
});
|
|
170957
|
+
reportBlocked = requireDeveloper(async (ctx) => {
|
|
170958
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
170959
|
+
const body2 = await parseRequestBody(ctx.request, DeployBlockedReportSchema);
|
|
170960
|
+
await ctx.services.deploymentState.reportDeployBlocked(slug2, ctx.user, body2);
|
|
170961
|
+
return { recorded: true };
|
|
170962
|
+
});
|
|
170963
|
+
deploymentState = defineControllerNames("deploymentState", {
|
|
170964
|
+
get,
|
|
170965
|
+
baseline,
|
|
170966
|
+
realignMigration,
|
|
170967
|
+
resolveMigration,
|
|
170968
|
+
history,
|
|
170969
|
+
restorePoints,
|
|
170970
|
+
restore,
|
|
170971
|
+
reportBlocked
|
|
170972
|
+
});
|
|
170973
|
+
});
|
|
168467
170974
|
var apply;
|
|
168468
170975
|
var getStatus;
|
|
168469
170976
|
var developer;
|
|
@@ -168953,6 +171460,7 @@ var init_lti_controller = __esm(() => {
|
|
|
168953
171460
|
var listKeys2;
|
|
168954
171461
|
var setSecrets;
|
|
168955
171462
|
var deleteSecret;
|
|
171463
|
+
var diff;
|
|
168956
171464
|
var secrets;
|
|
168957
171465
|
var init_secrets_controller = __esm(() => {
|
|
168958
171466
|
init_esm();
|
|
@@ -168999,10 +171507,19 @@ var init_secrets_controller = __esm(() => {
|
|
|
168999
171507
|
await ctx.services.secrets.deleteSecret(slug2, key, ctx.user);
|
|
169000
171508
|
return { success: true };
|
|
169001
171509
|
});
|
|
171510
|
+
diff = requireDeveloper(async (ctx) => {
|
|
171511
|
+
const slug2 = ctx.params.slug;
|
|
171512
|
+
if (!slug2) {
|
|
171513
|
+
throw ApiError.badRequest("Missing game slug");
|
|
171514
|
+
}
|
|
171515
|
+
const body2 = await parseRequestBody(ctx.request, SecretsDiffRequestSchema);
|
|
171516
|
+
return ctx.services.secrets.diff(slug2, body2.secrets, ctx.user);
|
|
171517
|
+
});
|
|
169002
171518
|
secrets = defineControllerNames("secrets", {
|
|
169003
171519
|
listKeys: listKeys2,
|
|
169004
171520
|
setSecrets,
|
|
169005
|
-
deleteSecret
|
|
171521
|
+
deleteSecret,
|
|
171522
|
+
diff
|
|
169006
171523
|
});
|
|
169007
171524
|
});
|
|
169008
171525
|
var seed2;
|
|
@@ -169126,7 +171643,7 @@ var timeback2;
|
|
|
169126
171643
|
var init_timeback_controller = __esm(() => {
|
|
169127
171644
|
init_esm();
|
|
169128
171645
|
init_schemas_index();
|
|
169129
|
-
|
|
171646
|
+
init_src5();
|
|
169130
171647
|
init_timeback3();
|
|
169131
171648
|
init_errors();
|
|
169132
171649
|
init_utils11();
|
|
@@ -169889,6 +172406,7 @@ var init_controllers = __esm(() => {
|
|
|
169889
172406
|
init_dashboard_controller();
|
|
169890
172407
|
init_database_controller();
|
|
169891
172408
|
init_deploy_controller();
|
|
172409
|
+
init_deployment_state_controller();
|
|
169892
172410
|
init_developer_controller();
|
|
169893
172411
|
init_domain_controller();
|
|
169894
172412
|
init_game_member_controller();
|
|
@@ -170409,6 +172927,21 @@ var init_deploy = __esm(async () => {
|
|
|
170409
172927
|
});
|
|
170410
172928
|
});
|
|
170411
172929
|
});
|
|
172930
|
+
var gameDeploymentStateRouter;
|
|
172931
|
+
var init_deployment_state = __esm(async () => {
|
|
172932
|
+
init_dist7();
|
|
172933
|
+
init_controllers();
|
|
172934
|
+
await init_api3();
|
|
172935
|
+
gameDeploymentStateRouter = new Hono2;
|
|
172936
|
+
gameDeploymentStateRouter.get("/:slug/deployment-state", handle2(deploymentState.get));
|
|
172937
|
+
gameDeploymentStateRouter.post("/:slug/deployment-state/baseline", handle2(deploymentState.baseline));
|
|
172938
|
+
gameDeploymentStateRouter.post("/:slug/deployment-state/migrations/:tag/realign", handle2(deploymentState.realignMigration));
|
|
172939
|
+
gameDeploymentStateRouter.post("/:slug/deployment-state/migrations/:tag/resolve", handle2(deploymentState.resolveMigration));
|
|
172940
|
+
gameDeploymentStateRouter.get("/:slug/deployments", handle2(deploymentState.history));
|
|
172941
|
+
gameDeploymentStateRouter.post("/:slug/deployments/blocked", handle2(deploymentState.reportBlocked));
|
|
172942
|
+
gameDeploymentStateRouter.get("/:slug/database/restore-points", handle2(deploymentState.restorePoints));
|
|
172943
|
+
gameDeploymentStateRouter.post("/:slug/database/restore", handle2(deploymentState.restore));
|
|
172944
|
+
});
|
|
170412
172945
|
var gameDomainsRouter;
|
|
170413
172946
|
var init_domains2 = __esm(async () => {
|
|
170414
172947
|
init_dist7();
|
|
@@ -170447,6 +172980,7 @@ var init_secrets = __esm(async () => {
|
|
|
170447
172980
|
gameSecretsRouter = new Hono2;
|
|
170448
172981
|
gameSecretsRouter.get("/:slug/secrets", handle2(secrets.listKeys));
|
|
170449
172982
|
gameSecretsRouter.post("/:slug/secrets", handle2(secrets.setSecrets));
|
|
172983
|
+
gameSecretsRouter.post("/:slug/secrets/diff", handle2(secrets.diff));
|
|
170450
172984
|
gameSecretsRouter.delete("/:slug/secrets/:key", handle2(secrets.deleteSecret));
|
|
170451
172985
|
});
|
|
170452
172986
|
var gameSeedRouter;
|
|
@@ -170641,6 +173175,7 @@ var init_games2 = __esm(async () => {
|
|
|
170641
173175
|
await __promiseAll([
|
|
170642
173176
|
init_crud(),
|
|
170643
173177
|
init_deploy(),
|
|
173178
|
+
init_deployment_state(),
|
|
170644
173179
|
init_domains2(),
|
|
170645
173180
|
init_logs(),
|
|
170646
173181
|
init_scores(),
|
|
@@ -170655,6 +173190,7 @@ var init_games2 = __esm(async () => {
|
|
|
170655
173190
|
gamesRouter.route("/", gameVerifyRouter);
|
|
170656
173191
|
gamesRouter.route("/", gameUploadsRouter);
|
|
170657
173192
|
gamesRouter.route("/", gameDeployRouter);
|
|
173193
|
+
gamesRouter.route("/", gameDeploymentStateRouter);
|
|
170658
173194
|
gamesRouter.route("/", gameDomainsRouter);
|
|
170659
173195
|
gamesRouter.route("/", gameLogsRouter);
|
|
170660
173196
|
gamesRouter.route("/", gameScoresRouter);
|
|
@@ -171586,7 +174122,7 @@ var init_domains3 = __esm3(() => {
|
|
|
171586
174122
|
});
|
|
171587
174123
|
var init_env_vars2 = () => {};
|
|
171588
174124
|
var GAME_MEMBER_ROLES2;
|
|
171589
|
-
var
|
|
174125
|
+
var init_game4 = __esm3(() => {
|
|
171590
174126
|
GAME_MEMBER_ROLES2 = {
|
|
171591
174127
|
OWNER: "owner",
|
|
171592
174128
|
COLLABORATOR: "collaborator"
|
|
@@ -171661,13 +174197,13 @@ var init_cloudflare2 = __esm3(() => {
|
|
|
171661
174197
|
LOCAL_PREFIX: "local-"
|
|
171662
174198
|
};
|
|
171663
174199
|
});
|
|
171664
|
-
var
|
|
174200
|
+
var init_src6 = __esm3(() => {
|
|
171665
174201
|
init_auth3();
|
|
171666
174202
|
init_dashboard2();
|
|
171667
174203
|
init_typescript2();
|
|
171668
174204
|
init_domains3();
|
|
171669
174205
|
init_env_vars2();
|
|
171670
|
-
|
|
174206
|
+
init_game4();
|
|
171671
174207
|
init_platform3();
|
|
171672
174208
|
init_timeback8();
|
|
171673
174209
|
init_cloudflare2();
|
|
@@ -171677,7 +174213,7 @@ var DEMO_USER_IDS2;
|
|
|
171677
174213
|
var DEMO_USERS2;
|
|
171678
174214
|
var DEMO_USER2;
|
|
171679
174215
|
var init_demo_users2 = __esm3(() => {
|
|
171680
|
-
|
|
174216
|
+
init_src6();
|
|
171681
174217
|
now2 = new Date;
|
|
171682
174218
|
DEMO_USER_IDS2 = {
|
|
171683
174219
|
player: "00000000-0000-0000-0000-000000000001",
|