@bevel-software/platform-core-backend 0.30.0 → 0.32.0
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/agent-guide/access-control.md +49 -4
- package/agent-guide/html-views.md +76 -2
- package/dist/core/catalog-revision.js +3 -3
- package/dist/core/create-core-server.d.ts.map +1 -1
- package/dist/core/create-core-server.js +2 -1
- package/dist/core/create-core-server.js.map +1 -1
- package/dist/core/create-core-services.d.ts +7 -0
- package/dist/core/create-core-services.d.ts.map +1 -1
- package/dist/core/create-core-services.js +16 -3
- package/dist/core/create-core-services.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/access/access-control.service.d.ts.map +1 -1
- package/dist/modules/access/access-control.service.js +1 -5
- package/dist/modules/access/access-control.service.js.map +1 -1
- package/dist/modules/access-model/index.d.ts +14 -0
- package/dist/modules/access-model/index.d.ts.map +1 -0
- package/dist/modules/access-model/index.js +13 -0
- package/dist/modules/access-model/index.js.map +1 -0
- package/dist/modules/audit/agent-audit.service.d.ts.map +1 -1
- package/dist/modules/audit/agent-audit.service.js +66 -6
- package/dist/modules/audit/agent-audit.service.js.map +1 -1
- package/dist/modules/audit/audit.contract.d.ts +52 -8
- package/dist/modules/audit/audit.contract.d.ts.map +1 -1
- package/dist/modules/audit/audit.contract.js +37 -1
- package/dist/modules/audit/audit.contract.js.map +1 -1
- package/dist/modules/audit/audit.routes.d.ts +1 -1
- package/dist/modules/audit/audit.routes.d.ts.map +1 -1
- package/dist/modules/audit/audit.routes.js +15 -13
- package/dist/modules/audit/audit.routes.js.map +1 -1
- package/dist/modules/audit/request-audit.d.ts +21 -10
- package/dist/modules/audit/request-audit.d.ts.map +1 -1
- package/dist/modules/audit/request-audit.js +26 -10
- package/dist/modules/audit/request-audit.js.map +1 -1
- package/dist/modules/auth/account-hooks.d.ts +4 -6
- package/dist/modules/auth/account-hooks.d.ts.map +1 -1
- package/dist/modules/auth/account-hooks.js +7 -17
- package/dist/modules/auth/account-hooks.js.map +1 -1
- package/dist/modules/auth/auth.service.d.ts +9 -8
- package/dist/modules/auth/auth.service.d.ts.map +1 -1
- package/dist/modules/auth/auth.service.js +19 -18
- package/dist/modules/auth/auth.service.js.map +1 -1
- package/dist/modules/database/core-schema.d.ts +43 -4
- package/dist/modules/database/core-schema.d.ts.map +1 -1
- package/dist/modules/database/core-schema.js +31 -7
- package/dist/modules/database/core-schema.js.map +1 -1
- package/dist/modules/kb-fs/index.d.ts +1 -0
- package/dist/modules/kb-fs/index.d.ts.map +1 -1
- package/dist/modules/kb-fs/index.js +1 -0
- package/dist/modules/kb-fs/index.js.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.d.ts.map +1 -1
- package/dist/modules/kb-fs/locking-filesystem.js +3 -12
- package/dist/modules/kb-fs/locking-filesystem.js.map +1 -1
- package/dist/modules/kb-fs/mutex.d.ts +11 -0
- package/dist/modules/kb-fs/mutex.d.ts.map +1 -1
- package/dist/modules/kb-fs/mutex.js +14 -0
- package/dist/modules/kb-fs/mutex.js.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.d.ts +14 -0
- package/dist/modules/mcp/mcp-auth.middleware.d.ts.map +1 -1
- package/dist/modules/mcp/mcp-auth.middleware.js +4 -0
- package/dist/modules/mcp/mcp-auth.middleware.js.map +1 -1
- package/dist/modules/mcp/mcp.routes.d.ts +5 -0
- package/dist/modules/mcp/mcp.routes.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.routes.js +67 -0
- package/dist/modules/mcp/mcp.routes.js.map +1 -1
- package/dist/modules/mcp/mcp.service.d.ts +107 -27
- package/dist/modules/mcp/mcp.service.d.ts.map +1 -1
- package/dist/modules/mcp/mcp.service.js +257 -50
- package/dist/modules/mcp/mcp.service.js.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts +28 -0
- package/dist/modules/mcp/oauth/bevel-oauth-provider.d.ts.map +1 -1
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js +166 -39
- package/dist/modules/mcp/oauth/bevel-oauth-provider.js.map +1 -1
- package/dist/modules/mcp/request-context.d.ts +25 -0
- package/dist/modules/mcp/request-context.d.ts.map +1 -0
- package/dist/modules/mcp/request-context.js +11 -0
- package/dist/modules/mcp/request-context.js.map +1 -0
- package/dist/modules/mcp/shared-definitions.d.ts +56 -0
- package/dist/modules/mcp/shared-definitions.d.ts.map +1 -0
- package/dist/modules/mcp/shared-definitions.js +117 -0
- package/dist/modules/mcp/shared-definitions.js.map +1 -0
- package/dist/modules/mcp/surface-cache.d.ts +70 -0
- package/dist/modules/mcp/surface-cache.d.ts.map +1 -0
- package/dist/modules/mcp/surface-cache.js +104 -0
- package/dist/modules/mcp/surface-cache.js.map +1 -0
- package/dist/modules/tool-helpers/tool-context.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool-context.js +33 -12
- package/dist/modules/tool-helpers/tool-context.js.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.d.ts +8 -0
- package/dist/modules/tool-helpers/tool.contract.d.ts.map +1 -1
- package/dist/modules/tool-helpers/tool.contract.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js +3 -14
- package/dist/modules/workflow/agent-tools/change-request-read.tools.js.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts +10 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/change-request-summary.js +2 -0
- package/dist/modules/workflow/agent-tools/change-request-summary.js.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts +18 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.d.ts.map +1 -1
- package/dist/modules/workflow/agent-tools/workflow.tools.js +458 -2
- package/dist/modules/workflow/agent-tools/workflow.tools.js.map +1 -1
- package/dist/modules/workflow/change-request-hooks.d.ts +78 -0
- package/dist/modules/workflow/change-request-hooks.d.ts.map +1 -0
- package/dist/modules/workflow/change-request-hooks.js +15 -0
- package/dist/modules/workflow/change-request-hooks.js.map +1 -0
- package/dist/modules/workflow/git/git.service.d.ts +113 -4
- package/dist/modules/workflow/git/git.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/git.service.js +470 -106
- package/dist/modules/workflow/git/git.service.js.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.d.ts +10 -0
- package/dist/modules/workflow/git/pull-request.service.d.ts.map +1 -1
- package/dist/modules/workflow/git/pull-request.service.js +9 -0
- package/dist/modules/workflow/git/pull-request.service.js.map +1 -1
- package/dist/modules/workflow/read-on-branch.d.ts +35 -0
- package/dist/modules/workflow/read-on-branch.d.ts.map +1 -0
- package/dist/modules/workflow/read-on-branch.js +58 -0
- package/dist/modules/workflow/read-on-branch.js.map +1 -0
- package/dist/modules/workflow/workflow.routes.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.routes.js +22 -41
- package/dist/modules/workflow/workflow.routes.js.map +1 -1
- package/dist/modules/workflow/workflow.service.d.ts +124 -24
- package/dist/modules/workflow/workflow.service.d.ts.map +1 -1
- package/dist/modules/workflow/workflow.service.js +326 -48
- package/dist/modules/workflow/workflow.service.js.map +1 -1
- package/dist/modules/workspace/index.d.ts +2 -0
- package/dist/modules/workspace/index.d.ts.map +1 -1
- package/dist/modules/workspace/index.js +4 -0
- package/dist/modules/workspace/index.js.map +1 -1
- package/dist/shared/git.contract.d.ts +7 -0
- package/dist/shared/git.contract.d.ts.map +1 -1
- package/dist/shared/git.contract.js +9 -0
- package/dist/shared/git.contract.js.map +1 -1
- package/dist/shared/listener-seam.d.ts +34 -0
- package/dist/shared/listener-seam.d.ts.map +1 -0
- package/dist/shared/listener-seam.js +42 -0
- package/dist/shared/listener-seam.js.map +1 -0
- package/dist/shared/no-link-below.d.ts +10 -0
- package/dist/shared/no-link-below.d.ts.map +1 -0
- package/dist/shared/no-link-below.js +30 -0
- package/dist/shared/no-link-below.js.map +1 -0
- package/migrations/0018_agent_events_page.sql +11 -0
- package/migrations/0019_oauth_token_rotated_at.sql +7 -0
- package/migrations/meta/0018_snapshot.json +2429 -0
- package/migrations/meta/0019_snapshot.json +2435 -0
- package/migrations/meta/_journal.json +15 -1
- package/package.json +3 -3
- package/src/core/catalog-revision.ts +3 -3
- package/src/core/create-core-server.ts +2 -1
- package/src/core/create-core-services.ts +21 -2
- package/src/index.ts +7 -0
- package/src/modules/access/access-control.service.ts +1 -6
- package/src/modules/access-model/index.ts +13 -0
- package/src/modules/agent-guide/__tests__/agent-guide.test.ts +22 -1
- package/src/modules/audit/__tests__/agent-audit.service.test.ts +78 -1
- package/src/modules/audit/__tests__/audit.routes.test.ts +43 -5
- package/src/modules/audit/agent-audit.service.ts +66 -6
- package/src/modules/audit/audit.contract.ts +62 -10
- package/src/modules/audit/audit.routes.ts +14 -12
- package/src/modules/audit/request-audit.ts +28 -13
- package/src/modules/auth/__tests__/account-deactivation.test.ts +38 -2
- package/src/modules/auth/account-hooks.ts +13 -21
- package/src/modules/auth/auth.service.ts +19 -18
- package/src/modules/database/core-schema.ts +31 -7
- package/src/modules/kb-fs/__tests__/mutex.canonical.test.ts +57 -0
- package/src/modules/kb-fs/index.ts +1 -0
- package/src/modules/kb-fs/locking-filesystem.ts +3 -12
- package/src/modules/kb-fs/mutex.ts +16 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.pg.test.ts +279 -0
- package/src/modules/mcp/__tests__/bevel-oauth-provider.test.ts +190 -15
- package/src/modules/mcp/__tests__/mcp-routes-harness.ts +7 -1
- package/src/modules/mcp/__tests__/mcp.routes.page.test.ts +115 -0
- package/src/modules/mcp/__tests__/mcp.surface-cache.test.ts +521 -0
- package/src/modules/mcp/__tests__/shared-definitions.test.ts +28 -0
- package/src/modules/mcp/__tests__/surface-cache.test.ts +127 -0
- package/src/modules/mcp/mcp-auth.middleware.ts +21 -0
- package/src/modules/mcp/mcp.routes.ts +68 -0
- package/src/modules/mcp/mcp.service.ts +315 -51
- package/src/modules/mcp/oauth/bevel-oauth-provider.ts +214 -48
- package/src/modules/mcp/request-context.ts +34 -0
- package/src/modules/mcp/shared-definitions.ts +121 -0
- package/src/modules/mcp/surface-cache.ts +154 -0
- package/src/modules/tool-helpers/tool-context.ts +37 -18
- package/src/modules/tool-helpers/tool.contract.ts +8 -0
- package/src/modules/tool-registry/__tests__/own-tool-schemas.test.ts +1 -1
- package/src/modules/tool-registry/__tests__/tool-description-length.test.ts +1 -1
- package/src/modules/workflow/__tests__/change-request-hooks.test.ts +62 -0
- package/src/modules/workflow/__tests__/workflow.routes.history-read-gate.test.ts +23 -12
- package/src/modules/workflow/__tests__/workflow.routes.open-change-request.test.ts +79 -0
- package/src/modules/workflow/__tests__/workflow.service.delete-branch.test.ts +9 -1
- package/src/modules/workflow/__tests__/workflow.service.facade.test.ts +508 -5
- package/src/modules/workflow/agent-tools/__tests__/workflow.tools.test.ts +474 -4
- package/src/modules/workflow/agent-tools/change-request-read.tools.ts +4 -22
- package/src/modules/workflow/agent-tools/change-request-summary.ts +10 -1
- package/src/modules/workflow/agent-tools/workflow.tools.ts +468 -1
- package/src/modules/workflow/change-request-hooks.ts +96 -0
- package/src/modules/workflow/git/__tests__/git.service.diffFileBetweenBranches.test.ts +76 -11
- package/src/modules/workflow/git/__tests__/git.service.mergeIntoBranch.test.ts +721 -0
- package/src/modules/workflow/git/git.service.ts +537 -106
- package/src/modules/workflow/git/pull-request.service.ts +10 -0
- package/src/modules/workflow/read-on-branch.ts +79 -0
- package/src/modules/workflow/workflow.routes.ts +21 -39
- package/src/modules/workflow/workflow.service.ts +390 -55
- package/src/modules/workspace/index.ts +11 -0
- package/src/shared/__tests__/no-link-below.test.ts +45 -0
- package/src/shared/git.contract.ts +10 -0
- package/src/shared/listener-seam.ts +52 -0
- package/src/shared/no-link-below.ts +29 -0
|
@@ -253,6 +253,16 @@ export class PullRequestService implements IPullRequestService {
|
|
|
253
253
|
this.linkBase = changeRequestLinkBase(publicFrontendUrl);
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
/**
|
|
257
|
+
* The link to change request `number`, as every summary and detail carries
|
|
258
|
+
* it — absolute under the public address, else the in-app path with its
|
|
259
|
+
* note. Needs no read: a caller holding only the number (an open whose push
|
|
260
|
+
* was refused, say) builds the same link a listing would.
|
|
261
|
+
*/
|
|
262
|
+
linkFor(number: number): { url: string; urlNote?: string } {
|
|
263
|
+
return changeRequestLink(number, this.linkBase);
|
|
264
|
+
}
|
|
265
|
+
|
|
256
266
|
setDetailEnricher(enricher: PrDetailEnricher): void {
|
|
257
267
|
this.detailEnricher = enricher;
|
|
258
268
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { IAccessControl } from '../access/access-control.interface.js';
|
|
2
|
+
import { WorkflowDomainError } from '../../shared/domain-errors.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Which of `paths` `userEmail` may read at `origin/<base>`, as a membership
|
|
6
|
+
* set — ONE rule for the change-request reads (their file lists, judged at
|
|
7
|
+
* the request's base) and for `branch_status` (the paths a request would
|
|
8
|
+
* carry). A null answer (the ref, or its `roles.yaml`, does not resolve)
|
|
9
|
+
* withholds everything, which is the same reading the app's fork-point
|
|
10
|
+
* route gives it.
|
|
11
|
+
*/
|
|
12
|
+
export async function readablePathsAt(
|
|
13
|
+
accessControl: Pick<IAccessControl, 'canReadBatchAtRef'>,
|
|
14
|
+
workspaceId: string,
|
|
15
|
+
userEmail: string,
|
|
16
|
+
base: string,
|
|
17
|
+
paths: string[],
|
|
18
|
+
): Promise<Set<string>> {
|
|
19
|
+
if (paths.length === 0) return new Set();
|
|
20
|
+
const verdicts = await accessControl.canReadBatchAtRef(workspaceId, `origin/${base}`, userEmail, [...new Set(paths)]);
|
|
21
|
+
if (!verdicts) return new Set();
|
|
22
|
+
return new Set([...verdicts].filter(([, allowed]) => allowed).map(([p]) => p));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Whether `userEmail` may read `repoRelativePath` at EVERY one of `commits`
|
|
27
|
+
* — ONE rule for content served from another branch, for the app's compare
|
|
28
|
+
* route and the agent's history tools alike: the commits the service read
|
|
29
|
+
* the content at, pinned by the service inside its reservation (a diff's two
|
|
30
|
+
* sides, a file at a branch's published tip, the two commits a merge is of),
|
|
31
|
+
* so the verdict is about the bytes actually served, never about a branch
|
|
32
|
+
* ref a fetch may have moved between the verdict and the read. A commit
|
|
33
|
+
* whose rules cannot be read (null) grants nothing. An access-model FAILURE
|
|
34
|
+
* is not a null: it propagates with its own status (an unreadable access
|
|
35
|
+
* tree is the documented 503 a client retries).
|
|
36
|
+
*/
|
|
37
|
+
export async function readableAtCommits(
|
|
38
|
+
accessControl: Pick<IAccessControl, 'canReadAtRef'>,
|
|
39
|
+
workspaceId: string,
|
|
40
|
+
userEmail: string,
|
|
41
|
+
repoRelativePath: string,
|
|
42
|
+
commits: readonly string[],
|
|
43
|
+
): Promise<boolean> {
|
|
44
|
+
for (const sha of commits) {
|
|
45
|
+
if ((await accessControl.canReadAtRef(workspaceId, sha, userEmail, repoRelativePath)) !== true) return false;
|
|
46
|
+
}
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** The refusal `readableAtCommits` answers false with, as every caller words it: the branch named, never the commit. */
|
|
51
|
+
export class ReadOnBranchDeniedError extends WorkflowDomainError {
|
|
52
|
+
constructor(displayPath: string, branch: string) {
|
|
53
|
+
super(`You don't have permission to read "${displayPath}" on "${branch}".`, 403, {
|
|
54
|
+
kind: 'read-denied',
|
|
55
|
+
path: displayPath,
|
|
56
|
+
branch,
|
|
57
|
+
});
|
|
58
|
+
this.name = 'ReadOnBranchDeniedError';
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* `readableAtCommits` at every commit `branch` resolves to (the one its
|
|
64
|
+
* content was served from, and any other its local head or published tip
|
|
65
|
+
* names), thrown when it answers false.
|
|
66
|
+
*/
|
|
67
|
+
export async function assertReadableAtCommits(
|
|
68
|
+
accessControl: Pick<IAccessControl, 'canReadAtRef'>,
|
|
69
|
+
workspaceId: string,
|
|
70
|
+
userEmail: string,
|
|
71
|
+
repoRelativePath: string,
|
|
72
|
+
commits: readonly string[],
|
|
73
|
+
branch: string,
|
|
74
|
+
displayPath: string = repoRelativePath,
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
if (!(await readableAtCommits(accessControl, workspaceId, userEmail, repoRelativePath, commits))) {
|
|
77
|
+
throw new ReadOnBranchDeniedError(displayPath, branch);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
@@ -38,6 +38,7 @@ import type { WorkspaceService } from '../workspace/workspace.service.js';
|
|
|
38
38
|
import { branchForWorkspaceId } from '../../shared/workspace-id.js';
|
|
39
39
|
import type { WorkflowEventBus } from './event-bus.js';
|
|
40
40
|
import { WorkflowDomainError } from '../../shared/domain-errors.js';
|
|
41
|
+
import { assertReadableAtCommits } from './read-on-branch.js';
|
|
41
42
|
import { assertValidRelativePath } from '../kb-fs/branch-name.js';
|
|
42
43
|
import { domainErrorBody } from '../../shared/http-errors.js';
|
|
43
44
|
import { canonicalFileIdentity } from '../../shared/canonical-file-identity.js';
|
|
@@ -413,46 +414,27 @@ export function createWorkflowRoutes(
|
|
|
413
414
|
// The other history endpoints serve commits of the URL workspace's own
|
|
414
415
|
// branch, so its working-tree verdict covers what they return. This one
|
|
415
416
|
// serves content of TWO caller-named branches — the verdict must come
|
|
416
|
-
// from the
|
|
417
|
-
//
|
|
418
|
-
//
|
|
419
|
-
//
|
|
420
|
-
//
|
|
421
|
-
//
|
|
422
|
-
//
|
|
423
|
-
//
|
|
424
|
-
// workspace `canRead`
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
for (const ref of [`origin/${branch}`, branch]) {
|
|
432
|
-
try {
|
|
433
|
-
verdicts.push(await accessControl.canReadAtRef(req.params.id, ref, user.email, repoRelative));
|
|
434
|
-
} catch (err) {
|
|
435
|
-
// An access-model FAILURE is not an unresolvable ref: mapped to
|
|
436
|
-
// null it would let the other candidate's grant serve a ref
|
|
437
|
-
// whose authorization errored. Fail closed — but keep the
|
|
438
|
-
// error's own status: an unreadable access tree is the
|
|
439
|
-
// documented 503 a client retries, not a permanent 403 denial.
|
|
440
|
-
const { status, body } = toHttpError(err);
|
|
441
|
-
res.status(status).json(body);
|
|
442
|
-
return;
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
const denied = verdicts.some((v) => v === false);
|
|
446
|
-
const unresolvable = verdicts.every((v) => v === null);
|
|
447
|
-
if (denied || unresolvable) {
|
|
448
|
-
res.status(403).json({ error: `You don't have permission to read "${pathParam}" on "${branch}".` });
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
}
|
|
417
|
+
// from the commits actually served (`readableAtCommits`, the one rule
|
|
418
|
+
// this route and the agent's history tools share): the service pins the
|
|
419
|
+
// commit each side is read at and asks this verdict inside its
|
|
420
|
+
// reservation, before anything is said about the path at either, with
|
|
421
|
+
// every commit the branch resolves to — its local head and its published
|
|
422
|
+
// tip, all of which must grant, as this route has always required of
|
|
423
|
+
// both refs — never at a branch ref a fetch may move between a verdict
|
|
424
|
+
// and a read. (The
|
|
425
|
+
// working-tree side is additionally covered by the workspace `canRead`
|
|
426
|
+
// above.) An access-model failure keeps its own status: an unreadable
|
|
427
|
+
// access tree is the documented 503 a client retries, not a permanent
|
|
428
|
+
// 403 denial.
|
|
429
|
+
const user = await requireUser(req, res);
|
|
430
|
+
if (!user) return;
|
|
431
|
+
const repoRelative = toKbRelative(pathParam, kbDirName)!;
|
|
453
432
|
try {
|
|
454
|
-
const
|
|
455
|
-
|
|
433
|
+
const compared = await workflow.compareFile(req.params.id, pathParam, fromBranch, toBranch, async (sides) => {
|
|
434
|
+
await assertReadableAtCommits(accessControl, req.params.id, user.email, repoRelative, sides.from, fromBranch, pathParam);
|
|
435
|
+
await assertReadableAtCommits(accessControl, req.params.id, user.email, repoRelative, sides.to, toBranch, pathParam);
|
|
436
|
+
});
|
|
437
|
+
res.json({ diff: compared.diff });
|
|
456
438
|
} catch (err) {
|
|
457
439
|
const { status, body } = toHttpError(err);
|
|
458
440
|
res.status(status).json(body);
|