@playcademy/sandbox 0.10.1-beta.6 → 0.10.1-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/cli.js +170 -15
- package/dist/server.js +170 -15
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1128,7 +1128,7 @@ var package_default;
|
|
|
1128
1128
|
var init_package = __esm(() => {
|
|
1129
1129
|
package_default = {
|
|
1130
1130
|
name: "@playcademy/sandbox",
|
|
1131
|
-
version: "0.10.1-beta.
|
|
1131
|
+
version: "0.10.1-beta.8",
|
|
1132
1132
|
description: "Local development server for Playcademy game development",
|
|
1133
1133
|
type: "module",
|
|
1134
1134
|
exports: {
|
|
@@ -18645,7 +18645,10 @@ var init_table5 = __esm(() => {
|
|
|
18645
18645
|
bookmarkCapturedAt: timestamp("bookmark_captured_at", { withTimezone: true }),
|
|
18646
18646
|
isActive: boolean("is_active").notNull().default(false),
|
|
18647
18647
|
resources: jsonb("resources").$type(),
|
|
18648
|
-
deployedAt: timestamp("deployed_at", { withTimezone: true }).notNull().defaultNow()
|
|
18648
|
+
deployedAt: timestamp("deployed_at", { withTimezone: true }).notNull().defaultNow(),
|
|
18649
|
+
deployJobId: uuid("deploy_job_id").references(() => gameDeployJobs.id, {
|
|
18650
|
+
onDelete: "set null"
|
|
18651
|
+
})
|
|
18649
18652
|
}, (table3) => [index("game_deployments_game_target_idx").on(table3.gameId, table3.target)]);
|
|
18650
18653
|
gameDeployJobs = pgTable("game_deploy_jobs", {
|
|
18651
18654
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
@@ -30464,7 +30467,11 @@ class DeployJobService {
|
|
|
30464
30467
|
const uploadDeps = this.buildUploadDeps();
|
|
30465
30468
|
try {
|
|
30466
30469
|
assertLease();
|
|
30467
|
-
for await (const step of this.deps.runDeploy(game2.slug, request, user,
|
|
30470
|
+
for await (const step of this.deps.runDeploy(game2.slug, request, user, {
|
|
30471
|
+
uploadDeps,
|
|
30472
|
+
extractZip: extractZipToDirectory,
|
|
30473
|
+
jobId
|
|
30474
|
+
})) {
|
|
30468
30475
|
assertLease();
|
|
30469
30476
|
if (step.type === "status" && "message" in step.data && typeof step.data.message === "string") {
|
|
30470
30477
|
const details = "details" in step.data && isEventDetails(step.data.details) ? step.data.details : undefined;
|
|
@@ -35892,9 +35899,9 @@ class DeployService {
|
|
|
35892
35899
|
}
|
|
35893
35900
|
return cf;
|
|
35894
35901
|
}
|
|
35895
|
-
async* deploy(slug, request, user,
|
|
35902
|
+
async* deploy(slug, request, user, options = {}) {
|
|
35896
35903
|
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
35897
|
-
const context2 = { slug, request, user, game: game2,
|
|
35904
|
+
const context2 = { slug, request, user, game: game2, ...options };
|
|
35898
35905
|
switch (request.target ?? "game") {
|
|
35899
35906
|
case "dashboard": {
|
|
35900
35907
|
yield* this.deployDashboard(context2);
|
|
@@ -35910,7 +35917,7 @@ class DeployService {
|
|
|
35910
35917
|
}
|
|
35911
35918
|
}
|
|
35912
35919
|
async* deployGame(context2) {
|
|
35913
|
-
const { slug, request, user, game: game2, uploadDeps, extractZip } = context2;
|
|
35920
|
+
const { slug, request, user, game: game2, uploadDeps, extractZip, jobId } = context2;
|
|
35914
35921
|
const cf = this.getCloudflare();
|
|
35915
35922
|
const db2 = this.deps.db;
|
|
35916
35923
|
const flags2 = this.validateGameRequest(request);
|
|
@@ -36015,7 +36022,8 @@ class DeployService {
|
|
|
36015
36022
|
request,
|
|
36016
36023
|
user,
|
|
36017
36024
|
flags: flags2,
|
|
36018
|
-
database: databaseOutcome
|
|
36025
|
+
database: databaseOutcome,
|
|
36026
|
+
jobId
|
|
36019
36027
|
});
|
|
36020
36028
|
if (request.pruneSecrets?.length) {
|
|
36021
36029
|
yield* this.pruneManagedSecretsStep(game2.id, result.deploymentId, request.pruneSecrets);
|
|
@@ -36023,7 +36031,7 @@ class DeployService {
|
|
|
36023
36031
|
yield { type: "complete", data: updatedGame };
|
|
36024
36032
|
}
|
|
36025
36033
|
async* deployDashboard(context2) {
|
|
36026
|
-
const { slug, request, user, game: game2 } = context2;
|
|
36034
|
+
const { slug, request, user, game: game2, jobId } = context2;
|
|
36027
36035
|
const db2 = this.deps.db;
|
|
36028
36036
|
const contract = this.validateDashboardRequest(request, context2.uploadDeps, context2.extractZip);
|
|
36029
36037
|
const deploymentId = getDashboardDeploymentId(slug, this.deps.config.sstStage);
|
|
@@ -36113,7 +36121,8 @@ class DeployService {
|
|
|
36113
36121
|
url: result.url,
|
|
36114
36122
|
codeHash,
|
|
36115
36123
|
resources: result.resources,
|
|
36116
|
-
target: "dashboard"
|
|
36124
|
+
target: "dashboard",
|
|
36125
|
+
deployJobId: jobId
|
|
36117
36126
|
});
|
|
36118
36127
|
setAttribute("app.deploy.code_hash", codeHash);
|
|
36119
36128
|
try {
|
|
@@ -36743,7 +36752,8 @@ class DeployService {
|
|
|
36743
36752
|
request,
|
|
36744
36753
|
user,
|
|
36745
36754
|
flags: flags2,
|
|
36746
|
-
database
|
|
36755
|
+
database,
|
|
36756
|
+
jobId
|
|
36747
36757
|
}) {
|
|
36748
36758
|
const { hasBackend, hasFrontend, hasMetadata } = flags2;
|
|
36749
36759
|
const db2 = this.deps.db;
|
|
@@ -36758,7 +36768,8 @@ class DeployService {
|
|
|
36758
36768
|
timeTravelBookmark: database.timeTravelBookmark,
|
|
36759
36769
|
bookmarkCapturedAt: database.bookmarkCapturedAt,
|
|
36760
36770
|
resources: result.resources,
|
|
36761
|
-
target: "game"
|
|
36771
|
+
target: "game",
|
|
36772
|
+
deployJobId: jobId
|
|
36762
36773
|
});
|
|
36763
36774
|
await this.persistArtifactHashes(game2.id, {
|
|
36764
36775
|
buildHash: hasFrontend ? request.buildHash : undefined,
|
|
@@ -37032,7 +37043,8 @@ class DeployService {
|
|
|
37032
37043
|
timeTravelBookmark: record.timeTravelBookmark ?? null,
|
|
37033
37044
|
bookmarkCapturedAt: record.bookmarkCapturedAt ?? null,
|
|
37034
37045
|
resources: record.resources,
|
|
37035
|
-
isActive: true
|
|
37046
|
+
isActive: true,
|
|
37047
|
+
deployJobId: record.deployJobId ?? null
|
|
37036
37048
|
});
|
|
37037
37049
|
});
|
|
37038
37050
|
}
|
|
@@ -38455,7 +38467,7 @@ function createGameServices(deps) {
|
|
|
38455
38467
|
uploadBucket: config3.uploadBucket,
|
|
38456
38468
|
storage,
|
|
38457
38469
|
validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
|
|
38458
|
-
runDeploy: (slug, request, user,
|
|
38470
|
+
runDeploy: (slug, request, user, options) => deploy.deploy(slug, request, user, options),
|
|
38459
38471
|
notifyDeploymentFailure: (failure) => deploy.notifyDeploymentFailure(failure)
|
|
38460
38472
|
});
|
|
38461
38473
|
const logs = new LogsService({
|
|
@@ -46851,6 +46863,95 @@ var init_kv_service = __esm(() => {
|
|
|
46851
46863
|
init_errors2();
|
|
46852
46864
|
});
|
|
46853
46865
|
|
|
46866
|
+
// ../utils/src/url.ts
|
|
46867
|
+
function publishableRepositoryUrl(value) {
|
|
46868
|
+
let url;
|
|
46869
|
+
try {
|
|
46870
|
+
url = new URL(value);
|
|
46871
|
+
} catch {
|
|
46872
|
+
return null;
|
|
46873
|
+
}
|
|
46874
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
|
|
46875
|
+
return null;
|
|
46876
|
+
}
|
|
46877
|
+
return value;
|
|
46878
|
+
}
|
|
46879
|
+
|
|
46880
|
+
// ../api-core/src/services/releases.service.ts
|
|
46881
|
+
class ReleasesService {
|
|
46882
|
+
deps;
|
|
46883
|
+
constructor(deps) {
|
|
46884
|
+
this.deps = deps;
|
|
46885
|
+
}
|
|
46886
|
+
async list() {
|
|
46887
|
+
const rows = await this.deps.db.select(releaseColumns()).from(gameDeployments).innerJoin(games, eq(gameDeployments.gameId, games.id)).leftJoin(gameDeployJobs, eq(gameDeployments.deployJobId, gameDeployJobs.id)).where(and(activeWorkerDeployment(), listedGame())).orderBy(games.slug);
|
|
46888
|
+
setAttributes({ "app.releases.count": rows.length });
|
|
46889
|
+
return { releases: rows.map(toRelease) };
|
|
46890
|
+
}
|
|
46891
|
+
async get(slug, { history } = {}) {
|
|
46892
|
+
const game2 = await this.deps.db.query.games.findFirst({
|
|
46893
|
+
where: and(eq(games.slug, slug), listedGame()),
|
|
46894
|
+
columns: { id: true }
|
|
46895
|
+
});
|
|
46896
|
+
if (!game2) {
|
|
46897
|
+
throw new NotFoundError("Game", slug);
|
|
46898
|
+
}
|
|
46899
|
+
const rows = await this.deps.db.select(releaseColumns()).from(gameDeployments).innerJoin(games, eq(gameDeployments.gameId, games.id)).leftJoin(gameDeployJobs, eq(gameDeployments.deployJobId, gameDeployJobs.id)).where(and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "game"))).orderBy(desc(gameDeployments.deployedAt)).limit(Math.max(1, history ?? 0));
|
|
46900
|
+
setAttributes({
|
|
46901
|
+
"app.releases.history_requested": history ?? 0,
|
|
46902
|
+
"app.releases.history_rows": rows.length
|
|
46903
|
+
});
|
|
46904
|
+
const releases = rows.map(toRelease);
|
|
46905
|
+
const current = releases[0] ?? null;
|
|
46906
|
+
return history === undefined ? { current } : { current, history: releases };
|
|
46907
|
+
}
|
|
46908
|
+
}
|
|
46909
|
+
function releaseColumns() {
|
|
46910
|
+
return {
|
|
46911
|
+
slug: games.slug,
|
|
46912
|
+
url: games.deploymentUrl,
|
|
46913
|
+
source: sql`${gameDeployJobs.request} -> 'source'`,
|
|
46914
|
+
deployedAt: gameDeployments.deployedAt
|
|
46915
|
+
};
|
|
46916
|
+
}
|
|
46917
|
+
function listedGame() {
|
|
46918
|
+
return ne(games.visibility, "internal");
|
|
46919
|
+
}
|
|
46920
|
+
function activeWorkerDeployment() {
|
|
46921
|
+
return and(eq(gameDeployments.isActive, true), eq(gameDeployments.target, "game"));
|
|
46922
|
+
}
|
|
46923
|
+
function toRelease(row) {
|
|
46924
|
+
return {
|
|
46925
|
+
slug: row.slug,
|
|
46926
|
+
url: row.url,
|
|
46927
|
+
source: normalizeSource(row.source),
|
|
46928
|
+
deployedAt: row.deployedAt.toISOString()
|
|
46929
|
+
};
|
|
46930
|
+
}
|
|
46931
|
+
function normalizeSource(value) {
|
|
46932
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
46933
|
+
return null;
|
|
46934
|
+
}
|
|
46935
|
+
const raw = value;
|
|
46936
|
+
const repository = nullableString(raw.repository);
|
|
46937
|
+
return {
|
|
46938
|
+
repository: repository === null ? null : publishableRepositoryUrl(repository),
|
|
46939
|
+
commit: nullableString(raw.commit),
|
|
46940
|
+
branch: nullableString(raw.branch),
|
|
46941
|
+
dirty: raw.dirty === true,
|
|
46942
|
+
externalBuild: raw.externalBuild === true
|
|
46943
|
+
};
|
|
46944
|
+
}
|
|
46945
|
+
function nullableString(value) {
|
|
46946
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
46947
|
+
}
|
|
46948
|
+
var init_releases_service = __esm(() => {
|
|
46949
|
+
init_drizzle_orm();
|
|
46950
|
+
init_tables_index();
|
|
46951
|
+
init_spans();
|
|
46952
|
+
init_errors2();
|
|
46953
|
+
});
|
|
46954
|
+
|
|
46854
46955
|
// ../api-core/src/services/secrets.service.ts
|
|
46855
46956
|
class SecretsService {
|
|
46856
46957
|
deps;
|
|
@@ -47590,7 +47691,7 @@ var init_emoji = __esm(() => {
|
|
|
47590
47691
|
function requestsBinding(binding) {
|
|
47591
47692
|
return binding === true || Array.isArray(binding) && binding.length > 0;
|
|
47592
47693
|
}
|
|
47593
|
-
var HttpUrlSchema, GameEmojiSchema, GamePermissionsSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, InsertGameDeploymentStateSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, AddGameDashboardUserSchema, VerifyGameDashboardLoginSchema, AcceptGameDashboardInviteSchema, AcceptGameDashboardResetSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SecretsDiffRequestSchema, SeedRequestSchema, SchemaInfoSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployMigrationSchema, DeployDatabaseSchema, DatabaseResetDatabaseSchema, DatabaseResetRequestSchema, BaselineEvidenceSchema, DeployBaselineSchema, DeploymentStateBaselineSchema, MigrationRealignSchema, MigrationResolveSchema, DatabaseRestoreSchema, DeployBlockedReportSchema, DeployRequestSchema;
|
|
47694
|
+
var HttpUrlSchema, GameEmojiSchema, GamePermissionsSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, InsertGameDeploymentStateSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, AddGameDashboardUserSchema, VerifyGameDashboardLoginSchema, AcceptGameDashboardInviteSchema, AcceptGameDashboardResetSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SecretsDiffRequestSchema, SeedRequestSchema, SchemaInfoSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployMigrationSchema, DeployDatabaseSchema, DatabaseResetDatabaseSchema, DatabaseResetRequestSchema, BaselineEvidenceSchema, DeployBaselineSchema, DeploySourceSchema, DeploymentStateBaselineSchema, MigrationRealignSchema, MigrationResolveSchema, DatabaseRestoreSchema, DeployBlockedReportSchema, DeployRequestSchema;
|
|
47594
47695
|
var init_schemas2 = __esm(() => {
|
|
47595
47696
|
init_drizzle_zod();
|
|
47596
47697
|
init_esm();
|
|
@@ -47836,6 +47937,13 @@ var init_schemas2 = __esm(() => {
|
|
|
47836
47937
|
message: "A baseline must claim something — migration history, a schema snapshot, or artifact hashes",
|
|
47837
47938
|
path: ["lastAppliedMigrationTag"]
|
|
47838
47939
|
});
|
|
47940
|
+
DeploySourceSchema = exports_external.object({
|
|
47941
|
+
repository: exports_external.string().url().nullable().transform((value) => value === null ? null : publishableRepositoryUrl(value)),
|
|
47942
|
+
commit: exports_external.string().regex(/^[0-9a-f]{40}$/, "commit must be a full 40-character lowercase SHA").nullable(),
|
|
47943
|
+
branch: exports_external.string().min(1).nullable(),
|
|
47944
|
+
dirty: exports_external.boolean(),
|
|
47945
|
+
externalBuild: exports_external.boolean().default(false)
|
|
47946
|
+
});
|
|
47839
47947
|
DeploymentStateBaselineSchema = exports_external.object({
|
|
47840
47948
|
lastAppliedMigrationTag: exports_external.string().min(1).optional(),
|
|
47841
47949
|
journal: exports_external.array(exports_external.object({
|
|
@@ -47907,6 +48015,7 @@ var init_schemas2 = __esm(() => {
|
|
|
47907
48015
|
buildHash: exports_external.string().min(1).optional(),
|
|
47908
48016
|
integrationsHash: exports_external.string().min(1).optional(),
|
|
47909
48017
|
pruneSecrets: exports_external.array(exports_external.string().min(1)).optional(),
|
|
48018
|
+
source: DeploySourceSchema.optional(),
|
|
47910
48019
|
metadata: exports_external.object({
|
|
47911
48020
|
displayName: exports_external.string().optional(),
|
|
47912
48021
|
description: exports_external.string().optional(),
|
|
@@ -109825,6 +109934,7 @@ function createPlatformServices(deps) {
|
|
|
109825
109934
|
const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109826
109935
|
const secrets = new SecretsService({ db: db2, config: config5, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109827
109936
|
const domain3 = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
|
|
109937
|
+
const releases = new ReleasesService({ db: db2 });
|
|
109828
109938
|
const deploymentState = new DeploymentStateService({
|
|
109829
109939
|
db: db2,
|
|
109830
109940
|
config: config5,
|
|
@@ -109884,6 +109994,7 @@ function createPlatformServices(deps) {
|
|
|
109884
109994
|
kv,
|
|
109885
109995
|
secrets,
|
|
109886
109996
|
domain: domain3,
|
|
109997
|
+
releases,
|
|
109887
109998
|
deploymentState,
|
|
109888
109999
|
database,
|
|
109889
110000
|
seed,
|
|
@@ -109902,6 +110013,7 @@ var init_platform2 = __esm(async () => {
|
|
|
109902
110013
|
init_deployment_state_service();
|
|
109903
110014
|
init_domain_service();
|
|
109904
110015
|
init_kv_service();
|
|
110016
|
+
init_releases_service();
|
|
109905
110017
|
init_secrets_service();
|
|
109906
110018
|
init_seed_service();
|
|
109907
110019
|
init_upload_service();
|
|
@@ -167633,6 +167745,47 @@ var init_lti_controller = __esm(() => {
|
|
|
167633
167745
|
});
|
|
167634
167746
|
});
|
|
167635
167747
|
|
|
167748
|
+
// ../api-core/src/controllers/releases.controller.ts
|
|
167749
|
+
function requireNoQuery(url4) {
|
|
167750
|
+
if (url4.search !== "") {
|
|
167751
|
+
throw ApiError.badRequest("This endpoint takes no query parameters");
|
|
167752
|
+
}
|
|
167753
|
+
}
|
|
167754
|
+
function parseHistoryParam(url4) {
|
|
167755
|
+
if (url4.search === "") {
|
|
167756
|
+
return;
|
|
167757
|
+
}
|
|
167758
|
+
const match3 = CANONICAL_HISTORY.exec(url4.search);
|
|
167759
|
+
if (!match3) {
|
|
167760
|
+
throw ApiError.badRequest(`The only supported query is ?history=N with N an integer between 1 and ${MAX_HISTORY}`);
|
|
167761
|
+
}
|
|
167762
|
+
return Number(match3[1]);
|
|
167763
|
+
}
|
|
167764
|
+
function cached3(body2) {
|
|
167765
|
+
return Response.json(body2, {
|
|
167766
|
+
headers: {
|
|
167767
|
+
"Content-Type": "application/json",
|
|
167768
|
+
"Cache-Control": CACHE_CONTROL2
|
|
167769
|
+
}
|
|
167770
|
+
});
|
|
167771
|
+
}
|
|
167772
|
+
async function list3(ctx) {
|
|
167773
|
+
requireNoQuery(ctx.url);
|
|
167774
|
+
return cached3(await ctx.services.releases.list());
|
|
167775
|
+
}
|
|
167776
|
+
async function get2(ctx) {
|
|
167777
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
167778
|
+
const history2 = parseHistoryParam(ctx.url);
|
|
167779
|
+
return cached3(await ctx.services.releases.get(slug2, { history: history2 }));
|
|
167780
|
+
}
|
|
167781
|
+
var CACHE_CONTROL2 = "public, max-age=60", MAX_HISTORY = 100, CANONICAL_HISTORY, releases;
|
|
167782
|
+
var init_releases_controller = __esm(() => {
|
|
167783
|
+
init_errors2();
|
|
167784
|
+
init_utils7();
|
|
167785
|
+
CANONICAL_HISTORY = /^\?history=([1-9]\d?|100)$/;
|
|
167786
|
+
releases = defineControllerNames("releases", { list: list3, get: get2 });
|
|
167787
|
+
});
|
|
167788
|
+
|
|
167636
167789
|
// ../api-core/src/controllers/secrets.controller.ts
|
|
167637
167790
|
var listKeys2, setSecrets, deleteSecret, diff, secrets;
|
|
167638
167791
|
var init_secrets_controller = __esm(() => {
|
|
@@ -168795,6 +168948,7 @@ var init_controllers = __esm(() => {
|
|
|
168795
168948
|
init_leaderboard_controller();
|
|
168796
168949
|
init_logs_controller();
|
|
168797
168950
|
init_lti_controller();
|
|
168951
|
+
init_releases_controller();
|
|
168798
168952
|
init_secrets_controller();
|
|
168799
168953
|
init_seed_controller();
|
|
168800
168954
|
init_session_controller();
|
|
@@ -169246,7 +169400,8 @@ var init_deploy = __esm(async () => {
|
|
|
169246
169400
|
config: body2.config,
|
|
169247
169401
|
bindings: body2.bindings,
|
|
169248
169402
|
schema: body2.schema,
|
|
169249
|
-
metadata: body2.metadata
|
|
169403
|
+
metadata: body2.metadata,
|
|
169404
|
+
source: body2.source
|
|
169250
169405
|
},
|
|
169251
169406
|
events,
|
|
169252
169407
|
result: {
|
package/dist/server.js
CHANGED
|
@@ -1127,7 +1127,7 @@ var package_default;
|
|
|
1127
1127
|
var init_package = __esm(() => {
|
|
1128
1128
|
package_default = {
|
|
1129
1129
|
name: "@playcademy/sandbox",
|
|
1130
|
-
version: "0.10.1-beta.
|
|
1130
|
+
version: "0.10.1-beta.8",
|
|
1131
1131
|
description: "Local development server for Playcademy game development",
|
|
1132
1132
|
type: "module",
|
|
1133
1133
|
exports: {
|
|
@@ -18644,7 +18644,10 @@ var init_table5 = __esm(() => {
|
|
|
18644
18644
|
bookmarkCapturedAt: timestamp("bookmark_captured_at", { withTimezone: true }),
|
|
18645
18645
|
isActive: boolean("is_active").notNull().default(false),
|
|
18646
18646
|
resources: jsonb("resources").$type(),
|
|
18647
|
-
deployedAt: timestamp("deployed_at", { withTimezone: true }).notNull().defaultNow()
|
|
18647
|
+
deployedAt: timestamp("deployed_at", { withTimezone: true }).notNull().defaultNow(),
|
|
18648
|
+
deployJobId: uuid("deploy_job_id").references(() => gameDeployJobs.id, {
|
|
18649
|
+
onDelete: "set null"
|
|
18650
|
+
})
|
|
18648
18651
|
}, (table3) => [index("game_deployments_game_target_idx").on(table3.gameId, table3.target)]);
|
|
18649
18652
|
gameDeployJobs = pgTable("game_deploy_jobs", {
|
|
18650
18653
|
id: uuid("id").primaryKey().defaultRandom(),
|
|
@@ -30463,7 +30466,11 @@ class DeployJobService {
|
|
|
30463
30466
|
const uploadDeps = this.buildUploadDeps();
|
|
30464
30467
|
try {
|
|
30465
30468
|
assertLease();
|
|
30466
|
-
for await (const step of this.deps.runDeploy(game2.slug, request, user,
|
|
30469
|
+
for await (const step of this.deps.runDeploy(game2.slug, request, user, {
|
|
30470
|
+
uploadDeps,
|
|
30471
|
+
extractZip: extractZipToDirectory,
|
|
30472
|
+
jobId
|
|
30473
|
+
})) {
|
|
30467
30474
|
assertLease();
|
|
30468
30475
|
if (step.type === "status" && "message" in step.data && typeof step.data.message === "string") {
|
|
30469
30476
|
const details = "details" in step.data && isEventDetails(step.data.details) ? step.data.details : undefined;
|
|
@@ -35891,9 +35898,9 @@ class DeployService {
|
|
|
35891
35898
|
}
|
|
35892
35899
|
return cf;
|
|
35893
35900
|
}
|
|
35894
|
-
async* deploy(slug, request, user,
|
|
35901
|
+
async* deploy(slug, request, user, options = {}) {
|
|
35895
35902
|
const game2 = await this.deps.validateDeveloperAccessBySlug(user, slug);
|
|
35896
|
-
const context2 = { slug, request, user, game: game2,
|
|
35903
|
+
const context2 = { slug, request, user, game: game2, ...options };
|
|
35897
35904
|
switch (request.target ?? "game") {
|
|
35898
35905
|
case "dashboard": {
|
|
35899
35906
|
yield* this.deployDashboard(context2);
|
|
@@ -35909,7 +35916,7 @@ class DeployService {
|
|
|
35909
35916
|
}
|
|
35910
35917
|
}
|
|
35911
35918
|
async* deployGame(context2) {
|
|
35912
|
-
const { slug, request, user, game: game2, uploadDeps, extractZip } = context2;
|
|
35919
|
+
const { slug, request, user, game: game2, uploadDeps, extractZip, jobId } = context2;
|
|
35913
35920
|
const cf = this.getCloudflare();
|
|
35914
35921
|
const db2 = this.deps.db;
|
|
35915
35922
|
const flags2 = this.validateGameRequest(request);
|
|
@@ -36014,7 +36021,8 @@ class DeployService {
|
|
|
36014
36021
|
request,
|
|
36015
36022
|
user,
|
|
36016
36023
|
flags: flags2,
|
|
36017
|
-
database: databaseOutcome
|
|
36024
|
+
database: databaseOutcome,
|
|
36025
|
+
jobId
|
|
36018
36026
|
});
|
|
36019
36027
|
if (request.pruneSecrets?.length) {
|
|
36020
36028
|
yield* this.pruneManagedSecretsStep(game2.id, result.deploymentId, request.pruneSecrets);
|
|
@@ -36022,7 +36030,7 @@ class DeployService {
|
|
|
36022
36030
|
yield { type: "complete", data: updatedGame };
|
|
36023
36031
|
}
|
|
36024
36032
|
async* deployDashboard(context2) {
|
|
36025
|
-
const { slug, request, user, game: game2 } = context2;
|
|
36033
|
+
const { slug, request, user, game: game2, jobId } = context2;
|
|
36026
36034
|
const db2 = this.deps.db;
|
|
36027
36035
|
const contract = this.validateDashboardRequest(request, context2.uploadDeps, context2.extractZip);
|
|
36028
36036
|
const deploymentId = getDashboardDeploymentId(slug, this.deps.config.sstStage);
|
|
@@ -36112,7 +36120,8 @@ class DeployService {
|
|
|
36112
36120
|
url: result.url,
|
|
36113
36121
|
codeHash,
|
|
36114
36122
|
resources: result.resources,
|
|
36115
|
-
target: "dashboard"
|
|
36123
|
+
target: "dashboard",
|
|
36124
|
+
deployJobId: jobId
|
|
36116
36125
|
});
|
|
36117
36126
|
setAttribute("app.deploy.code_hash", codeHash);
|
|
36118
36127
|
try {
|
|
@@ -36742,7 +36751,8 @@ class DeployService {
|
|
|
36742
36751
|
request,
|
|
36743
36752
|
user,
|
|
36744
36753
|
flags: flags2,
|
|
36745
|
-
database
|
|
36754
|
+
database,
|
|
36755
|
+
jobId
|
|
36746
36756
|
}) {
|
|
36747
36757
|
const { hasBackend, hasFrontend, hasMetadata } = flags2;
|
|
36748
36758
|
const db2 = this.deps.db;
|
|
@@ -36757,7 +36767,8 @@ class DeployService {
|
|
|
36757
36767
|
timeTravelBookmark: database.timeTravelBookmark,
|
|
36758
36768
|
bookmarkCapturedAt: database.bookmarkCapturedAt,
|
|
36759
36769
|
resources: result.resources,
|
|
36760
|
-
target: "game"
|
|
36770
|
+
target: "game",
|
|
36771
|
+
deployJobId: jobId
|
|
36761
36772
|
});
|
|
36762
36773
|
await this.persistArtifactHashes(game2.id, {
|
|
36763
36774
|
buildHash: hasFrontend ? request.buildHash : undefined,
|
|
@@ -37031,7 +37042,8 @@ class DeployService {
|
|
|
37031
37042
|
timeTravelBookmark: record.timeTravelBookmark ?? null,
|
|
37032
37043
|
bookmarkCapturedAt: record.bookmarkCapturedAt ?? null,
|
|
37033
37044
|
resources: record.resources,
|
|
37034
|
-
isActive: true
|
|
37045
|
+
isActive: true,
|
|
37046
|
+
deployJobId: record.deployJobId ?? null
|
|
37035
37047
|
});
|
|
37036
37048
|
});
|
|
37037
37049
|
}
|
|
@@ -38454,7 +38466,7 @@ function createGameServices(deps) {
|
|
|
38454
38466
|
uploadBucket: config3.uploadBucket,
|
|
38455
38467
|
storage,
|
|
38456
38468
|
validateDeveloperAccessBySlug: (user, slug) => game2.validateDeveloperAccessBySlug(user, slug),
|
|
38457
|
-
runDeploy: (slug, request, user,
|
|
38469
|
+
runDeploy: (slug, request, user, options) => deploy.deploy(slug, request, user, options),
|
|
38458
38470
|
notifyDeploymentFailure: (failure) => deploy.notifyDeploymentFailure(failure)
|
|
38459
38471
|
});
|
|
38460
38472
|
const logs = new LogsService({
|
|
@@ -46850,6 +46862,95 @@ var init_kv_service = __esm(() => {
|
|
|
46850
46862
|
init_errors2();
|
|
46851
46863
|
});
|
|
46852
46864
|
|
|
46865
|
+
// ../utils/src/url.ts
|
|
46866
|
+
function publishableRepositoryUrl(value) {
|
|
46867
|
+
let url;
|
|
46868
|
+
try {
|
|
46869
|
+
url = new URL(value);
|
|
46870
|
+
} catch {
|
|
46871
|
+
return null;
|
|
46872
|
+
}
|
|
46873
|
+
if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) {
|
|
46874
|
+
return null;
|
|
46875
|
+
}
|
|
46876
|
+
return value;
|
|
46877
|
+
}
|
|
46878
|
+
|
|
46879
|
+
// ../api-core/src/services/releases.service.ts
|
|
46880
|
+
class ReleasesService {
|
|
46881
|
+
deps;
|
|
46882
|
+
constructor(deps) {
|
|
46883
|
+
this.deps = deps;
|
|
46884
|
+
}
|
|
46885
|
+
async list() {
|
|
46886
|
+
const rows = await this.deps.db.select(releaseColumns()).from(gameDeployments).innerJoin(games, eq(gameDeployments.gameId, games.id)).leftJoin(gameDeployJobs, eq(gameDeployments.deployJobId, gameDeployJobs.id)).where(and(activeWorkerDeployment(), listedGame())).orderBy(games.slug);
|
|
46887
|
+
setAttributes({ "app.releases.count": rows.length });
|
|
46888
|
+
return { releases: rows.map(toRelease) };
|
|
46889
|
+
}
|
|
46890
|
+
async get(slug, { history } = {}) {
|
|
46891
|
+
const game2 = await this.deps.db.query.games.findFirst({
|
|
46892
|
+
where: and(eq(games.slug, slug), listedGame()),
|
|
46893
|
+
columns: { id: true }
|
|
46894
|
+
});
|
|
46895
|
+
if (!game2) {
|
|
46896
|
+
throw new NotFoundError("Game", slug);
|
|
46897
|
+
}
|
|
46898
|
+
const rows = await this.deps.db.select(releaseColumns()).from(gameDeployments).innerJoin(games, eq(gameDeployments.gameId, games.id)).leftJoin(gameDeployJobs, eq(gameDeployments.deployJobId, gameDeployJobs.id)).where(and(eq(gameDeployments.gameId, game2.id), eq(gameDeployments.target, "game"))).orderBy(desc(gameDeployments.deployedAt)).limit(Math.max(1, history ?? 0));
|
|
46899
|
+
setAttributes({
|
|
46900
|
+
"app.releases.history_requested": history ?? 0,
|
|
46901
|
+
"app.releases.history_rows": rows.length
|
|
46902
|
+
});
|
|
46903
|
+
const releases = rows.map(toRelease);
|
|
46904
|
+
const current = releases[0] ?? null;
|
|
46905
|
+
return history === undefined ? { current } : { current, history: releases };
|
|
46906
|
+
}
|
|
46907
|
+
}
|
|
46908
|
+
function releaseColumns() {
|
|
46909
|
+
return {
|
|
46910
|
+
slug: games.slug,
|
|
46911
|
+
url: games.deploymentUrl,
|
|
46912
|
+
source: sql`${gameDeployJobs.request} -> 'source'`,
|
|
46913
|
+
deployedAt: gameDeployments.deployedAt
|
|
46914
|
+
};
|
|
46915
|
+
}
|
|
46916
|
+
function listedGame() {
|
|
46917
|
+
return ne(games.visibility, "internal");
|
|
46918
|
+
}
|
|
46919
|
+
function activeWorkerDeployment() {
|
|
46920
|
+
return and(eq(gameDeployments.isActive, true), eq(gameDeployments.target, "game"));
|
|
46921
|
+
}
|
|
46922
|
+
function toRelease(row) {
|
|
46923
|
+
return {
|
|
46924
|
+
slug: row.slug,
|
|
46925
|
+
url: row.url,
|
|
46926
|
+
source: normalizeSource(row.source),
|
|
46927
|
+
deployedAt: row.deployedAt.toISOString()
|
|
46928
|
+
};
|
|
46929
|
+
}
|
|
46930
|
+
function normalizeSource(value) {
|
|
46931
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
46932
|
+
return null;
|
|
46933
|
+
}
|
|
46934
|
+
const raw = value;
|
|
46935
|
+
const repository = nullableString(raw.repository);
|
|
46936
|
+
return {
|
|
46937
|
+
repository: repository === null ? null : publishableRepositoryUrl(repository),
|
|
46938
|
+
commit: nullableString(raw.commit),
|
|
46939
|
+
branch: nullableString(raw.branch),
|
|
46940
|
+
dirty: raw.dirty === true,
|
|
46941
|
+
externalBuild: raw.externalBuild === true
|
|
46942
|
+
};
|
|
46943
|
+
}
|
|
46944
|
+
function nullableString(value) {
|
|
46945
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
46946
|
+
}
|
|
46947
|
+
var init_releases_service = __esm(() => {
|
|
46948
|
+
init_drizzle_orm();
|
|
46949
|
+
init_tables_index();
|
|
46950
|
+
init_spans();
|
|
46951
|
+
init_errors2();
|
|
46952
|
+
});
|
|
46953
|
+
|
|
46853
46954
|
// ../api-core/src/services/secrets.service.ts
|
|
46854
46955
|
class SecretsService {
|
|
46855
46956
|
deps;
|
|
@@ -47589,7 +47690,7 @@ var init_emoji = __esm(() => {
|
|
|
47589
47690
|
function requestsBinding(binding) {
|
|
47590
47691
|
return binding === true || Array.isArray(binding) && binding.length > 0;
|
|
47591
47692
|
}
|
|
47592
|
-
var HttpUrlSchema, GameEmojiSchema, GamePermissionsSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, InsertGameDeploymentStateSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, AddGameDashboardUserSchema, VerifyGameDashboardLoginSchema, AcceptGameDashboardInviteSchema, AcceptGameDashboardResetSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SecretsDiffRequestSchema, SeedRequestSchema, SchemaInfoSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployMigrationSchema, DeployDatabaseSchema, DatabaseResetDatabaseSchema, DatabaseResetRequestSchema, BaselineEvidenceSchema, DeployBaselineSchema, DeploymentStateBaselineSchema, MigrationRealignSchema, MigrationResolveSchema, DatabaseRestoreSchema, DeployBlockedReportSchema, DeployRequestSchema;
|
|
47693
|
+
var HttpUrlSchema, GameEmojiSchema, GamePermissionsSchema, GameMetadataRecordSchema, InsertGameSchema, UpdateGameSchema, InsertGameDeploymentSchema, InsertGameDeployJobSchema, InsertGameDeploymentStateSchema, UpsertGameMetadataSchema, PatchGameMetadataSchema, AddGameMemberSchema, UpdateGameMemberRoleSchema, AddGameDashboardUserSchema, VerifyGameDashboardLoginSchema, AcceptGameDashboardInviteSchema, AcceptGameDashboardResetSchema, ALLOWED_UPLOAD_EXTENSIONS, InitiateUploadSchema, AddCustomHostnameSchema, SetSecretsRequestSchema, SecretsDiffRequestSchema, SeedRequestSchema, SchemaInfoSchema, VerifyTokenSchema, KVSeedRequestSchema, DeployMigrationSchema, DeployDatabaseSchema, DatabaseResetDatabaseSchema, DatabaseResetRequestSchema, BaselineEvidenceSchema, DeployBaselineSchema, DeploySourceSchema, DeploymentStateBaselineSchema, MigrationRealignSchema, MigrationResolveSchema, DatabaseRestoreSchema, DeployBlockedReportSchema, DeployRequestSchema;
|
|
47593
47694
|
var init_schemas2 = __esm(() => {
|
|
47594
47695
|
init_drizzle_zod();
|
|
47595
47696
|
init_esm();
|
|
@@ -47835,6 +47936,13 @@ var init_schemas2 = __esm(() => {
|
|
|
47835
47936
|
message: "A baseline must claim something — migration history, a schema snapshot, or artifact hashes",
|
|
47836
47937
|
path: ["lastAppliedMigrationTag"]
|
|
47837
47938
|
});
|
|
47939
|
+
DeploySourceSchema = exports_external.object({
|
|
47940
|
+
repository: exports_external.string().url().nullable().transform((value) => value === null ? null : publishableRepositoryUrl(value)),
|
|
47941
|
+
commit: exports_external.string().regex(/^[0-9a-f]{40}$/, "commit must be a full 40-character lowercase SHA").nullable(),
|
|
47942
|
+
branch: exports_external.string().min(1).nullable(),
|
|
47943
|
+
dirty: exports_external.boolean(),
|
|
47944
|
+
externalBuild: exports_external.boolean().default(false)
|
|
47945
|
+
});
|
|
47838
47946
|
DeploymentStateBaselineSchema = exports_external.object({
|
|
47839
47947
|
lastAppliedMigrationTag: exports_external.string().min(1).optional(),
|
|
47840
47948
|
journal: exports_external.array(exports_external.object({
|
|
@@ -47906,6 +48014,7 @@ var init_schemas2 = __esm(() => {
|
|
|
47906
48014
|
buildHash: exports_external.string().min(1).optional(),
|
|
47907
48015
|
integrationsHash: exports_external.string().min(1).optional(),
|
|
47908
48016
|
pruneSecrets: exports_external.array(exports_external.string().min(1)).optional(),
|
|
48017
|
+
source: DeploySourceSchema.optional(),
|
|
47909
48018
|
metadata: exports_external.object({
|
|
47910
48019
|
displayName: exports_external.string().optional(),
|
|
47911
48020
|
description: exports_external.string().optional(),
|
|
@@ -109824,6 +109933,7 @@ function createPlatformServices(deps) {
|
|
|
109824
109933
|
const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109825
109934
|
const secrets = new SecretsService({ db: db2, config: config5, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109826
109935
|
const domain3 = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
|
|
109936
|
+
const releases = new ReleasesService({ db: db2 });
|
|
109827
109937
|
const deploymentState = new DeploymentStateService({
|
|
109828
109938
|
db: db2,
|
|
109829
109939
|
config: config5,
|
|
@@ -109883,6 +109993,7 @@ function createPlatformServices(deps) {
|
|
|
109883
109993
|
kv,
|
|
109884
109994
|
secrets,
|
|
109885
109995
|
domain: domain3,
|
|
109996
|
+
releases,
|
|
109886
109997
|
deploymentState,
|
|
109887
109998
|
database,
|
|
109888
109999
|
seed,
|
|
@@ -109901,6 +110012,7 @@ var init_platform2 = __esm(async () => {
|
|
|
109901
110012
|
init_deployment_state_service();
|
|
109902
110013
|
init_domain_service();
|
|
109903
110014
|
init_kv_service();
|
|
110015
|
+
init_releases_service();
|
|
109904
110016
|
init_secrets_service();
|
|
109905
110017
|
init_seed_service();
|
|
109906
110018
|
init_upload_service();
|
|
@@ -167632,6 +167744,47 @@ var init_lti_controller = __esm(() => {
|
|
|
167632
167744
|
});
|
|
167633
167745
|
});
|
|
167634
167746
|
|
|
167747
|
+
// ../api-core/src/controllers/releases.controller.ts
|
|
167748
|
+
function requireNoQuery(url4) {
|
|
167749
|
+
if (url4.search !== "") {
|
|
167750
|
+
throw ApiError.badRequest("This endpoint takes no query parameters");
|
|
167751
|
+
}
|
|
167752
|
+
}
|
|
167753
|
+
function parseHistoryParam(url4) {
|
|
167754
|
+
if (url4.search === "") {
|
|
167755
|
+
return;
|
|
167756
|
+
}
|
|
167757
|
+
const match3 = CANONICAL_HISTORY.exec(url4.search);
|
|
167758
|
+
if (!match3) {
|
|
167759
|
+
throw ApiError.badRequest(`The only supported query is ?history=N with N an integer between 1 and ${MAX_HISTORY}`);
|
|
167760
|
+
}
|
|
167761
|
+
return Number(match3[1]);
|
|
167762
|
+
}
|
|
167763
|
+
function cached3(body2) {
|
|
167764
|
+
return Response.json(body2, {
|
|
167765
|
+
headers: {
|
|
167766
|
+
"Content-Type": "application/json",
|
|
167767
|
+
"Cache-Control": CACHE_CONTROL2
|
|
167768
|
+
}
|
|
167769
|
+
});
|
|
167770
|
+
}
|
|
167771
|
+
async function list3(ctx) {
|
|
167772
|
+
requireNoQuery(ctx.url);
|
|
167773
|
+
return cached3(await ctx.services.releases.list());
|
|
167774
|
+
}
|
|
167775
|
+
async function get2(ctx) {
|
|
167776
|
+
const slug2 = requireSlug(ctx.params.slug);
|
|
167777
|
+
const history2 = parseHistoryParam(ctx.url);
|
|
167778
|
+
return cached3(await ctx.services.releases.get(slug2, { history: history2 }));
|
|
167779
|
+
}
|
|
167780
|
+
var CACHE_CONTROL2 = "public, max-age=60", MAX_HISTORY = 100, CANONICAL_HISTORY, releases;
|
|
167781
|
+
var init_releases_controller = __esm(() => {
|
|
167782
|
+
init_errors2();
|
|
167783
|
+
init_utils7();
|
|
167784
|
+
CANONICAL_HISTORY = /^\?history=([1-9]\d?|100)$/;
|
|
167785
|
+
releases = defineControllerNames("releases", { list: list3, get: get2 });
|
|
167786
|
+
});
|
|
167787
|
+
|
|
167635
167788
|
// ../api-core/src/controllers/secrets.controller.ts
|
|
167636
167789
|
var listKeys2, setSecrets, deleteSecret, diff, secrets;
|
|
167637
167790
|
var init_secrets_controller = __esm(() => {
|
|
@@ -168794,6 +168947,7 @@ var init_controllers = __esm(() => {
|
|
|
168794
168947
|
init_leaderboard_controller();
|
|
168795
168948
|
init_logs_controller();
|
|
168796
168949
|
init_lti_controller();
|
|
168950
|
+
init_releases_controller();
|
|
168797
168951
|
init_secrets_controller();
|
|
168798
168952
|
init_seed_controller();
|
|
168799
168953
|
init_session_controller();
|
|
@@ -169245,7 +169399,8 @@ var init_deploy = __esm(async () => {
|
|
|
169245
169399
|
config: body2.config,
|
|
169246
169400
|
bindings: body2.bindings,
|
|
169247
169401
|
schema: body2.schema,
|
|
169248
|
-
metadata: body2.metadata
|
|
169402
|
+
metadata: body2.metadata,
|
|
169403
|
+
source: body2.source
|
|
169249
169404
|
},
|
|
169250
169405
|
events,
|
|
169251
169406
|
result: {
|