@mastra/factory 0.2.2-alpha.0 → 0.2.2-alpha.2
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/CHANGELOG.md +36 -0
- package/dist/auth.d.ts +2 -2
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +401 -314
- package/dist/auth.js.map +1 -1
- package/dist/capabilities/connection.js +1 -1
- package/dist/capabilities/intake.js +1 -1
- package/dist/capabilities/version-control.js +1 -1
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +441 -39455
- package/dist/factory.js.map +1 -1
- package/dist/index.js +2 -39466
- package/dist/integrations/base.js +1 -1
- package/dist/integrations/github/config.js +36 -22
- package/dist/integrations/github/config.js.map +1 -1
- package/dist/integrations/github/factory-session.js +33 -37
- package/dist/integrations/github/factory-session.js.map +1 -1
- package/dist/integrations/github/integration.js +1037 -3624
- package/dist/integrations/github/integration.js.map +1 -1
- package/dist/integrations/github/issue-triage.js +71 -71
- package/dist/integrations/github/issue-triage.js.map +1 -1
- package/dist/integrations/github/pat.js +47 -36
- package/dist/integrations/github/pat.js.map +1 -1
- package/dist/integrations/github/provenance.js +72 -101
- package/dist/integrations/github/provenance.js.map +1 -1
- package/dist/integrations/github/routes.d.ts.map +1 -1
- package/dist/integrations/github/routes.js +1272 -1833
- package/dist/integrations/github/routes.js.map +1 -1
- package/dist/integrations/github/rules.js +203 -471
- package/dist/integrations/github/rules.js.map +1 -1
- package/dist/integrations/github/sandbox.d.ts +2 -0
- package/dist/integrations/github/sandbox.d.ts.map +1 -1
- package/dist/integrations/github/sandbox.js +401 -326
- package/dist/integrations/github/sandbox.js.map +1 -1
- package/dist/integrations/github/session-subscriptions.js +165 -223
- package/dist/integrations/github/session-subscriptions.js.map +1 -1
- package/dist/integrations/github/subscriptions.js +43 -51
- package/dist/integrations/github/subscriptions.js.map +1 -1
- package/dist/integrations/github/token-refresh.js +15 -18
- package/dist/integrations/github/token-refresh.js.map +1 -1
- package/dist/integrations/github/webhook.js +359 -343
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/linear/agent-tools.js +77 -100
- package/dist/integrations/linear/agent-tools.js.map +1 -1
- package/dist/integrations/linear/integration.js +611 -1309
- package/dist/integrations/linear/integration.js.map +1 -1
- package/dist/integrations/linear/routes.js +227 -230
- package/dist/integrations/linear/routes.js.map +1 -1
- package/dist/integrations/linear/rules.js +126 -346
- package/dist/integrations/linear/rules.js.map +1 -1
- package/dist/integrations/linear/storage.js +1 -1
- package/dist/integrations/platform/api-client.js +131 -135
- package/dist/integrations/platform/api-client.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.js +275 -573
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/platform/github/integration.d.ts.map +1 -1
- package/dist/integrations/platform/github/integration.js +845 -3942
- package/dist/integrations/platform/github/integration.js.map +1 -1
- package/dist/integrations/platform/linear/integration.js +457 -1309
- package/dist/integrations/platform/linear/integration.js.map +1 -1
- package/dist/integrations/workos/integration.js +80 -85
- package/dist/integrations/workos/integration.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/config.js +933 -915
- package/dist/routes/config.js.map +1 -1
- package/dist/routes/custom-provider-source.js +110 -93
- package/dist/routes/custom-provider-source.js.map +1 -1
- package/dist/routes/fs.js +401 -349
- package/dist/routes/fs.js.map +1 -1
- package/dist/routes/intake.js +165 -165
- package/dist/routes/intake.js.map +1 -1
- package/dist/routes/oauth.js +447 -25166
- package/dist/routes/oauth.js.map +1 -1
- package/dist/routes/projects.js +437 -376
- package/dist/routes/projects.js.map +1 -1
- package/dist/routes/provider-credentials.js +76 -58
- package/dist/routes/provider-credentials.js.map +1 -1
- package/dist/routes/route.js +12 -8
- package/dist/routes/route.js.map +1 -1
- package/dist/routes/skills.js +160 -173
- package/dist/routes/skills.js.map +1 -1
- package/dist/routes/surface.js +250 -28618
- package/dist/routes/surface.js.map +1 -1
- package/dist/routes/tenant-credentials.js +141 -166
- package/dist/routes/tenant-credentials.js.map +1 -1
- package/dist/routes/work-items.js +654 -862
- package/dist/routes/work-items.js.map +1 -1
- package/dist/rules/binding-context.js +23 -35
- package/dist/rules/binding-context.js.map +1 -1
- package/dist/rules/defaults.js +200 -309
- package/dist/rules/defaults.js.map +1 -1
- package/dist/rules/dispatcher.d.ts.map +1 -1
- package/dist/rules/dispatcher.js +438 -677
- package/dist/rules/dispatcher.js.map +1 -1
- package/dist/rules/index.js +5 -599
- package/dist/rules/processor.js +344 -596
- package/dist/rules/processor.js.map +1 -1
- package/dist/rules/resolve.js +21 -18
- package/dist/rules/resolve.js.map +1 -1
- package/dist/rules/start-coordinator.js +168 -157
- package/dist/rules/start-coordinator.js.map +1 -1
- package/dist/rules/tools.js +50 -85
- package/dist/rules/tools.js.map +1 -1
- package/dist/rules/transition-service.js +183 -454
- package/dist/rules/transition-service.js.map +1 -1
- package/dist/rules/types.js +33 -29
- package/dist/rules/types.js.map +1 -1
- package/dist/rules/validation.js +266 -281
- package/dist/rules/validation.js.map +1 -1
- package/dist/sandbox/fleet.js +272 -250
- package/dist/sandbox/fleet.js.map +1 -1
- package/dist/sandbox/reattach.js +13 -6
- package/dist/sandbox/reattach.js.map +1 -1
- package/dist/server-error.js +12 -16
- package/dist/server-error.js.map +1 -1
- package/dist/skills/service.js +30 -35
- package/dist/skills/service.js.map +1 -1
- package/dist/spa-static.d.ts.map +1 -1
- package/dist/spa-static.js +91 -64
- package/dist/spa-static.js.map +1 -1
- package/dist/state-signing.js +73 -44
- package/dist/state-signing.js.map +1 -1
- package/dist/storage/domains/audit/agent-audit.js +60 -59
- package/dist/storage/domains/audit/agent-audit.js.map +1 -1
- package/dist/storage/domains/audit/base.js +169 -112
- package/dist/storage/domains/audit/base.js.map +1 -1
- package/dist/storage/domains/audit/domain.js +155 -153
- package/dist/storage/domains/audit/domain.js.map +1 -1
- package/dist/storage/domains/credentials/base.js +259 -243
- package/dist/storage/domains/credentials/base.js.map +1 -1
- package/dist/storage/domains/custom-providers/base.js +138 -143
- package/dist/storage/domains/custom-providers/base.js.map +1 -1
- package/dist/storage/domains/intake/base.js +75 -57
- package/dist/storage/domains/intake/base.js.map +1 -1
- package/dist/storage/domains/integrations/base.js +310 -245
- package/dist/storage/domains/integrations/base.js.map +1 -1
- package/dist/storage/domains/memory-settings/base.js +117 -102
- package/dist/storage/domains/memory-settings/base.js.map +1 -1
- package/dist/storage/domains/model-packs/base.js +89 -88
- package/dist/storage/domains/model-packs/base.js.map +1 -1
- package/dist/storage/domains/projects/base.js +97 -92
- package/dist/storage/domains/projects/base.js.map +1 -1
- package/dist/storage/domains/queue-health/base.js +111 -76
- package/dist/storage/domains/queue-health/base.js.map +1 -1
- package/dist/storage/domains/source-control/base.d.ts +9 -0
- package/dist/storage/domains/source-control/base.d.ts.map +1 -1
- package/dist/storage/domains/source-control/base.js +777 -702
- package/dist/storage/domains/source-control/base.js.map +1 -1
- package/dist/storage/domains/source-control/inmemory.d.ts +4 -0
- package/dist/storage/domains/source-control/inmemory.d.ts.map +1 -1
- package/dist/storage/domains/source-control/inmemory.js +322 -344
- 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 +1266 -1132
- package/dist/storage/domains/work-items/base.js.map +1 -1
- package/dist/storage/domains/work-items/metrics.js +202 -184
- package/dist/storage/domains/work-items/metrics.js.map +1 -1
- package/dist/timing.d.ts +15 -0
- package/dist/timing.d.ts.map +1 -0
- package/dist/timing.js +34 -0
- package/dist/timing.js.map +1 -0
- package/dist/workspace.d.ts +2 -3
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +236 -438
- package/dist/workspace.js.map +1 -1
- package/factory-skills/factory-review/SKILL.md +9 -2
- package/package.json +9 -9
- package/dist/capabilities/connection.js.map +0 -1
- package/dist/capabilities/intake.js.map +0 -1
- package/dist/capabilities/version-control.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/integrations/base.js.map +0 -1
- package/dist/integrations/linear/storage.js.map +0 -1
- package/dist/rules/index.js.map +0 -1
package/dist/routes/projects.js
CHANGED
|
@@ -1,396 +1,457 @@
|
|
|
1
|
-
|
|
1
|
+
import { Route } from "./route.js";
|
|
2
2
|
import { registerApiRoute } from "@mastra/core/server";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
// src/routes/projects.ts
|
|
13
|
-
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
14
|
-
var MAX_NAME_LENGTH = 200;
|
|
15
|
-
var MAX_DESCRIPTION_LENGTH = 2e3;
|
|
16
|
-
var MAX_SETUP_COMMAND_LENGTH = 2e3;
|
|
17
|
-
var MAX_BRANCH_LENGTH = 255;
|
|
18
|
-
var MAX_SANDBOX_PROVIDER_LENGTH = 100;
|
|
19
|
-
var MAX_SANDBOX_WORKDIR_LENGTH = 1e3;
|
|
20
|
-
var CONTROL_CHAR_RE = /[\0-\x08\x0b\x0c\x0e-\x1f\x7f]/;
|
|
3
|
+
//#region src/routes/projects.ts
|
|
4
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
5
|
+
const MAX_NAME_LENGTH = 200;
|
|
6
|
+
const MAX_DESCRIPTION_LENGTH = 2e3;
|
|
7
|
+
const MAX_SETUP_COMMAND_LENGTH = 2e3;
|
|
8
|
+
const MAX_BRANCH_LENGTH = 255;
|
|
9
|
+
const MAX_SANDBOX_PROVIDER_LENGTH = 100;
|
|
10
|
+
const MAX_SANDBOX_WORKDIR_LENGTH = 1e3;
|
|
11
|
+
const CONTROL_CHAR_RE = /[\0-\x08\x0b\x0c\x0e-\x1f\x7f]/;
|
|
21
12
|
function loose(context) {
|
|
22
|
-
|
|
13
|
+
return context;
|
|
23
14
|
}
|
|
24
15
|
async function readJson(context) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
16
|
+
try {
|
|
17
|
+
return await context.req.json();
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
30
21
|
}
|
|
31
22
|
function parseCreateInput(value) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
if (!value || typeof value !== "object") return null;
|
|
24
|
+
const input = value;
|
|
25
|
+
if (typeof input.name !== "string") return null;
|
|
26
|
+
const name = input.name.trim();
|
|
27
|
+
if (!name || name.length > MAX_NAME_LENGTH) return null;
|
|
28
|
+
if (input.description !== void 0 && input.description !== null && typeof input.description !== "string") return null;
|
|
29
|
+
const description = typeof input.description === "string" ? input.description.trim() || null : null;
|
|
30
|
+
if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;
|
|
31
|
+
return {
|
|
32
|
+
name,
|
|
33
|
+
description
|
|
34
|
+
};
|
|
42
35
|
}
|
|
43
36
|
function parseUpdateInput(value) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
37
|
+
if (!value || typeof value !== "object") return null;
|
|
38
|
+
const input = value;
|
|
39
|
+
const patch = {};
|
|
40
|
+
if (input.name !== void 0) {
|
|
41
|
+
if (typeof input.name !== "string") return null;
|
|
42
|
+
const name = input.name.trim();
|
|
43
|
+
if (!name || name.length > MAX_NAME_LENGTH) return null;
|
|
44
|
+
patch.name = name;
|
|
45
|
+
}
|
|
46
|
+
if (input.description !== void 0) {
|
|
47
|
+
if (input.description !== null && typeof input.description !== "string") return null;
|
|
48
|
+
const description = typeof input.description === "string" ? input.description.trim() || null : null;
|
|
49
|
+
if (description && description.length > MAX_DESCRIPTION_LENGTH) return null;
|
|
50
|
+
patch.description = description;
|
|
51
|
+
}
|
|
52
|
+
if (input.defaultModelId !== void 0) {
|
|
53
|
+
const defaultModelId = parseOptionalString(input.defaultModelId, {
|
|
54
|
+
maxLength: MAX_NAME_LENGTH,
|
|
55
|
+
nullable: true
|
|
56
|
+
});
|
|
57
|
+
if (defaultModelId === false) return null;
|
|
58
|
+
patch.defaultModelId = defaultModelId ?? null;
|
|
59
|
+
}
|
|
60
|
+
return Object.keys(patch).length > 0 ? patch : null;
|
|
65
61
|
}
|
|
66
62
|
function parseConnectionInput(value) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
63
|
+
if (!value || typeof value !== "object") return null;
|
|
64
|
+
const input = value;
|
|
65
|
+
if (typeof input.integrationId !== "string" || !input.integrationId.trim()) return null;
|
|
66
|
+
if (typeof input.installationId !== "string" || !UUID_RE.test(input.installationId)) return null;
|
|
67
|
+
return {
|
|
68
|
+
integrationId: input.integrationId.trim(),
|
|
69
|
+
installationId: input.installationId
|
|
70
|
+
};
|
|
72
71
|
}
|
|
73
72
|
function parseOptionalString(value, { maxLength, nullable = false }) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
if (value === void 0) return void 0;
|
|
74
|
+
if (value === null) return nullable ? null : false;
|
|
75
|
+
if (typeof value !== "string") return false;
|
|
76
|
+
const normalized = value.trim();
|
|
77
|
+
if (!normalized) return nullable ? null : false;
|
|
78
|
+
if (normalized.length > maxLength || CONTROL_CHAR_RE.test(normalized)) return false;
|
|
79
|
+
return normalized;
|
|
81
80
|
}
|
|
82
81
|
function parseRepositoryLinkInput(value) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
82
|
+
if (!value || typeof value !== "object") return null;
|
|
83
|
+
const input = value;
|
|
84
|
+
if (typeof input.repositoryId !== "string" || !UUID_RE.test(input.repositoryId)) return null;
|
|
85
|
+
const branch = parseOptionalString(input.branch, {
|
|
86
|
+
maxLength: MAX_BRANCH_LENGTH,
|
|
87
|
+
nullable: true
|
|
88
|
+
});
|
|
89
|
+
const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });
|
|
90
|
+
const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });
|
|
91
|
+
const setupCommand = parseOptionalString(input.setupCommand, {
|
|
92
|
+
maxLength: MAX_SETUP_COMMAND_LENGTH,
|
|
93
|
+
nullable: true
|
|
94
|
+
});
|
|
95
|
+
if (branch === false || typeof sandboxProvider !== "string" || typeof sandboxWorkdir !== "string" || setupCommand === false) return null;
|
|
96
|
+
return {
|
|
97
|
+
repositoryId: input.repositoryId,
|
|
98
|
+
branch: branch ?? null,
|
|
99
|
+
sandboxProvider,
|
|
100
|
+
sandboxWorkdir,
|
|
101
|
+
setupCommand: setupCommand ?? null
|
|
102
|
+
};
|
|
99
103
|
}
|
|
100
104
|
function parseRepositoryUpdateInput(value) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
105
|
+
if (!value || typeof value !== "object") return null;
|
|
106
|
+
const input = value;
|
|
107
|
+
const patch = {};
|
|
108
|
+
const branch = parseOptionalString(input.branch, {
|
|
109
|
+
maxLength: MAX_BRANCH_LENGTH,
|
|
110
|
+
nullable: true
|
|
111
|
+
});
|
|
112
|
+
const sandboxProvider = parseOptionalString(input.sandboxProvider, { maxLength: MAX_SANDBOX_PROVIDER_LENGTH });
|
|
113
|
+
const sandboxWorkdir = parseOptionalString(input.sandboxWorkdir, { maxLength: MAX_SANDBOX_WORKDIR_LENGTH });
|
|
114
|
+
const setupCommand = parseOptionalString(input.setupCommand, {
|
|
115
|
+
maxLength: MAX_SETUP_COMMAND_LENGTH,
|
|
116
|
+
nullable: true
|
|
117
|
+
});
|
|
118
|
+
if (branch === false || sandboxProvider === false || sandboxProvider === null || sandboxWorkdir === false || sandboxWorkdir === null || setupCommand === false) return null;
|
|
119
|
+
if (branch !== void 0) patch.branch = branch;
|
|
120
|
+
if (sandboxProvider !== void 0) patch.sandboxProvider = sandboxProvider;
|
|
121
|
+
if (sandboxWorkdir !== void 0) patch.sandboxWorkdir = sandboxWorkdir;
|
|
122
|
+
if (setupCommand !== void 0) patch.setupCommand = setupCommand;
|
|
123
|
+
return Object.keys(patch).length > 0 ? patch : null;
|
|
115
124
|
}
|
|
116
125
|
var ProjectRoutes = class extends Route {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
|
|
126
|
+
#versionControlIntegrationIds;
|
|
127
|
+
constructor(deps) {
|
|
128
|
+
super(deps);
|
|
129
|
+
this.#versionControlIntegrationIds = new Set(deps.versionControlIntegrationIds ?? []);
|
|
130
|
+
}
|
|
131
|
+
async #projects() {
|
|
132
|
+
await this.deps.projects.ensureReady();
|
|
133
|
+
return this.deps.projects;
|
|
134
|
+
}
|
|
135
|
+
async #sourceControl() {
|
|
136
|
+
await this.deps.sourceControl.ensureReady();
|
|
137
|
+
return this.deps.sourceControl;
|
|
138
|
+
}
|
|
139
|
+
async #handles() {
|
|
140
|
+
const storage = await this.#sourceControl();
|
|
141
|
+
return [...this.#versionControlIntegrationIds].map((integrationId) => storage.forIntegration(integrationId));
|
|
142
|
+
}
|
|
143
|
+
async #project(orgId, id) {
|
|
144
|
+
return (await this.#projects()).get({
|
|
145
|
+
orgId,
|
|
146
|
+
id
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
async #findConnection({ orgId, projectId, id }) {
|
|
150
|
+
for (const handle of await this.#handles()) {
|
|
151
|
+
const connection = await handle.connections.get({
|
|
152
|
+
orgId,
|
|
153
|
+
id
|
|
154
|
+
});
|
|
155
|
+
if (connection?.factoryProjectId === projectId) return {
|
|
156
|
+
handle,
|
|
157
|
+
connection
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
async #findProjectRepository({ orgId, projectId, id }) {
|
|
163
|
+
for (const handle of await this.#handles()) {
|
|
164
|
+
const projectRepository = await handle.projectRepositories.get({
|
|
165
|
+
orgId,
|
|
166
|
+
id
|
|
167
|
+
});
|
|
168
|
+
if (!projectRepository) continue;
|
|
169
|
+
const connection = await handle.connections.get({
|
|
170
|
+
orgId,
|
|
171
|
+
id: projectRepository.connectionId
|
|
172
|
+
});
|
|
173
|
+
if (connection?.factoryProjectId === projectId) return {
|
|
174
|
+
handle,
|
|
175
|
+
connection,
|
|
176
|
+
projectRepository
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
async #repositoryPayload(handle, orgId, projectRepository) {
|
|
182
|
+
const repository = await handle.repositories.get({
|
|
183
|
+
orgId,
|
|
184
|
+
id: projectRepository.repositoryId
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
...projectRepository,
|
|
188
|
+
repository
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async #resolveTenant(context) {
|
|
192
|
+
await this.deps.auth.ensureUser(context);
|
|
193
|
+
const tenant = this.deps.auth.tenant(context);
|
|
194
|
+
if (!tenant) return { response: context.json({ error: "unauthorized" }, 401) };
|
|
195
|
+
if (!tenant.orgId) return { response: context.json({
|
|
196
|
+
error: "organization_required",
|
|
197
|
+
message: "Factory projects require an organization."
|
|
198
|
+
}, 403) };
|
|
199
|
+
return {
|
|
200
|
+
orgId: tenant.orgId,
|
|
201
|
+
userId: tenant.userId
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
routes() {
|
|
205
|
+
return [
|
|
206
|
+
registerApiRoute("/web/factory/projects", {
|
|
207
|
+
method: "GET",
|
|
208
|
+
requiresAuth: false,
|
|
209
|
+
handler: async (routeContext) => {
|
|
210
|
+
const context = loose(routeContext);
|
|
211
|
+
const tenant = await this.#resolveTenant(context);
|
|
212
|
+
if ("response" in tenant) return tenant.response;
|
|
213
|
+
return context.json({ projects: await (await this.#projects()).list({ orgId: tenant.orgId }) });
|
|
214
|
+
}
|
|
215
|
+
}),
|
|
216
|
+
registerApiRoute("/web/factory/projects", {
|
|
217
|
+
method: "POST",
|
|
218
|
+
requiresAuth: false,
|
|
219
|
+
handler: async (routeContext) => {
|
|
220
|
+
const context = loose(routeContext);
|
|
221
|
+
const tenant = await this.#resolveTenant(context);
|
|
222
|
+
if ("response" in tenant) return tenant.response;
|
|
223
|
+
const input = parseCreateInput(await readJson(context));
|
|
224
|
+
if (!input) return context.json({ error: "invalid_project" }, 400);
|
|
225
|
+
const project = await (await this.#projects()).create({
|
|
226
|
+
orgId: tenant.orgId,
|
|
227
|
+
userId: tenant.userId,
|
|
228
|
+
input
|
|
229
|
+
});
|
|
230
|
+
return context.json({ project }, 201);
|
|
231
|
+
}
|
|
232
|
+
}),
|
|
233
|
+
registerApiRoute("/web/factory/projects/:id", {
|
|
234
|
+
method: "GET",
|
|
235
|
+
requiresAuth: false,
|
|
236
|
+
handler: async (routeContext) => {
|
|
237
|
+
const context = loose(routeContext);
|
|
238
|
+
const tenant = await this.#resolveTenant(context);
|
|
239
|
+
if ("response" in tenant) return tenant.response;
|
|
240
|
+
const id = context.req.param("id");
|
|
241
|
+
if (!id || !UUID_RE.test(id)) return context.json({ error: "Project not found" }, 404);
|
|
242
|
+
const project = await this.#project(tenant.orgId, id);
|
|
243
|
+
return project ? context.json({ project }) : context.json({ error: "Project not found" }, 404);
|
|
244
|
+
}
|
|
245
|
+
}),
|
|
246
|
+
registerApiRoute("/web/factory/projects/:id", {
|
|
247
|
+
method: "PATCH",
|
|
248
|
+
requiresAuth: false,
|
|
249
|
+
handler: async (routeContext) => {
|
|
250
|
+
const context = loose(routeContext);
|
|
251
|
+
const tenant = await this.#resolveTenant(context);
|
|
252
|
+
if ("response" in tenant) return tenant.response;
|
|
253
|
+
const id = context.req.param("id");
|
|
254
|
+
if (!id || !UUID_RE.test(id)) return context.json({ error: "Project not found" }, 404);
|
|
255
|
+
const input = parseUpdateInput(await readJson(context));
|
|
256
|
+
if (!input) return context.json({ error: "invalid_project" }, 400);
|
|
257
|
+
const project = await (await this.#projects()).update({
|
|
258
|
+
orgId: tenant.orgId,
|
|
259
|
+
id,
|
|
260
|
+
input
|
|
261
|
+
});
|
|
262
|
+
return project ? context.json({ project }) : context.json({ error: "Project not found" }, 404);
|
|
263
|
+
}
|
|
264
|
+
}),
|
|
265
|
+
registerApiRoute("/web/factory/projects/:id", {
|
|
266
|
+
method: "DELETE",
|
|
267
|
+
requiresAuth: false,
|
|
268
|
+
handler: async (routeContext) => {
|
|
269
|
+
const context = loose(routeContext);
|
|
270
|
+
const tenant = await this.#resolveTenant(context);
|
|
271
|
+
if ("response" in tenant) return tenant.response;
|
|
272
|
+
const id = context.req.param("id");
|
|
273
|
+
if (!id || !UUID_RE.test(id)) return context.json({ error: "Project not found" }, 404);
|
|
274
|
+
if (!await this.#project(tenant.orgId, id)) return context.json({ error: "Project not found" }, 404);
|
|
275
|
+
for (const handle of await this.#handles()) for (const connection of await handle.connections.list({
|
|
276
|
+
orgId: tenant.orgId,
|
|
277
|
+
factoryProjectId: id
|
|
278
|
+
})) await handle.connections.delete({
|
|
279
|
+
orgId: tenant.orgId,
|
|
280
|
+
id: connection.id
|
|
281
|
+
});
|
|
282
|
+
await (await this.#projects()).delete({
|
|
283
|
+
orgId: tenant.orgId,
|
|
284
|
+
id
|
|
285
|
+
});
|
|
286
|
+
return context.body(null, 204);
|
|
287
|
+
}
|
|
288
|
+
}),
|
|
289
|
+
registerApiRoute("/web/factory/projects/:id/source-control-connections", {
|
|
290
|
+
method: "GET",
|
|
291
|
+
requiresAuth: false,
|
|
292
|
+
handler: async (routeContext) => {
|
|
293
|
+
const context = loose(routeContext);
|
|
294
|
+
const tenant = await this.#resolveTenant(context);
|
|
295
|
+
if ("response" in tenant) return tenant.response;
|
|
296
|
+
const projectId = context.req.param("id");
|
|
297
|
+
if (!projectId || !UUID_RE.test(projectId) || !await this.#project(tenant.orgId, projectId)) return context.json({ error: "Project not found" }, 404);
|
|
298
|
+
const connections = [];
|
|
299
|
+
for (const handle of await this.#handles()) for (const connection of await handle.connections.list({
|
|
300
|
+
orgId: tenant.orgId,
|
|
301
|
+
factoryProjectId: projectId
|
|
302
|
+
})) {
|
|
303
|
+
const installation = await handle.installations.get({
|
|
304
|
+
orgId: tenant.orgId,
|
|
305
|
+
id: connection.installationId
|
|
306
|
+
});
|
|
307
|
+
if (!installation) continue;
|
|
308
|
+
const links = await handle.projectRepositories.list({
|
|
309
|
+
orgId: tenant.orgId,
|
|
310
|
+
connectionId: connection.id
|
|
311
|
+
});
|
|
312
|
+
connections.push({
|
|
313
|
+
...connection,
|
|
314
|
+
installation,
|
|
315
|
+
repositories: await Promise.all(links.map((link) => this.#repositoryPayload(handle, tenant.orgId, link)))
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
return context.json({ connections });
|
|
319
|
+
}
|
|
320
|
+
}),
|
|
321
|
+
registerApiRoute("/web/factory/projects/:id/source-control-connections", {
|
|
322
|
+
method: "POST",
|
|
323
|
+
requiresAuth: false,
|
|
324
|
+
handler: async (routeContext) => {
|
|
325
|
+
const context = loose(routeContext);
|
|
326
|
+
const tenant = await this.#resolveTenant(context);
|
|
327
|
+
if ("response" in tenant) return tenant.response;
|
|
328
|
+
const projectId = context.req.param("id");
|
|
329
|
+
if (!projectId || !UUID_RE.test(projectId) || !await this.#project(tenant.orgId, projectId)) return context.json({ error: "Project not found" }, 404);
|
|
330
|
+
const input = parseConnectionInput(await readJson(context));
|
|
331
|
+
if (!input) return context.json({ error: "invalid_source_control_connection" }, 400);
|
|
332
|
+
if (!this.#versionControlIntegrationIds.has(input.integrationId)) return context.json({ error: "Source-control integration not found" }, 404);
|
|
333
|
+
const handle = (await this.#sourceControl()).forIntegration(input.integrationId);
|
|
334
|
+
if (!await handle.installations.get({
|
|
335
|
+
orgId: tenant.orgId,
|
|
336
|
+
id: input.installationId
|
|
337
|
+
})) return context.json({ error: "Source-control installation not found" }, 404);
|
|
338
|
+
const connection = await handle.connections.create({
|
|
339
|
+
orgId: tenant.orgId,
|
|
340
|
+
factoryProjectId: projectId,
|
|
341
|
+
installationId: input.installationId,
|
|
342
|
+
createdByUserId: tenant.userId
|
|
343
|
+
});
|
|
344
|
+
return context.json({ connection }, 201);
|
|
345
|
+
}
|
|
346
|
+
}),
|
|
347
|
+
registerApiRoute("/web/factory/projects/:id/source-control-connections/:connectionId", {
|
|
348
|
+
method: "DELETE",
|
|
349
|
+
requiresAuth: false,
|
|
350
|
+
handler: async (routeContext) => {
|
|
351
|
+
const context = loose(routeContext);
|
|
352
|
+
const tenant = await this.#resolveTenant(context);
|
|
353
|
+
if ("response" in tenant) return tenant.response;
|
|
354
|
+
const projectId = context.req.param("id");
|
|
355
|
+
const connectionId = context.req.param("connectionId");
|
|
356
|
+
if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId)) return context.json({ error: "Source-control connection not found" }, 404);
|
|
357
|
+
const found = await this.#findConnection({
|
|
358
|
+
orgId: tenant.orgId,
|
|
359
|
+
projectId,
|
|
360
|
+
id: connectionId
|
|
361
|
+
});
|
|
362
|
+
if (!found) return context.json({ error: "Source-control connection not found" }, 404);
|
|
363
|
+
await found.handle.connections.delete({
|
|
364
|
+
orgId: tenant.orgId,
|
|
365
|
+
id: connectionId
|
|
366
|
+
});
|
|
367
|
+
return context.body(null, 204);
|
|
368
|
+
}
|
|
369
|
+
}),
|
|
370
|
+
registerApiRoute("/web/factory/projects/:id/source-control-connections/:connectionId/repositories", {
|
|
371
|
+
method: "POST",
|
|
372
|
+
requiresAuth: false,
|
|
373
|
+
handler: async (routeContext) => {
|
|
374
|
+
const context = loose(routeContext);
|
|
375
|
+
const tenant = await this.#resolveTenant(context);
|
|
376
|
+
if ("response" in tenant) return tenant.response;
|
|
377
|
+
const projectId = context.req.param("id");
|
|
378
|
+
const connectionId = context.req.param("connectionId");
|
|
379
|
+
if (!projectId || !UUID_RE.test(projectId) || !connectionId || !UUID_RE.test(connectionId)) return context.json({ error: "Source-control connection not found" }, 404);
|
|
380
|
+
const found = await this.#findConnection({
|
|
381
|
+
orgId: tenant.orgId,
|
|
382
|
+
projectId,
|
|
383
|
+
id: connectionId
|
|
384
|
+
});
|
|
385
|
+
if (!found) return context.json({ error: "Source-control connection not found" }, 404);
|
|
386
|
+
const input = parseRepositoryLinkInput(await readJson(context));
|
|
387
|
+
if (!input) return context.json({ error: "invalid_project_repository" }, 400);
|
|
388
|
+
const repository = await found.handle.repositories.get({
|
|
389
|
+
orgId: tenant.orgId,
|
|
390
|
+
id: input.repositoryId
|
|
391
|
+
});
|
|
392
|
+
if (!repository || repository.installationId !== found.connection.installationId) return context.json({ error: "Source-control repository not found" }, 404);
|
|
393
|
+
const projectRepository = await found.handle.projectRepositories.link({
|
|
394
|
+
orgId: tenant.orgId,
|
|
395
|
+
connectionId,
|
|
396
|
+
createdByUserId: tenant.userId,
|
|
397
|
+
...input
|
|
398
|
+
});
|
|
399
|
+
return context.json({ projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) }, 201);
|
|
400
|
+
}
|
|
401
|
+
}),
|
|
402
|
+
registerApiRoute("/web/factory/projects/:id/repositories/:projectRepositoryId", {
|
|
403
|
+
method: "PATCH",
|
|
404
|
+
requiresAuth: false,
|
|
405
|
+
handler: async (routeContext) => {
|
|
406
|
+
const context = loose(routeContext);
|
|
407
|
+
const tenant = await this.#resolveTenant(context);
|
|
408
|
+
if ("response" in tenant) return tenant.response;
|
|
409
|
+
const projectId = context.req.param("id");
|
|
410
|
+
const projectRepositoryId = context.req.param("projectRepositoryId");
|
|
411
|
+
if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId)) return context.json({ error: "Project repository not found" }, 404);
|
|
412
|
+
const found = await this.#findProjectRepository({
|
|
413
|
+
orgId: tenant.orgId,
|
|
414
|
+
projectId,
|
|
415
|
+
id: projectRepositoryId
|
|
416
|
+
});
|
|
417
|
+
if (!found) return context.json({ error: "Project repository not found" }, 404);
|
|
418
|
+
const input = parseRepositoryUpdateInput(await readJson(context));
|
|
419
|
+
if (!input) return context.json({ error: "invalid_project_repository" }, 400);
|
|
420
|
+
const projectRepository = await found.handle.projectRepositories.update({
|
|
421
|
+
orgId: tenant.orgId,
|
|
422
|
+
id: projectRepositoryId,
|
|
423
|
+
input
|
|
424
|
+
});
|
|
425
|
+
return context.json({ projectRepository: await this.#repositoryPayload(found.handle, tenant.orgId, projectRepository) });
|
|
426
|
+
}
|
|
427
|
+
}),
|
|
428
|
+
registerApiRoute("/web/factory/projects/:id/repositories/:projectRepositoryId", {
|
|
429
|
+
method: "DELETE",
|
|
430
|
+
requiresAuth: false,
|
|
431
|
+
handler: async (routeContext) => {
|
|
432
|
+
const context = loose(routeContext);
|
|
433
|
+
const tenant = await this.#resolveTenant(context);
|
|
434
|
+
if ("response" in tenant) return tenant.response;
|
|
435
|
+
const projectId = context.req.param("id");
|
|
436
|
+
const projectRepositoryId = context.req.param("projectRepositoryId");
|
|
437
|
+
if (!projectId || !UUID_RE.test(projectId) || !projectRepositoryId || !UUID_RE.test(projectRepositoryId)) return context.json({ error: "Project repository not found" }, 404);
|
|
438
|
+
const found = await this.#findProjectRepository({
|
|
439
|
+
orgId: tenant.orgId,
|
|
440
|
+
projectId,
|
|
441
|
+
id: projectRepositoryId
|
|
442
|
+
});
|
|
443
|
+
if (!found) return context.json({ error: "Project repository not found" }, 404);
|
|
444
|
+
await found.handle.projectRepositories.unlink({
|
|
445
|
+
orgId: tenant.orgId,
|
|
446
|
+
id: projectRepositoryId
|
|
447
|
+
});
|
|
448
|
+
return context.body(null, 204);
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
];
|
|
452
|
+
}
|
|
395
453
|
};
|
|
454
|
+
//#endregion
|
|
455
|
+
export { ProjectRoutes };
|
|
456
|
+
|
|
396
457
|
//# sourceMappingURL=projects.js.map
|