@playcademy/sandbox 0.10.1-beta.7 → 0.10.1
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 +160 -20
- package/dist/server.js +160 -20
- 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
|
|
1131
|
+
version: "0.10.1",
|
|
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;
|
|
@@ -47837,13 +47938,7 @@ var init_schemas2 = __esm(() => {
|
|
|
47837
47938
|
path: ["lastAppliedMigrationTag"]
|
|
47838
47939
|
});
|
|
47839
47940
|
DeploySourceSchema = exports_external.object({
|
|
47840
|
-
repository: exports_external.string().url().nullable().transform((value) =>
|
|
47841
|
-
if (value === null) {
|
|
47842
|
-
return null;
|
|
47843
|
-
}
|
|
47844
|
-
const url = new URL(value);
|
|
47845
|
-
return url.username || url.password || url.search || url.hash ? null : value;
|
|
47846
|
-
}),
|
|
47941
|
+
repository: exports_external.string().url().nullable().transform((value) => value === null ? null : publishableRepositoryUrl(value)),
|
|
47847
47942
|
commit: exports_external.string().regex(/^[0-9a-f]{40}$/, "commit must be a full 40-character lowercase SHA").nullable(),
|
|
47848
47943
|
branch: exports_external.string().min(1).nullable(),
|
|
47849
47944
|
dirty: exports_external.boolean(),
|
|
@@ -109839,6 +109934,7 @@ function createPlatformServices(deps) {
|
|
|
109839
109934
|
const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109840
109935
|
const secrets = new SecretsService({ db: db2, config: config5, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109841
109936
|
const domain3 = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
|
|
109937
|
+
const releases = new ReleasesService({ db: db2 });
|
|
109842
109938
|
const deploymentState = new DeploymentStateService({
|
|
109843
109939
|
db: db2,
|
|
109844
109940
|
config: config5,
|
|
@@ -109898,6 +109994,7 @@ function createPlatformServices(deps) {
|
|
|
109898
109994
|
kv,
|
|
109899
109995
|
secrets,
|
|
109900
109996
|
domain: domain3,
|
|
109997
|
+
releases,
|
|
109901
109998
|
deploymentState,
|
|
109902
109999
|
database,
|
|
109903
110000
|
seed,
|
|
@@ -109916,6 +110013,7 @@ var init_platform2 = __esm(async () => {
|
|
|
109916
110013
|
init_deployment_state_service();
|
|
109917
110014
|
init_domain_service();
|
|
109918
110015
|
init_kv_service();
|
|
110016
|
+
init_releases_service();
|
|
109919
110017
|
init_secrets_service();
|
|
109920
110018
|
init_seed_service();
|
|
109921
110019
|
init_upload_service();
|
|
@@ -167647,6 +167745,47 @@ var init_lti_controller = __esm(() => {
|
|
|
167647
167745
|
});
|
|
167648
167746
|
});
|
|
167649
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
|
+
|
|
167650
167789
|
// ../api-core/src/controllers/secrets.controller.ts
|
|
167651
167790
|
var listKeys2, setSecrets, deleteSecret, diff, secrets;
|
|
167652
167791
|
var init_secrets_controller = __esm(() => {
|
|
@@ -168809,6 +168948,7 @@ var init_controllers = __esm(() => {
|
|
|
168809
168948
|
init_leaderboard_controller();
|
|
168810
168949
|
init_logs_controller();
|
|
168811
168950
|
init_lti_controller();
|
|
168951
|
+
init_releases_controller();
|
|
168812
168952
|
init_secrets_controller();
|
|
168813
168953
|
init_seed_controller();
|
|
168814
168954
|
init_session_controller();
|
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
|
|
1130
|
+
version: "0.10.1",
|
|
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;
|
|
@@ -47836,13 +47937,7 @@ var init_schemas2 = __esm(() => {
|
|
|
47836
47937
|
path: ["lastAppliedMigrationTag"]
|
|
47837
47938
|
});
|
|
47838
47939
|
DeploySourceSchema = exports_external.object({
|
|
47839
|
-
repository: exports_external.string().url().nullable().transform((value) =>
|
|
47840
|
-
if (value === null) {
|
|
47841
|
-
return null;
|
|
47842
|
-
}
|
|
47843
|
-
const url = new URL(value);
|
|
47844
|
-
return url.username || url.password || url.search || url.hash ? null : value;
|
|
47845
|
-
}),
|
|
47940
|
+
repository: exports_external.string().url().nullable().transform((value) => value === null ? null : publishableRepositoryUrl(value)),
|
|
47846
47941
|
commit: exports_external.string().regex(/^[0-9a-f]{40}$/, "commit must be a full 40-character lowercase SHA").nullable(),
|
|
47847
47942
|
branch: exports_external.string().min(1).nullable(),
|
|
47848
47943
|
dirty: exports_external.boolean(),
|
|
@@ -109838,6 +109933,7 @@ function createPlatformServices(deps) {
|
|
|
109838
109933
|
const kv = new KVService({ db: db2, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109839
109934
|
const secrets = new SecretsService({ db: db2, config: config5, cloudflare: cloudflare2, validateDeveloperAccessBySlug });
|
|
109840
109935
|
const domain3 = new DomainService({ db: db2, cloudflare: cloudflare2, corsKvs, validateDeveloperAccessBySlug });
|
|
109936
|
+
const releases = new ReleasesService({ db: db2 });
|
|
109841
109937
|
const deploymentState = new DeploymentStateService({
|
|
109842
109938
|
db: db2,
|
|
109843
109939
|
config: config5,
|
|
@@ -109897,6 +109993,7 @@ function createPlatformServices(deps) {
|
|
|
109897
109993
|
kv,
|
|
109898
109994
|
secrets,
|
|
109899
109995
|
domain: domain3,
|
|
109996
|
+
releases,
|
|
109900
109997
|
deploymentState,
|
|
109901
109998
|
database,
|
|
109902
109999
|
seed,
|
|
@@ -109915,6 +110012,7 @@ var init_platform2 = __esm(async () => {
|
|
|
109915
110012
|
init_deployment_state_service();
|
|
109916
110013
|
init_domain_service();
|
|
109917
110014
|
init_kv_service();
|
|
110015
|
+
init_releases_service();
|
|
109918
110016
|
init_secrets_service();
|
|
109919
110017
|
init_seed_service();
|
|
109920
110018
|
init_upload_service();
|
|
@@ -167646,6 +167744,47 @@ var init_lti_controller = __esm(() => {
|
|
|
167646
167744
|
});
|
|
167647
167745
|
});
|
|
167648
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
|
+
|
|
167649
167788
|
// ../api-core/src/controllers/secrets.controller.ts
|
|
167650
167789
|
var listKeys2, setSecrets, deleteSecret, diff, secrets;
|
|
167651
167790
|
var init_secrets_controller = __esm(() => {
|
|
@@ -168808,6 +168947,7 @@ var init_controllers = __esm(() => {
|
|
|
168808
168947
|
init_leaderboard_controller();
|
|
168809
168948
|
init_logs_controller();
|
|
168810
168949
|
init_lti_controller();
|
|
168950
|
+
init_releases_controller();
|
|
168811
168951
|
init_secrets_controller();
|
|
168812
168952
|
init_seed_controller();
|
|
168813
168953
|
init_session_controller();
|