@mastra/factory 0.21.1-alpha.3 → 0.22.0-alpha.4
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/capabilities/version-control.d.ts +13 -8
- package/dist/capabilities/version-control.d.ts.map +1 -1
- package/dist/environment/build-runner.d.ts +72 -0
- package/dist/environment/build-runner.d.ts.map +1 -0
- package/dist/environment/build-runner.js +131 -0
- package/dist/environment/build-runner.js.map +1 -0
- package/dist/environment/build-schedule.d.ts +40 -0
- package/dist/environment/build-schedule.d.ts.map +1 -0
- package/dist/environment/build-schedule.js +94 -0
- package/dist/environment/build-schedule.js.map +1 -0
- package/dist/environment/build-workflow.d.ts +62 -0
- package/dist/environment/build-workflow.d.ts.map +1 -0
- package/dist/environment/build-workflow.js +149 -0
- package/dist/environment/build-workflow.js.map +1 -0
- package/dist/environment/build.d.ts +111 -0
- package/dist/environment/build.d.ts.map +1 -0
- package/dist/environment/build.js +344 -0
- package/dist/environment/build.js.map +1 -0
- package/dist/environment/heads.d.ts +36 -0
- package/dist/environment/heads.d.ts.map +1 -0
- package/dist/environment/heads.js +60 -0
- package/dist/environment/heads.js.map +1 -0
- package/dist/factory.d.ts +18 -4
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +89 -15
- package/dist/factory.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/integrations/base.d.ts +17 -2
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/commits.d.ts +11 -0
- package/dist/integrations/github/commits.d.ts.map +1 -1
- package/dist/integrations/github/commits.js +20 -1
- package/dist/integrations/github/commits.js.map +1 -1
- package/dist/integrations/github/config.d.ts +3 -3
- package/dist/integrations/github/config.d.ts.map +1 -1
- package/dist/integrations/github/config.js +1 -1
- package/dist/integrations/github/config.js.map +1 -1
- package/dist/integrations/github/routes.d.ts +2 -2
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +6 -5
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.d.ts +3 -1
- package/dist/integrations/github/rules.d.ts.map +1 -1
- package/dist/integrations/github/rules.js +31 -1
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +35 -0
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +203 -2
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +117 -62
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/github/token-refresh.d.ts +7 -0
- package/dist/integrations/github/token-refresh.d.ts.map +1 -1
- package/dist/integrations/github/token-refresh.js.map +1 -1
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/gitlab/agent-tools.js +1 -1
- package/dist/integrations/gitlab/routes.d.ts +2 -2
- package/dist/integrations/gitlab/routes.d.ts.map +1 -1
- package/dist/integrations/gitlab/routes.js.map +1 -1
- package/dist/integrations/gitlab/session-subscriptions.d.ts.map +1 -1
- package/dist/integrations/gitlab/session-subscriptions.js +8 -7
- package/dist/integrations/gitlab/session-subscriptions.js.map +1 -1
- package/dist/integrations/incidentio/agent-tools.js +1 -1
- package/dist/integrations/jira/agent-tools.js +1 -1
- package/dist/integrations/linear/agent-tools.js +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +46 -2
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/jira/agent-tools.js +1 -1
- package/dist/integrations/slack/slack.d.ts.map +1 -1
- package/dist/integrations/slack/slack.js +15 -16
- package/dist/integrations/slack/slack.js.map +1 -1
- package/dist/integrations/subscription-session.js +1 -1
- package/dist/packages/_internals/workspace/dist/index.js +21 -1
- package/dist/packages/_internals/workspace/dist/index.js.map +1 -1
- package/dist/routes/contracts.d.ts +386 -2
- package/dist/routes/contracts.d.ts.map +1 -1
- package/dist/routes/contracts.js +166 -1
- package/dist/routes/contracts.js.map +1 -1
- package/dist/routes/fs.d.ts +25 -0
- package/dist/routes/fs.d.ts.map +1 -1
- package/dist/routes/fs.js +142 -46
- package/dist/routes/fs.js.map +1 -1
- package/dist/routes/projects.d.ts +6 -0
- package/dist/routes/projects.d.ts.map +1 -1
- package/dist/routes/projects.js +344 -1
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/source-control-sessions.d.ts +2 -0
- package/dist/routes/source-control-sessions.d.ts.map +1 -1
- package/dist/routes/source-control-sessions.js +36 -1
- package/dist/routes/source-control-sessions.js.map +1 -1
- package/dist/routes/supervisor.js +1 -1
- package/dist/routes/surface.d.ts +6 -3
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +21 -34
- package/dist/routes/surface.js.map +1 -1
- package/dist/rules/binding-context.d.ts +1 -1
- package/dist/rules/binding-context.d.ts.map +1 -1
- package/dist/rules/binding-context.js +1 -1
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/review-source-tool.js +1 -1
- package/dist/rules/start-coordinator.d.ts.map +1 -1
- package/dist/rules/start-coordinator.js +7 -2
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.js +1 -1
- package/dist/sandbox/callback-factory-sandbox.d.ts +15 -0
- package/dist/sandbox/callback-factory-sandbox.d.ts.map +1 -0
- package/dist/sandbox/callback-factory-sandbox.js +22 -0
- package/dist/sandbox/callback-factory-sandbox.js.map +1 -0
- package/dist/sandbox/local-factory-sandbox.d.ts +20 -0
- package/dist/sandbox/local-factory-sandbox.d.ts.map +1 -0
- package/dist/sandbox/local-factory-sandbox.js +27 -0
- package/dist/sandbox/local-factory-sandbox.js.map +1 -0
- package/dist/sandbox/session-retirement.d.ts.map +1 -1
- package/dist/sandbox/session-retirement.js +23 -3
- package/dist/sandbox/session-retirement.js.map +1 -1
- package/dist/sandbox/session-sandbox.d.ts +82 -40
- package/dist/sandbox/session-sandbox.d.ts.map +1 -1
- package/dist/sandbox/session-sandbox.js +130 -13
- package/dist/sandbox/session-sandbox.js.map +1 -1
- package/dist/sandbox/workdir.d.ts +10 -0
- package/dist/sandbox/workdir.d.ts.map +1 -1
- package/dist/sandbox/workdir.js +10 -1
- package/dist/sandbox/workdir.js.map +1 -1
- package/dist/session/environment-repositories.d.ts +31 -0
- package/dist/session/environment-repositories.d.ts.map +1 -0
- package/dist/session/environment-repositories.js +89 -0
- package/dist/session/environment-repositories.js.map +1 -0
- package/dist/session/environment-state-processor.d.ts +59 -0
- package/dist/session/environment-state-processor.d.ts.map +1 -0
- package/dist/session/environment-state-processor.js +105 -0
- package/dist/session/environment-state-processor.js.map +1 -0
- package/dist/session/factory-session.d.ts +26 -35
- package/dist/session/factory-session.d.ts.map +1 -1
- package/dist/session/factory-session.js +34 -71
- package/dist/session/factory-session.js.map +1 -1
- package/dist/session/filesystem-capture.d.ts +13 -0
- package/dist/session/filesystem-capture.d.ts.map +1 -1
- package/dist/session/filesystem-capture.js +73 -14
- package/dist/session/filesystem-capture.js.map +1 -1
- package/dist/session/source-control-tools.d.ts +1 -1
- package/dist/session/source-control-tools.d.ts.map +1 -1
- package/dist/session/source-control-tools.js +144 -71
- package/dist/session/source-control-tools.js.map +1 -1
- package/dist/spa-static.js +2 -2
- package/dist/storage/domains/audit/agent-audit.d.ts +6 -1
- package/dist/storage/domains/audit/agent-audit.d.ts.map +1 -1
- package/dist/storage/domains/audit/agent-audit.js +31 -13
- package/dist/storage/domains/audit/agent-audit.js.map +1 -1
- package/dist/storage/domains/projects/base.d.ts +115 -2
- package/dist/storage/domains/projects/base.d.ts.map +1 -1
- package/dist/storage/domains/projects/base.js +219 -12
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +123 -7
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +406 -33
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +19 -6
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +90 -2
- package/dist/storage/domains/source-control/inmemory.js.map +1 -1
- package/dist/storage/domains/work-items/base.d.ts.map +1 -1
- package/dist/storage/domains/work-items/base.js +12 -5
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/supervisor/read-tools.js +1 -1
- package/dist/supervisor/write-tools.js +1 -1
- package/dist/workspace.d.ts +50 -7
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +344 -24
- package/dist/workspace.js.map +1 -1
- package/package.json +7 -7
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FACTORY_PROJECTS_SCHEMA } from "../projects/base.js";
|
|
1
2
|
import { FactoryStorageDomain, UniqueViolationError } from "@mastra/core/storage";
|
|
2
3
|
//#region src/storage/domains/source-control/base.ts
|
|
3
4
|
const FACTORY_PROJECTS = "factory_projects";
|
|
@@ -6,6 +7,20 @@ const REPOSITORIES = "source_control_repositories";
|
|
|
6
7
|
const CONNECTIONS = "factory_project_source_control_connections";
|
|
7
8
|
const PROJECT_REPOSITORIES = "factory_project_repositories";
|
|
8
9
|
const SESSIONS = "source_control_sessions";
|
|
10
|
+
const SESSION_REPOSITORIES = "source_control_session_repositories";
|
|
11
|
+
/**
|
|
12
|
+
* The user already holds the branch on this link in a session without this
|
|
13
|
+
* factory's ownership, left by the factory migration. It is never reused and
|
|
14
|
+
* cannot be replaced while it holds the branch.
|
|
15
|
+
*/
|
|
16
|
+
var SessionOwnershipMismatchError = class extends Error {
|
|
17
|
+
sessionId;
|
|
18
|
+
constructor(sessionId) {
|
|
19
|
+
super(`Session ${sessionId} already holds this branch but does not belong to this factory.`);
|
|
20
|
+
this.sessionId = sessionId;
|
|
21
|
+
this.name = "SessionOwnershipMismatchError";
|
|
22
|
+
}
|
|
23
|
+
};
|
|
9
24
|
var SourceControlConnectionNotFoundError = class extends Error {
|
|
10
25
|
constructor() {
|
|
11
26
|
super("Project source-control connection not found for this organization and integration.");
|
|
@@ -109,6 +124,26 @@ const SOURCE_CONTROL_SCHEMAS = [
|
|
|
109
124
|
type: "text",
|
|
110
125
|
nullable: true
|
|
111
126
|
},
|
|
127
|
+
position: {
|
|
128
|
+
type: "integer",
|
|
129
|
+
default: 0
|
|
130
|
+
},
|
|
131
|
+
in_environment: {
|
|
132
|
+
type: "boolean",
|
|
133
|
+
default: true
|
|
134
|
+
},
|
|
135
|
+
last_build_status: {
|
|
136
|
+
type: "text",
|
|
137
|
+
default: "unbuilt"
|
|
138
|
+
},
|
|
139
|
+
last_build_error: {
|
|
140
|
+
type: "text",
|
|
141
|
+
nullable: true
|
|
142
|
+
},
|
|
143
|
+
last_built_at: {
|
|
144
|
+
type: "timestamp",
|
|
145
|
+
nullable: true
|
|
146
|
+
},
|
|
112
147
|
created_at: { type: "timestamp" },
|
|
113
148
|
updated_at: { type: "timestamp" }
|
|
114
149
|
},
|
|
@@ -127,6 +162,10 @@ const SOURCE_CONTROL_SCHEMAS = [
|
|
|
127
162
|
id: { type: "uuid-pk" },
|
|
128
163
|
session_id: { type: "text" },
|
|
129
164
|
project_repository_id: { type: "text" },
|
|
165
|
+
factory_project_id: {
|
|
166
|
+
type: "text",
|
|
167
|
+
nullable: true
|
|
168
|
+
},
|
|
130
169
|
org_id: { type: "text" },
|
|
131
170
|
user_id: { type: "text" },
|
|
132
171
|
branch: { type: "text" },
|
|
@@ -162,16 +201,59 @@ const SOURCE_CONTROL_SCHEMAS = [
|
|
|
162
201
|
created_at: { type: "timestamp" },
|
|
163
202
|
updated_at: { type: "timestamp" }
|
|
164
203
|
},
|
|
204
|
+
uniqueIndexes: [
|
|
205
|
+
{
|
|
206
|
+
name: "source_control_sessions_session_id_unique",
|
|
207
|
+
columns: ["session_id"]
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "source_control_sessions_repository_user_branch_unique",
|
|
211
|
+
columns: [
|
|
212
|
+
"project_repository_id",
|
|
213
|
+
"user_id",
|
|
214
|
+
"branch"
|
|
215
|
+
]
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: "source_control_sessions_project_user_branch_unique",
|
|
219
|
+
columns: [
|
|
220
|
+
"factory_project_id",
|
|
221
|
+
"user_id",
|
|
222
|
+
"branch"
|
|
223
|
+
]
|
|
224
|
+
}
|
|
225
|
+
],
|
|
226
|
+
indexes: [{
|
|
227
|
+
name: "source_control_sessions_project_idx",
|
|
228
|
+
columns: ["factory_project_id"]
|
|
229
|
+
}]
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
name: SESSION_REPOSITORIES,
|
|
233
|
+
columns: {
|
|
234
|
+
id: { type: "uuid-pk" },
|
|
235
|
+
session_id: { type: "text" },
|
|
236
|
+
project_repository_id: { type: "text" },
|
|
237
|
+
branch: { type: "text" },
|
|
238
|
+
change_request_id: {
|
|
239
|
+
type: "text",
|
|
240
|
+
nullable: true
|
|
241
|
+
},
|
|
242
|
+
change_request_url: {
|
|
243
|
+
type: "text",
|
|
244
|
+
nullable: true
|
|
245
|
+
},
|
|
246
|
+
pushed_at: { type: "timestamp" },
|
|
247
|
+
created_at: { type: "timestamp" },
|
|
248
|
+
updated_at: { type: "timestamp" }
|
|
249
|
+
},
|
|
165
250
|
uniqueIndexes: [{
|
|
166
|
-
name: "
|
|
251
|
+
name: "source_control_session_repositories_session_link_unique",
|
|
252
|
+
columns: ["session_id", "project_repository_id"]
|
|
253
|
+
}],
|
|
254
|
+
indexes: [{
|
|
255
|
+
name: "source_control_session_repositories_session_idx",
|
|
167
256
|
columns: ["session_id"]
|
|
168
|
-
}, {
|
|
169
|
-
name: "source_control_sessions_repository_user_branch_unique",
|
|
170
|
-
columns: [
|
|
171
|
-
"project_repository_id",
|
|
172
|
-
"user_id",
|
|
173
|
-
"branch"
|
|
174
|
-
]
|
|
175
257
|
}]
|
|
176
258
|
}
|
|
177
259
|
];
|
|
@@ -221,6 +303,31 @@ function toProjectRepository(row) {
|
|
|
221
303
|
sandboxWorkdir: row.sandbox_workdir,
|
|
222
304
|
setupCommand: row.setup_command,
|
|
223
305
|
teardownCommand: row.teardown_command,
|
|
306
|
+
position: row.position ?? 0,
|
|
307
|
+
inEnvironment: row.in_environment ?? true,
|
|
308
|
+
lastBuildStatus: toBuildStatus(row.last_build_status),
|
|
309
|
+
lastBuildError: row.last_build_error,
|
|
310
|
+
lastBuiltAt: row.last_built_at,
|
|
311
|
+
createdAt: row.created_at,
|
|
312
|
+
updatedAt: row.updated_at
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
function toBuildStatus(value) {
|
|
316
|
+
return value === "configured" || value === "failed" ? value : "unbuilt";
|
|
317
|
+
}
|
|
318
|
+
/** Oldest first; ties (same millisecond) break on id so re-runs see one order. */
|
|
319
|
+
function byCreatedAt(a, b) {
|
|
320
|
+
return a.created_at.getTime() - b.created_at.getTime() || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
321
|
+
}
|
|
322
|
+
function toSessionRepository(row) {
|
|
323
|
+
return {
|
|
324
|
+
id: row.id,
|
|
325
|
+
sessionId: row.session_id,
|
|
326
|
+
projectRepositoryId: row.project_repository_id,
|
|
327
|
+
branch: row.branch,
|
|
328
|
+
changeRequestId: row.change_request_id,
|
|
329
|
+
changeRequestUrl: row.change_request_url,
|
|
330
|
+
pushedAt: row.pushed_at,
|
|
224
331
|
createdAt: row.created_at,
|
|
225
332
|
updatedAt: row.updated_at
|
|
226
333
|
};
|
|
@@ -230,6 +337,7 @@ function toSession(row) {
|
|
|
230
337
|
id: row.id,
|
|
231
338
|
sessionId: row.session_id,
|
|
232
339
|
projectRepositoryId: row.project_repository_id,
|
|
340
|
+
factoryProjectId: row.factory_project_id ?? null,
|
|
233
341
|
orgId: row.org_id,
|
|
234
342
|
userId: row.user_id,
|
|
235
343
|
branch: row.branch,
|
|
@@ -245,14 +353,162 @@ function toSession(row) {
|
|
|
245
353
|
updatedAt: row.updated_at
|
|
246
354
|
};
|
|
247
355
|
}
|
|
356
|
+
function projectRepositoryPatch(input) {
|
|
357
|
+
const patch = { updated_at: /* @__PURE__ */ new Date() };
|
|
358
|
+
if (input.branch !== void 0) patch.branch = input.branch;
|
|
359
|
+
if (input.sandboxProvider !== void 0) patch.sandbox_provider = input.sandboxProvider;
|
|
360
|
+
if (input.sandboxWorkdir !== void 0) patch.sandbox_workdir = input.sandboxWorkdir;
|
|
361
|
+
if (input.setupCommand !== void 0) patch.setup_command = input.setupCommand;
|
|
362
|
+
if (input.teardownCommand !== void 0) patch.teardown_command = input.teardownCommand;
|
|
363
|
+
if (input.position !== void 0) patch.position = input.position;
|
|
364
|
+
if (input.inEnvironment !== void 0) patch.in_environment = input.inEnvironment;
|
|
365
|
+
return patch;
|
|
366
|
+
}
|
|
248
367
|
var SourceControlStorage = class extends FactoryStorageDomain {
|
|
249
368
|
constructor() {
|
|
250
369
|
super("source-control");
|
|
251
370
|
}
|
|
252
371
|
async init() {
|
|
253
372
|
await this.ensureCollections(SOURCE_CONTROL_SCHEMAS);
|
|
373
|
+
await this.#backfillEnvironment();
|
|
374
|
+
await this.#backfillSessionFactory();
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Fill `factory_project_id` on sessions that predate the column, walking
|
|
378
|
+
* link → connection. The old constraint was per link, so two links of one
|
|
379
|
+
* factory may hold the same user and branch; the new per-factory index
|
|
380
|
+
* would reject the second write and a throwing `init()` only leaves the
|
|
381
|
+
* domain not-ready. Within such a group the oldest session wins and the
|
|
382
|
+
* others keep a null factory. Idempotent, O(rows), deletes nothing.
|
|
383
|
+
*/
|
|
384
|
+
async #backfillSessionFactory() {
|
|
385
|
+
let unresolved = 0;
|
|
386
|
+
let collisions = 0;
|
|
387
|
+
try {
|
|
388
|
+
const pending = await this.ops.findMany(SESSIONS, { factory_project_id: null });
|
|
389
|
+
if (pending.length === 0) return;
|
|
390
|
+
const connections = await this.ops.findMany(CONNECTIONS, {});
|
|
391
|
+
const links = await this.ops.findMany(PROJECT_REPOSITORIES, {});
|
|
392
|
+
const projectByConnectionId = new Map(connections.map((row) => [row.id, row.factory_project_id]));
|
|
393
|
+
const projectByLinkId = new Map(links.flatMap((link) => {
|
|
394
|
+
const projectId = projectByConnectionId.get(link.connection_id);
|
|
395
|
+
return projectId ? [[link.id, projectId]] : [];
|
|
396
|
+
}));
|
|
397
|
+
const keyOf = (projectId, row) => `${projectId}\u0000${row.user_id}\u0000${row.branch}`;
|
|
398
|
+
const taken = /* @__PURE__ */ new Set();
|
|
399
|
+
for (const row of await this.ops.findMany(SESSIONS, {})) if (row.factory_project_id) taken.add(keyOf(row.factory_project_id, row));
|
|
400
|
+
const ordered = [...pending].sort((a, b) => a.created_at.getTime() - b.created_at.getTime() || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
401
|
+
for (const row of ordered) {
|
|
402
|
+
const projectId = projectByLinkId.get(row.project_repository_id);
|
|
403
|
+
if (!projectId) {
|
|
404
|
+
unresolved += 1;
|
|
405
|
+
continue;
|
|
406
|
+
}
|
|
407
|
+
const key = keyOf(projectId, row);
|
|
408
|
+
if (taken.has(key)) {
|
|
409
|
+
collisions += 1;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
try {
|
|
413
|
+
await this.ops.updateMany(SESSIONS, { id: row.id }, { factory_project_id: projectId });
|
|
414
|
+
taken.add(key);
|
|
415
|
+
} catch (error) {
|
|
416
|
+
if (!(error instanceof UniqueViolationError)) throw error;
|
|
417
|
+
collisions += 1;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
} catch (error) {
|
|
421
|
+
console.warn(`[factory] session backfill stopped early: ${error instanceof Error ? error.message : error}`);
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
if (unresolved > 0 || collisions > 0) console.warn(`[factory] session backfill: ${unresolved} sessions have no resolvable factory, ${collisions} sessions collide on (factory, user, branch) and keep a null factory`);
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* One-time fill of the environment columns from the rows that predate them.
|
|
428
|
+
* Idempotent and non-destructive: a project is touched only while its
|
|
429
|
+
* `environment_backfilled_at` is null, every other write lands only where the
|
|
430
|
+
* stored value is still the column default, and nothing is deleted. Runs
|
|
431
|
+
* on every start; reports what it could not resolve instead of throwing,
|
|
432
|
+
* because a throwing `init()` only leaves the domain not-ready.
|
|
433
|
+
*/
|
|
434
|
+
async #backfillEnvironment() {
|
|
435
|
+
await this.ensureCollections([FACTORY_PROJECTS_SCHEMA]);
|
|
436
|
+
let missingRepositories = 0;
|
|
437
|
+
const failedProjects = [];
|
|
438
|
+
try {
|
|
439
|
+
const projects = await this.ops.findMany(FACTORY_PROJECTS, { environment_backfilled_at: null });
|
|
440
|
+
if (projects.length === 0) return;
|
|
441
|
+
const connections = await this.ops.findMany(CONNECTIONS, {});
|
|
442
|
+
const links = await this.ops.findMany(PROJECT_REPOSITORIES, {});
|
|
443
|
+
const repositories = await this.ops.findMany(REPOSITORIES, {});
|
|
444
|
+
const slugByRepositoryId = new Map(repositories.map((row) => [row.id, row.slug]));
|
|
445
|
+
const linksByConnectionId = /* @__PURE__ */ new Map();
|
|
446
|
+
for (const link of links) {
|
|
447
|
+
const bucket = linksByConnectionId.get(link.connection_id) ?? [];
|
|
448
|
+
bucket.push(link);
|
|
449
|
+
linksByConnectionId.set(link.connection_id, bucket);
|
|
450
|
+
}
|
|
451
|
+
const connectionsByProjectId = /* @__PURE__ */ new Map();
|
|
452
|
+
for (const connection of connections) {
|
|
453
|
+
const bucket = connectionsByProjectId.get(connection.factory_project_id) ?? [];
|
|
454
|
+
bucket.push(connection);
|
|
455
|
+
connectionsByProjectId.set(connection.factory_project_id, bucket);
|
|
456
|
+
}
|
|
457
|
+
for (const project of projects) try {
|
|
458
|
+
const projectLinks = (connectionsByProjectId.get(project.id) ?? []).flatMap((connection) => linksByConnectionId.get(connection.id) ?? []).sort(byCreatedAt);
|
|
459
|
+
const slugOf = (link) => {
|
|
460
|
+
const slug = slugByRepositoryId.get(link.repository_id);
|
|
461
|
+
if (slug !== void 0) return slug;
|
|
462
|
+
missingRepositories += 1;
|
|
463
|
+
return `missing-repository:${link.id}`;
|
|
464
|
+
};
|
|
465
|
+
let nextPosition = Math.max(0, ...projectLinks.map((link) => link.position ?? 0));
|
|
466
|
+
const seenSlugs = new Set(projectLinks.filter((link) => (link.position ?? 0) > 0 && link.in_environment !== false).map(slugOf));
|
|
467
|
+
for (const link of projectLinks) {
|
|
468
|
+
if ((link.position ?? 0) > 0) continue;
|
|
469
|
+
nextPosition += 1;
|
|
470
|
+
const slug = slugOf(link);
|
|
471
|
+
const inEnvironment = link.in_environment !== false && !seenSlugs.has(slug);
|
|
472
|
+
if (inEnvironment) seenSlugs.add(slug);
|
|
473
|
+
await this.ops.updateMany(PROJECT_REPOSITORIES, { id: link.id }, {
|
|
474
|
+
position: nextPosition,
|
|
475
|
+
in_environment: inEnvironment
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
await this.ops.updateMany(FACTORY_PROJECTS, { id: project.id }, { environment_backfilled_at: /* @__PURE__ */ new Date() });
|
|
479
|
+
} catch {
|
|
480
|
+
failedProjects.push(project.id);
|
|
481
|
+
}
|
|
482
|
+
} catch (error) {
|
|
483
|
+
console.warn(`[factory] environment backfill did not run: ${error instanceof Error ? error.message : error}`);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (missingRepositories > 0 || failedProjects.length > 0) console.warn(`[factory] environment backfill: ${missingRepositories} links point at a missing repository row, ${failedProjects.length} projects could not be updated${failedProjects.length > 0 ? ` (${failedProjects.join(", ")})` : ""}`);
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Applies environment patches to a project's repository links through `ops`,
|
|
490
|
+
* normally a transaction that also holds the project row. Validates against
|
|
491
|
+
* the links as they are inside that transaction: every id must belong to the
|
|
492
|
+
* project, and a reorder must list every link so no two share a position.
|
|
493
|
+
* Nothing is written when validation fails.
|
|
494
|
+
*/
|
|
495
|
+
async updateEnvironmentRepositories(ops, args) {
|
|
496
|
+
const connections = args.integrationIds.length === 0 ? [] : await ops.findMany(CONNECTIONS, {
|
|
497
|
+
factory_project_id: args.factoryProjectId,
|
|
498
|
+
integration_id: { in: args.integrationIds }
|
|
499
|
+
});
|
|
500
|
+
const links = connections.length === 0 ? [] : await ops.findMany(PROJECT_REPOSITORIES, { connection_id: { in: connections.map((connection) => connection.id) } });
|
|
501
|
+
const linkIds = new Set(links.map((link) => link.id));
|
|
502
|
+
if (args.patches.some((patch) => !linkIds.has(patch.projectRepositoryId))) return "not_found";
|
|
503
|
+
if (args.patches.some((patch) => patch.position !== void 0) && args.patches.length !== linkIds.size) return "incomplete_reorder";
|
|
504
|
+
for (const { projectRepositoryId, ...input } of args.patches) {
|
|
505
|
+
if (Object.keys(input).length === 0) continue;
|
|
506
|
+
await ops.updateMany(PROJECT_REPOSITORIES, { id: projectRepositoryId }, projectRepositoryPatch(input));
|
|
507
|
+
}
|
|
508
|
+
return "updated";
|
|
254
509
|
}
|
|
255
510
|
async dangerouslyClearAll() {
|
|
511
|
+
await this.ops.deleteMany(SESSION_REPOSITORIES, {});
|
|
256
512
|
await this.ops.deleteMany(SESSIONS, {});
|
|
257
513
|
await this.ops.deleteMany(PROJECT_REPOSITORIES, {});
|
|
258
514
|
await this.ops.deleteMany(CONNECTIONS, {});
|
|
@@ -497,7 +753,11 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
497
753
|
id
|
|
498
754
|
})) return false;
|
|
499
755
|
const projectRepositories = await db().findMany(PROJECT_REPOSITORIES, { connection_id: id });
|
|
500
|
-
for (const projectRepository of projectRepositories)
|
|
756
|
+
for (const projectRepository of projectRepositories) {
|
|
757
|
+
for (const session of await db().findMany(SESSIONS, { project_repository_id: projectRepository.id })) await db().deleteMany(SESSION_REPOSITORIES, { session_id: session.session_id });
|
|
758
|
+
await db().deleteMany(SESSION_REPOSITORIES, { project_repository_id: projectRepository.id });
|
|
759
|
+
await db().deleteMany(SESSIONS, { project_repository_id: projectRepository.id });
|
|
760
|
+
}
|
|
501
761
|
await db().deleteMany(PROJECT_REPOSITORIES, { connection_id: id });
|
|
502
762
|
await db().deleteMany(CONNECTIONS, {
|
|
503
763
|
id,
|
|
@@ -568,16 +828,40 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
568
828
|
}
|
|
569
829
|
return targets;
|
|
570
830
|
},
|
|
831
|
+
listByProject: async ({ orgId, factoryProjectId }) => {
|
|
832
|
+
if (!await db().findOne(FACTORY_PROJECTS, {
|
|
833
|
+
id: factoryProjectId,
|
|
834
|
+
org_id: orgId
|
|
835
|
+
})) return [];
|
|
836
|
+
const connections = await db().findMany(CONNECTIONS, {
|
|
837
|
+
factory_project_id: factoryProjectId,
|
|
838
|
+
integration_id: integrationId
|
|
839
|
+
});
|
|
840
|
+
if (connections.length === 0) return [];
|
|
841
|
+
return (await db().findMany(PROJECT_REPOSITORIES, { connection_id: { in: connections.map((connection) => connection.id) } }, { orderBy: [["position", "asc"], ["created_at", "asc"]] })).map(toProjectRepository);
|
|
842
|
+
},
|
|
571
843
|
get: getProjectRepository,
|
|
572
844
|
link: async (input) => {
|
|
573
845
|
const connection = await requireConnection({
|
|
574
846
|
orgId: input.orgId,
|
|
575
847
|
id: input.connectionId
|
|
576
848
|
});
|
|
577
|
-
|
|
849
|
+
const repository = await requireRepository({
|
|
578
850
|
orgId: input.orgId,
|
|
579
851
|
id: input.repositoryId
|
|
580
|
-
})
|
|
852
|
+
});
|
|
853
|
+
if (repository.installationId !== connection.installationId) throw new Error("Repository does not belong to the connection installation.");
|
|
854
|
+
const siblingConnections = await db().findMany(CONNECTIONS, { factory_project_id: connection.factoryProjectId });
|
|
855
|
+
const siblings = await db().findMany(PROJECT_REPOSITORIES, { connection_id: { in: siblingConnections.map((sibling) => sibling.id) } });
|
|
856
|
+
const position = 1 + Math.max(0, ...siblings.map((sibling) => sibling.position ?? 0));
|
|
857
|
+
let inEnvironment = true;
|
|
858
|
+
for (const sibling of siblings) {
|
|
859
|
+
if (sibling.in_environment === false) continue;
|
|
860
|
+
if ((await db().findOne(REPOSITORIES, { id: sibling.repository_id }))?.slug === repository.slug) {
|
|
861
|
+
inEnvironment = false;
|
|
862
|
+
break;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
581
865
|
const now = /* @__PURE__ */ new Date();
|
|
582
866
|
try {
|
|
583
867
|
return toProjectRepository(await db().insertOne(PROJECT_REPOSITORIES, {
|
|
@@ -589,6 +873,11 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
589
873
|
sandbox_workdir: input.sandboxWorkdir,
|
|
590
874
|
setup_command: input.setupCommand ?? null,
|
|
591
875
|
teardown_command: input.teardownCommand ?? null,
|
|
876
|
+
position,
|
|
877
|
+
in_environment: inEnvironment,
|
|
878
|
+
last_build_status: "unbuilt",
|
|
879
|
+
last_build_error: null,
|
|
880
|
+
last_built_at: null,
|
|
592
881
|
created_at: now,
|
|
593
882
|
updated_at: now
|
|
594
883
|
}));
|
|
@@ -607,13 +896,23 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
607
896
|
orgId,
|
|
608
897
|
id
|
|
609
898
|
})) return null;
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
899
|
+
await db().updateMany(PROJECT_REPOSITORIES, { id }, projectRepositoryPatch(input));
|
|
900
|
+
return getProjectRepository({
|
|
901
|
+
orgId,
|
|
902
|
+
id
|
|
903
|
+
});
|
|
904
|
+
},
|
|
905
|
+
setBuildStatus: async ({ orgId, id, status, error, builtAt }) => {
|
|
906
|
+
if (!await getProjectRepository({
|
|
907
|
+
orgId,
|
|
908
|
+
id
|
|
909
|
+
})) return null;
|
|
910
|
+
await db().updateMany(PROJECT_REPOSITORIES, { id }, {
|
|
911
|
+
last_build_status: status,
|
|
912
|
+
last_build_error: error ?? null,
|
|
913
|
+
last_built_at: builtAt ?? null,
|
|
914
|
+
updated_at: /* @__PURE__ */ new Date()
|
|
915
|
+
});
|
|
617
916
|
return getProjectRepository({
|
|
618
917
|
orgId,
|
|
619
918
|
id
|
|
@@ -624,6 +923,8 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
624
923
|
orgId,
|
|
625
924
|
id
|
|
626
925
|
})) return false;
|
|
926
|
+
for (const session of await db().findMany(SESSIONS, { project_repository_id: id })) await db().deleteMany(SESSION_REPOSITORIES, { session_id: session.session_id });
|
|
927
|
+
await db().deleteMany(SESSION_REPOSITORIES, { project_repository_id: id });
|
|
627
928
|
await db().deleteMany(SESSIONS, { project_repository_id: id });
|
|
628
929
|
await db().deleteMany(PROJECT_REPOSITORIES, { id });
|
|
629
930
|
return true;
|
|
@@ -638,6 +939,19 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
638
939
|
if (!await getProjectRepositoryById(projectRepositoryId)) return [];
|
|
639
940
|
return (await db().findMany(SESSIONS, { project_repository_id: projectRepositoryId })).map(toSession);
|
|
640
941
|
},
|
|
942
|
+
listByProject: async ({ orgId, factoryProjectId, viewerUserId }) => {
|
|
943
|
+
const connections = await db().findMany(CONNECTIONS, {
|
|
944
|
+
factory_project_id: factoryProjectId,
|
|
945
|
+
integration_id: integrationId
|
|
946
|
+
});
|
|
947
|
+
if (connections.length === 0) return [];
|
|
948
|
+
const linkIds = /* @__PURE__ */ new Set();
|
|
949
|
+
for (const connection of connections) for (const link of await db().findMany(PROJECT_REPOSITORIES, { connection_id: connection.id })) linkIds.add(link.id);
|
|
950
|
+
return (await db().findMany(SESSIONS, {
|
|
951
|
+
factory_project_id: factoryProjectId,
|
|
952
|
+
org_id: orgId
|
|
953
|
+
})).filter((row) => linkIds.has(row.project_repository_id)).filter((row) => row.visibility !== "private" || row.user_id === viewerUserId).map(toSession);
|
|
954
|
+
},
|
|
641
955
|
getBySessionId: async (sessionId) => {
|
|
642
956
|
const row = await db().findOne(SESSIONS, { session_id: sessionId });
|
|
643
957
|
return row && await getProjectRepositoryById(row.project_repository_id) ? toSession(row) : null;
|
|
@@ -648,28 +962,49 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
648
962
|
updated_at: /* @__PURE__ */ new Date()
|
|
649
963
|
}));
|
|
650
964
|
},
|
|
651
|
-
getForBranch: async (
|
|
652
|
-
if (
|
|
965
|
+
getForBranch: async (args) => {
|
|
966
|
+
if ("factoryProjectId" in args) {
|
|
967
|
+
const row = await db().findOne(SESSIONS, {
|
|
968
|
+
factory_project_id: args.factoryProjectId,
|
|
969
|
+
user_id: args.userId,
|
|
970
|
+
branch: args.branch
|
|
971
|
+
});
|
|
972
|
+
return row && await getProjectRepositoryById(row.project_repository_id) ? toSession(row) : null;
|
|
973
|
+
}
|
|
974
|
+
if (!await getProjectRepositoryById(args.projectRepositoryId)) return null;
|
|
653
975
|
const row = await db().findOne(SESSIONS, {
|
|
654
|
-
project_repository_id: projectRepositoryId,
|
|
655
|
-
user_id: userId,
|
|
656
|
-
branch
|
|
976
|
+
project_repository_id: args.projectRepositoryId,
|
|
977
|
+
user_id: args.userId,
|
|
978
|
+
branch: args.branch
|
|
657
979
|
});
|
|
658
980
|
return row ? toSession(row) : null;
|
|
659
981
|
},
|
|
660
982
|
create: async (input) => {
|
|
661
|
-
await requireProjectRepositoryById(input.projectRepositoryId);
|
|
662
|
-
const
|
|
983
|
+
const link = await requireProjectRepositoryById(input.projectRepositoryId);
|
|
984
|
+
const connection = await getConnection({
|
|
985
|
+
orgId: input.orgId,
|
|
986
|
+
id: link.connectionId
|
|
987
|
+
});
|
|
988
|
+
if (!connection) throw new Error("Project repository not found for this integration.");
|
|
989
|
+
if (input.factoryProjectId !== void 0 && input.factoryProjectId !== connection.factoryProjectId) throw new Error("Session factory does not match the repository link.");
|
|
990
|
+
const factoryProjectId = connection.factoryProjectId;
|
|
991
|
+
const reuse = (row) => {
|
|
992
|
+
if (row.factory_project_id !== factoryProjectId) throw new SessionOwnershipMismatchError(row.session_id);
|
|
993
|
+
return toSession(row);
|
|
994
|
+
};
|
|
995
|
+
const findExisting = async () => db().findOne(SESSIONS, {
|
|
663
996
|
project_repository_id: input.projectRepositoryId,
|
|
664
997
|
user_id: input.userId,
|
|
665
998
|
branch: input.branch
|
|
666
999
|
});
|
|
667
|
-
|
|
1000
|
+
const existing = await findExisting();
|
|
1001
|
+
if (existing) return reuse(existing);
|
|
668
1002
|
const now = /* @__PURE__ */ new Date();
|
|
669
1003
|
try {
|
|
670
1004
|
return toSession(await db().insertOne(SESSIONS, {
|
|
671
1005
|
session_id: input.sessionId,
|
|
672
1006
|
project_repository_id: input.projectRepositoryId,
|
|
1007
|
+
factory_project_id: factoryProjectId,
|
|
673
1008
|
org_id: input.orgId,
|
|
674
1009
|
user_id: input.userId,
|
|
675
1010
|
branch: input.branch,
|
|
@@ -685,13 +1020,9 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
685
1020
|
}));
|
|
686
1021
|
} catch (error) {
|
|
687
1022
|
if (!(error instanceof UniqueViolationError)) throw error;
|
|
688
|
-
const row = await
|
|
689
|
-
project_repository_id: input.projectRepositoryId,
|
|
690
|
-
user_id: input.userId,
|
|
691
|
-
branch: input.branch
|
|
692
|
-
});
|
|
1023
|
+
const row = await findExisting();
|
|
693
1024
|
if (!row) throw error;
|
|
694
|
-
return
|
|
1025
|
+
return reuse(row);
|
|
695
1026
|
}
|
|
696
1027
|
},
|
|
697
1028
|
setSandbox: async ({ id, sandboxId, sandboxWorkdir }) => {
|
|
@@ -729,13 +1060,55 @@ var SourceControlStorage = class extends FactoryStorageDomain {
|
|
|
729
1060
|
});
|
|
730
1061
|
},
|
|
731
1062
|
delete: async (id) => {
|
|
1063
|
+
const row = await db().findOne(SESSIONS, { id });
|
|
1064
|
+
if (row) await db().deleteMany(SESSION_REPOSITORIES, { session_id: row.session_id });
|
|
732
1065
|
await db().deleteMany(SESSIONS, { id });
|
|
733
1066
|
}
|
|
1067
|
+
},
|
|
1068
|
+
sessionRepositories: {
|
|
1069
|
+
upsert: async (input) => {
|
|
1070
|
+
const key = {
|
|
1071
|
+
session_id: input.sessionId,
|
|
1072
|
+
project_repository_id: input.projectRepositoryId
|
|
1073
|
+
};
|
|
1074
|
+
const now = /* @__PURE__ */ new Date();
|
|
1075
|
+
const changes = { updated_at: now };
|
|
1076
|
+
if (input.branch !== void 0) {
|
|
1077
|
+
changes.branch = input.branch;
|
|
1078
|
+
changes.pushed_at = input.pushedAt ?? now;
|
|
1079
|
+
} else if (input.pushedAt) changes.pushed_at = input.pushedAt;
|
|
1080
|
+
if (input.changeRequestId !== void 0) changes.change_request_id = input.changeRequestId;
|
|
1081
|
+
if (input.changeRequestUrl !== void 0) changes.change_request_url = input.changeRequestUrl;
|
|
1082
|
+
const update = async () => {
|
|
1083
|
+
await db().updateMany(SESSION_REPOSITORIES, key, changes);
|
|
1084
|
+
const row = await db().findOne(SESSION_REPOSITORIES, key);
|
|
1085
|
+
if (!row) throw new Error("Session repository row vanished during upsert.");
|
|
1086
|
+
return toSessionRepository(row);
|
|
1087
|
+
};
|
|
1088
|
+
if (await db().findOne(SESSION_REPOSITORIES, key)) return update();
|
|
1089
|
+
const branch = input.branch ?? input.fallbackBranch;
|
|
1090
|
+
if (!branch) throw new Error("A session repository row needs a branch when it is first recorded.");
|
|
1091
|
+
try {
|
|
1092
|
+
return toSessionRepository(await db().insertOne(SESSION_REPOSITORIES, {
|
|
1093
|
+
...key,
|
|
1094
|
+
branch,
|
|
1095
|
+
change_request_id: input.changeRequestId ?? null,
|
|
1096
|
+
change_request_url: input.changeRequestUrl ?? null,
|
|
1097
|
+
pushed_at: input.pushedAt ?? now,
|
|
1098
|
+
updated_at: now,
|
|
1099
|
+
created_at: now
|
|
1100
|
+
}));
|
|
1101
|
+
} catch (error) {
|
|
1102
|
+
if (!(error instanceof UniqueViolationError)) throw error;
|
|
1103
|
+
return update();
|
|
1104
|
+
}
|
|
1105
|
+
},
|
|
1106
|
+
listBySession: async ({ sessionId }) => (await db().findMany(SESSION_REPOSITORIES, { session_id: sessionId }, { orderBy: [["created_at", "asc"]] })).map(toSessionRepository)
|
|
734
1107
|
}
|
|
735
1108
|
};
|
|
736
1109
|
}
|
|
737
1110
|
};
|
|
738
1111
|
//#endregion
|
|
739
|
-
export { SOURCE_CONTROL_SCHEMAS, SourceControlConnectionNotFoundError, SourceControlStorage };
|
|
1112
|
+
export { SOURCE_CONTROL_SCHEMAS, SessionOwnershipMismatchError, SourceControlConnectionNotFoundError, SourceControlStorage };
|
|
740
1113
|
|
|
741
1114
|
//# sourceMappingURL=base.js.map
|