@tequila99/githuman 0.2.3 → 0.4.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/README.md +26 -0
- package/dist/cli/commands/export.js +2 -1
- package/dist/cli/server-runtime.js +13 -1
- package/dist/cli/startup-message.js +3 -2
- package/dist/server/adapters/agents/event-mapper.js +140 -0
- package/dist/server/app.js +21 -12
- package/dist/server/config/agents.js +141 -0
- package/dist/server/errors/agents.js +12 -0
- package/dist/server/errors/comments.js +2 -0
- package/dist/server/errors/export.js +2 -0
- package/dist/server/errors/http.js +4 -0
- package/dist/server/errors/reviews.js +3 -0
- package/dist/server/hooks/agents/local-origin.js +33 -0
- package/dist/server/repositories/review.repo.js +1 -3
- package/dist/server/routes/agent.js +135 -0
- package/dist/server/routes/comments.js +17 -67
- package/dist/server/routes/diff.js +40 -7
- package/dist/server/routes/events.js +6 -1
- package/dist/server/routes/export.js +6 -15
- package/dist/server/routes/git.js +12 -21
- package/dist/server/routes/reviews.js +11 -43
- package/dist/server/services/agent/agent-session.js +497 -0
- package/dist/server/services/agent/context-builder.js +137 -0
- package/dist/server/services/agent/file-index.js +58 -0
- package/dist/server/services/agent/session-registry.js +100 -0
- package/dist/server/services/comment.service.js +1 -2
- package/dist/server/services/diff.service.js +121 -2
- package/dist/server/services/export.service.js +1 -2
- package/dist/server/services/git.service.js +110 -10
- package/dist/server/services/review.service.js +3 -3
- package/dist/server/utils/executable.js +26 -0
- package/dist/server/utils/file-name.js +6 -0
- package/dist/server/utils/sse.js +11 -0
- package/dist/server/utils/text.js +29 -0
- package/dist/shared/agents/chat-names.js +17 -0
- package/dist/shared/agents/constants.js +6 -0
- package/dist/shared/agents/context-format.js +17 -0
- package/dist/shared/agents/file-search.js +48 -0
- package/dist/shared/agents/schemas.js +51 -0
- package/dist/shared/agents/types.js +11 -0
- package/dist/shared/app/types.js +1 -0
- package/dist/shared/comments/types.js +1 -0
- package/dist/shared/diff/types.js +4 -0
- package/dist/shared/{types.js → events/types.js} +0 -3
- package/dist/shared/git/types.js +1 -0
- package/dist/shared/http/types.js +1 -0
- package/dist/shared/network/loopback.js +4 -0
- package/dist/shared/reviews/types.js +1 -0
- package/dist/shared/utils/error-message.js +3 -0
- package/dist/shared/utils/guards.js +4 -0
- package/dist/web/assets/AppPage-BNPBe_fi.js +1 -0
- package/dist/web/assets/CreateReviewFabButton-3V9ZYSHd.js +1 -0
- package/dist/web/assets/CreateReviewFabButton-Cufd8hVJ.css +1 -0
- package/dist/web/assets/{ErrorNotFound-BuL12Ld8.js → ErrorNotFound-BAP3n0aC.js} +1 -1
- package/dist/web/assets/IndexPage-CztuM7ha.css +1 -0
- package/dist/web/assets/IndexPage-xqMEFqAN.js +1 -0
- package/dist/web/assets/MainLayout-CMbAu8_d.js +7 -0
- package/dist/web/assets/MainLayout-ChxXEt_Q.css +1 -0
- package/dist/web/assets/QBtn-BDbJU6_B.js +1 -0
- package/dist/web/assets/QMenu-QYPSkHBE.js +1 -0
- package/dist/web/assets/QSelect-D4U0Of4k.js +1 -0
- package/dist/web/assets/ReviewDetailPage-CP6YqXPT.css +1 -0
- package/dist/web/assets/ReviewDetailPage-DSCeLI9D.js +1 -0
- package/dist/web/assets/ReviewsPage-CmHdSBsU.js +1 -0
- package/dist/web/assets/StatusSwitcher-Ca5k9Tjx.css +1 -0
- package/dist/web/assets/StatusSwitcher-CenEW-Lu.js +4 -0
- package/dist/web/assets/abnfDiagram-O67JEVCF-C4PFTrGZ.js +1 -0
- package/dist/web/assets/agent-icon-BUd-vCRS.css +1 -0
- package/dist/web/assets/agent-icon-C1oimBWa.js +17 -0
- package/dist/web/assets/agent-store-D1WRRYIE.js +1 -0
- package/dist/web/assets/app-info-store-xZiQfdgO.js +1 -0
- package/dist/web/assets/arc-CiNErV2i.js +1 -0
- package/dist/web/assets/architecture-7GRP2DOG-DORh0iBM.js +1 -0
- package/dist/web/assets/architectureDiagram-NJMV4G6O-DnAqHYSW.js +36 -0
- package/dist/web/assets/array-BifhSqXX.js +1 -0
- package/dist/web/assets/blockDiagram-BEXU5L5S-CCN9Ja5L.js +140 -0
- package/dist/web/assets/c4Diagram-YGBWAQC7-CPzIw79A.js +38 -0
- package/dist/web/assets/channel-WV0H_O-w.js +1 -0
- package/dist/web/assets/chunk-2Q5K7J3B-C1jixKkw.js +1 -0
- package/dist/web/assets/chunk-3FUC2YCW-BbeiQKv_.js +2 -0
- package/dist/web/assets/chunk-5DYCD2WN-CuzXaWoe.js +1 -0
- package/dist/web/assets/chunk-5VM5RSS4-ZNzvKenW.js +15 -0
- package/dist/web/assets/chunk-742MDFTN-DJUCmQfr.js +1 -0
- package/dist/web/assets/chunk-7INBJB4K-BBd4ghzW.js +62 -0
- package/dist/web/assets/chunk-7M6MHVWA-B0L68u_O.js +213 -0
- package/dist/web/assets/chunk-7PRAP22T-BvEmVPr6.js +1 -0
- package/dist/web/assets/chunk-DUW6YSOI-Dgx8z5s3.js +1 -0
- package/dist/web/assets/chunk-FOHPRMQF-CoA4358r.js +161 -0
- package/dist/web/assets/chunk-GTNCS2PH-B_vHEKRO.js +88 -0
- package/dist/web/assets/chunk-GWA4HPMP-DWaW5-VL.js +1 -0
- package/dist/web/assets/chunk-J5ZVWO5B-oDCd-gWO.js +1 -0
- package/dist/web/assets/chunk-JWPE2WC7-DVXcaiue.js +1 -0
- package/dist/web/assets/chunk-MBY4JIJT-Dfo1FM_M.js +72 -0
- package/dist/web/assets/chunk-NETBCI7D-DzjBtXVV.js +1 -0
- package/dist/web/assets/chunk-O7XYJQB3-DY5ZFCJH.js +126 -0
- package/dist/web/assets/chunk-UA2S7LBM-DX_QjVLS.js +1 -0
- package/dist/web/assets/chunk-WEXAMYUT-Cckc2fqg.js +1 -0
- package/dist/web/assets/chunk-XXDRQBXY-DjF0kpJr.js +1 -0
- package/dist/web/assets/chunk-Y2CYZVJY-DsF7k-Jl.js +1 -0
- package/dist/web/assets/chunk-Z7XXMR3K-Do7oG6KK.js +10 -0
- package/dist/web/assets/chunk-ZIGJFQKS-DTcqsqHq.js +2 -0
- package/dist/web/assets/classDiagram-v2-NBCMYWYE-Z6ZhPv4N.js +217 -0
- package/dist/web/assets/{core-Ruj7F7dx.js → core-tP7fpdit.js} +1 -1
- package/dist/web/assets/cose-bilkent-JH36ORCC-B5DV641c.js +1 -0
- package/dist/web/assets/create-BkZH2Q0U.js +1 -0
- package/dist/web/assets/cynefin-OW5HDTMX-BYrEwheK.js +1 -0
- package/dist/web/assets/cynefinDiagram-VND7K2PF-BXbLnUDP.js +62 -0
- package/dist/web/assets/cytoscape.esm-B-1iW2ft.js +321 -0
- package/dist/web/assets/dagre-6A5THRUB-DJHHAqZU.js +4 -0
- package/dist/web/assets/debounce-CDIE7CV6.js +1 -0
- package/dist/web/assets/defaultLocale-BFoDCU3G.js +1 -0
- package/dist/web/assets/diagram-22UHCM2B-DA9deaTX.js +200 -0
- package/dist/web/assets/diagram-3UASUU5V-C6bo1gN6.js +24 -0
- package/dist/web/assets/diagram-ATOU4E4O-TTQR35E_.js +3 -0
- package/dist/web/assets/diagram-CDSNMT55-ChFXwow0.js +30 -0
- package/dist/web/assets/diagram-MLGK6HIB-D3F8BzhV.js +24 -0
- package/dist/web/assets/diagram-MPIPVDR6-NJ6F45a1.js +41 -0
- package/dist/web/assets/dist-Besl4xjp.js +1 -0
- package/dist/web/assets/ebnfDiagram-ZINNZB2B-u2e1UzYe.js +1 -0
- package/dist/web/assets/elk-276RUBZZ-tCLm9cCU.js +27 -0
- package/dist/web/assets/engine-oniguruma-DaaPGLR3.js +1 -0
- package/dist/web/assets/erDiagram-OPXOYQCR-ChNVjKDe.js +99 -0
- package/dist/web/assets/event-rR76ioXz.js +1 -0
- package/dist/web/assets/eventmodeling-NTZA5JFV-KBTHxbEB.js +1 -0
- package/dist/web/assets/export-file-Di_2jvjP.js +1 -0
- package/dist/web/assets/fira-code-symbols2-400-normal-9X26I2LK.woff2 +0 -0
- package/dist/web/assets/fira-code-symbols2-500-normal-B5s_njw3.woff2 +0 -0
- package/dist/web/assets/fira-code-symbols2-600-normal-Bu3RnO39.woff2 +0 -0
- package/dist/web/assets/flowDiagram-KWPJA3E3-DM-yXypD.js +1 -0
- package/dist/web/assets/ganttDiagram-FUAMR5RP-8mcAm2ao.js +292 -0
- package/dist/web/assets/gitGraph-4MIJSDKK-DtM1RZrd.js +1 -0
- package/dist/web/assets/gitGraphDiagram-X574FWY7-C7KXkLLU.js +106 -0
- package/dist/web/assets/golos-text-cyrillic-400-normal-BwL4n7Pb.woff +0 -0
- package/dist/web/assets/golos-text-cyrillic-400-normal-C7us6pn1.woff2 +0 -0
- package/dist/web/assets/golos-text-cyrillic-500-normal-BSLQUuP1.woff2 +0 -0
- package/dist/web/assets/golos-text-cyrillic-500-normal-hXinzVVQ.woff +0 -0
- package/dist/web/assets/golos-text-cyrillic-ext-400-normal-Bo67NYBq.woff2 +0 -0
- package/dist/web/assets/golos-text-cyrillic-ext-400-normal-D592xEk3.woff +0 -0
- package/dist/web/assets/golos-text-cyrillic-ext-500-normal-CLsu_eDI.woff2 +0 -0
- package/dist/web/assets/golos-text-cyrillic-ext-500-normal-InHNvB7v.woff +0 -0
- package/dist/web/assets/golos-text-latin-400-normal-Coi1FYaD.woff2 +0 -0
- package/dist/web/assets/golos-text-latin-400-normal-DOuJOmdK.woff +0 -0
- package/dist/web/assets/golos-text-latin-500-normal-BQo4s7Kn.woff +0 -0
- package/dist/web/assets/golos-text-latin-500-normal-BznAvurO.woff2 +0 -0
- package/dist/web/assets/golos-text-latin-ext-400-normal-BXgyX_iE.woff +0 -0
- package/dist/web/assets/golos-text-latin-ext-400-normal-BjKcJWKY.woff2 +0 -0
- package/dist/web/assets/golos-text-latin-ext-500-normal-9vZPKadr.woff2 +0 -0
- package/dist/web/assets/golos-text-latin-ext-500-normal-BmtMVntR.woff +0 -0
- package/dist/web/assets/graphlib-DS17s2tU.js +1 -0
- package/dist/web/assets/i18n-BmgFD4Gx.js +1 -0
- package/dist/web/assets/{index-JAYe_SFI.css → index-CgeU6Wuu.css} +1 -1
- package/dist/web/assets/index-CuqF4_ks.js +2 -0
- package/dist/web/assets/info-A6RAGUB7-C5tVl8cQ.js +1 -0
- package/dist/web/assets/infoDiagram-VRGFBTTK-DO4D3mI7.js +2 -0
- package/dist/web/assets/init-C-OQMol4.js +1 -0
- package/dist/web/assets/init-app-C0kkg1Ie.js +1 -0
- package/dist/web/assets/is-BB7rJzdU.js +1 -0
- package/dist/web/assets/ishikawaDiagram-OU5B5YK6-DA2H3Eih.js +70 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-ext-400-normal-Bh0R7Dhr.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-ext-400-normal-C_uLvvQ5.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-ext-500-normal-C9ShMxGR.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-ext-500-normal-CEVyPk4Y.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-ext-600-normal-DVvC-yKp.woff +0 -0
- package/dist/web/assets/jetbrains-mono-cyrillic-ext-600-normal-Dj6eZy5f.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-400-normal-ByoDsISC.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-500-normal-BvH7FW2L.woff2 +0 -0
- package/dist/web/assets/jetbrains-mono-vietnamese-600-normal-Dtizs43-.woff2 +0 -0
- package/dist/web/assets/journeyDiagram-ZHPQQLJL-CdPvR3Sr.js +139 -0
- package/dist/web/assets/kanban-definition-PNTS6WVX-BRIiazVf.js +89 -0
- package/dist/web/assets/katex-ZlcWpGUi.js +257 -0
- package/dist/web/assets/line-CmXhYhmh.js +1 -0
- package/dist/web/assets/linear-Cyx-ptw-.js +1 -0
- package/dist/web/assets/mermaid-parser.core-CZ05RARR.js +7 -0
- package/dist/web/assets/mermaid.core-ZosqiKX-.js +44 -0
- package/dist/web/assets/mindmap-definition-NLK3R4M7-a7nG9rPn.js +96 -0
- package/dist/web/assets/ordinal-BDEzSJ7C.js +1 -0
- package/dist/web/assets/packet-AYTQ26CC-yIJsAali.js +1 -0
- package/dist/web/assets/path-fybaL0A-.js +1 -0
- package/dist/web/assets/pegDiagram-GJSIUBJH-CBdB4i41.js +1 -0
- package/dist/web/assets/pie-WAS4IAKB-ybMEHYPX.js +1 -0
- package/dist/web/assets/pieDiagram-5QR66LMP-BfiG4Lho.js +39 -0
- package/dist/web/assets/pinia-DmTi2T44.js +1 -0
- package/dist/web/assets/pinia-r4XAEwbB.js +1 -0
- package/dist/web/assets/quadrantDiagram-O4NWA36T-7A6x_hoF.js +7 -0
- package/dist/web/assets/radar-RG4KPBEZ-BbeFIczu.js +1 -0
- package/dist/web/assets/railroad-74A4TZTK-CAyDA7Zz.js +1 -0
- package/dist/web/assets/railroad-abnf-HS5TGJTU-BzS6thC5.js +1 -0
- package/dist/web/assets/railroad-ebnf-LZEXJU2U-MS09sAPW.js +1 -0
- package/dist/web/assets/railroad-peg-WCYAUIDC-DEQwqbcE.js +1 -0
- package/dist/web/assets/railroadDiagram-XR7U4H2S-BXiOJ_AA.js +1 -0
- package/dist/web/assets/requirementDiagram-PLB6GJNP-bQNGcpuV.js +84 -0
- package/dist/web/assets/rough.esm-Dy-Kn_BL.js +1 -0
- package/dist/web/assets/runtime-core.esm-bundler-CUpq88z7.js +1 -0
- package/dist/web/assets/safe-storage-CqQMHcn7.js +1 -0
- package/dist/web/assets/sankeyDiagram-IPEJSGJF-CgfGV4TY.js +40 -0
- package/dist/web/assets/sequenceDiagram-PO4LG4MO-BdCuKkDZ.js +169 -0
- package/dist/web/assets/single-flight-r8B7stt1.js +1 -0
- package/dist/web/assets/sizeCapture-INFHLROL-B0uUizjq.js +1 -0
- package/dist/web/assets/src-AvcVSqt3.js +1 -0
- package/dist/web/assets/stateDiagram-v2-GCMORJYK-aj1Op7xi.js +291 -0
- package/dist/web/assets/swimlanes-2SLR337P-DQ_YYhZu.js +1 -0
- package/dist/web/assets/swimlanesDiagram-TC7HE7FX-9frEtrG_.js +8 -0
- package/dist/web/assets/theme-BSjPUW3f.js +1 -0
- package/dist/web/assets/timeline-definition-EJHVYXUP-Bk3Mvy3w.js +120 -0
- package/dist/web/assets/treeView-Q6P3EWNA-BzM7oZcT.js +1 -0
- package/dist/web/assets/treemap-WGGIJYW6-DzgzhZei.js +1 -0
- package/dist/web/assets/use-app-theme-DEiGbVuj.js +1 -0
- package/dist/web/assets/use-panel-width-ujFPWf-l.js +1 -0
- package/dist/web/assets/use-virtual-scroll-BVHTKnoC.js +1 -0
- package/dist/web/assets/usecaseDiagram-POWQR4AR-kriFnh7L.js +328 -0
- package/dist/web/assets/vennDiagram-UO4OBE2U-CDdCMQs7.js +34 -0
- package/dist/web/assets/{vue-i18n.runtime-CxBM6QPV.js → vue-i18n.runtime-D0pkhc7g.js} +2 -2
- package/dist/web/assets/wardley-WFR3VGLG-BUWTYQyf.js +1 -0
- package/dist/web/assets/wardleyDiagram-VNRHLVJA-CW7lMcTj.js +78 -0
- package/dist/web/assets/xychartDiagram-PMCCYNJV-DoIu8J_3.js +7 -0
- package/dist/web/index.html +8 -6
- package/package.json +12 -3
- package/dist/web/assets/AppPage-B9tWPrzn.js +0 -1
- package/dist/web/assets/CreateReviewFabButton-BqWdgfEf.css +0 -1
- package/dist/web/assets/CreateReviewFabButton-DcXpOlBP.js +0 -1
- package/dist/web/assets/IndexPage-CsgdLXZh.css +0 -1
- package/dist/web/assets/IndexPage-y9DcUqW9.js +0 -1
- package/dist/web/assets/MainLayout-BRPoh8y0.css +0 -1
- package/dist/web/assets/MainLayout-fQytypS5.js +0 -1
- package/dist/web/assets/Platform-DDGYwfzl.js +0 -1
- package/dist/web/assets/QBtn-DGiC5uLo.js +0 -1
- package/dist/web/assets/QTooltip-CPN6UvuX.js +0 -1
- package/dist/web/assets/ReviewDetailPage-CL6A5zjH.css +0 -1
- package/dist/web/assets/ReviewDetailPage-aMMuYtax.js +0 -1
- package/dist/web/assets/ReviewsPage-B1CTHU13.js +0 -1
- package/dist/web/assets/StatusSwitcher-Bj-XCwSr.js +0 -3
- package/dist/web/assets/StatusSwitcher-BydQq1LI.css +0 -1
- package/dist/web/assets/app-info-store-Dw8pPU-w.js +0 -1
- package/dist/web/assets/client-De34x_P_.js +0 -1
- package/dist/web/assets/engine-oniguruma-Bon2Icja.js +0 -1
- package/dist/web/assets/event-D0kaqRwq.js +0 -1
- package/dist/web/assets/format-Bb6ahGqi.js +0 -1
- package/dist/web/assets/i18n-Dw0eMaZf.js +0 -1
- package/dist/web/assets/index-i1Dlwasy.js +0 -2
- package/dist/web/assets/init-app-CPTVCTFc.js +0 -1
- package/dist/web/assets/pinia-ClnoFqXt.js +0 -1
- package/dist/web/assets/pinia-jMX0pzvu.js +0 -1
- package/dist/web/assets/runtime-core.esm-bundler-Db5Q9Gsb.js +0 -1
- package/dist/web/assets/theme-CgDsrcvS.js +0 -1
- package/dist/web/assets/use-app-theme-D0Pl-VfI.js +0 -1
- package/dist/web/assets/use-server-events-pJWjsMIC.js +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { NotFoundError } from "../errors/http.js";
|
|
1
2
|
import { Type } from '@sinclair/typebox';
|
|
2
3
|
import { getReview } from "../services/review.service.js";
|
|
3
|
-
import { createComment, getComments, editComment, removeComment, resolveComment, unresolveComment
|
|
4
|
+
import { createComment, getComments, editComment, removeComment, resolveComment, unresolveComment } from "../services/comment.service.js";
|
|
4
5
|
// Null goes first in each union: Fastify's Ajv coerces types, and with
|
|
5
6
|
// Integer (or String) first it turns a JSON null into 0 (or "") before the
|
|
6
7
|
// Null branch is ever tried — a file-level comment would be stored as line 0.
|
|
@@ -27,87 +28,36 @@ export async function commentRoutes(app, opts) {
|
|
|
27
28
|
const typedApp = app.withTypeProvider();
|
|
28
29
|
typedApp.post('/api/reviews/:id/comments', { schema: { params: ReviewIdParams, body: CreateCommentBody } }, async (request, reply) => {
|
|
29
30
|
if (!getReview(db, request.params.id)) {
|
|
30
|
-
|
|
31
|
-
return {
|
|
32
|
-
error: 'Not Found',
|
|
33
|
-
message: `Review ${request.params.id} not found`,
|
|
34
|
-
statusCode: 404
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
try {
|
|
38
|
-
const comment = createComment(db, request.params.id, request.body, eventBus);
|
|
39
|
-
reply.code(201);
|
|
40
|
-
return comment;
|
|
41
|
-
}
|
|
42
|
-
catch (error) {
|
|
43
|
-
if (error instanceof ValidationError) {
|
|
44
|
-
reply.code(400);
|
|
45
|
-
return {
|
|
46
|
-
error: 'Bad Request',
|
|
47
|
-
message: error.message,
|
|
48
|
-
statusCode: 400
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
throw error;
|
|
31
|
+
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
52
32
|
}
|
|
33
|
+
const comment = createComment(db, request.params.id, request.body, eventBus);
|
|
34
|
+
reply.code(201);
|
|
35
|
+
return comment;
|
|
53
36
|
});
|
|
54
|
-
typedApp.get('/api/reviews/:id/comments', { schema: { params: ReviewIdParams } }, async (request
|
|
37
|
+
typedApp.get('/api/reviews/:id/comments', { schema: { params: ReviewIdParams } }, async (request) => {
|
|
55
38
|
if (!getReview(db, request.params.id)) {
|
|
56
|
-
|
|
57
|
-
return {
|
|
58
|
-
error: 'Not Found',
|
|
59
|
-
message: `Review ${request.params.id} not found`,
|
|
60
|
-
statusCode: 404
|
|
61
|
-
};
|
|
39
|
+
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
62
40
|
}
|
|
63
41
|
return getComments(db, request.params.id);
|
|
64
42
|
});
|
|
65
|
-
typedApp.patch('/api/comments/:id', { schema: { params: CommentIdParams, body: UpdateCommentBody } }, async (request
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
reply.code(404);
|
|
70
|
-
return {
|
|
71
|
-
error: 'Not Found',
|
|
72
|
-
message: `Comment ${request.params.id} not found`,
|
|
73
|
-
statusCode: 404
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
return updated;
|
|
77
|
-
}
|
|
78
|
-
catch (error) {
|
|
79
|
-
if (error instanceof ValidationError) {
|
|
80
|
-
reply.code(400);
|
|
81
|
-
return {
|
|
82
|
-
error: 'Bad Request',
|
|
83
|
-
message: error.message,
|
|
84
|
-
statusCode: 400
|
|
85
|
-
};
|
|
86
|
-
}
|
|
87
|
-
throw error;
|
|
43
|
+
typedApp.patch('/api/comments/:id', { schema: { params: CommentIdParams, body: UpdateCommentBody } }, async (request) => {
|
|
44
|
+
const updated = editComment(db, request.params.id, request.body.content, eventBus);
|
|
45
|
+
if (!updated) {
|
|
46
|
+
throw new NotFoundError(`Comment ${request.params.id} not found`);
|
|
88
47
|
}
|
|
48
|
+
return updated;
|
|
89
49
|
});
|
|
90
|
-
typedApp.patch('/api/comments/:id/resolve', { schema: { params: CommentIdParams } }, async (request
|
|
50
|
+
typedApp.patch('/api/comments/:id/resolve', { schema: { params: CommentIdParams } }, async (request) => {
|
|
91
51
|
const updated = resolveComment(db, request.params.id, eventBus);
|
|
92
52
|
if (!updated) {
|
|
93
|
-
|
|
94
|
-
return {
|
|
95
|
-
error: 'Not Found',
|
|
96
|
-
message: `Comment ${request.params.id} not found`,
|
|
97
|
-
statusCode: 404
|
|
98
|
-
};
|
|
53
|
+
throw new NotFoundError(`Comment ${request.params.id} not found`);
|
|
99
54
|
}
|
|
100
55
|
return updated;
|
|
101
56
|
});
|
|
102
|
-
typedApp.patch('/api/comments/:id/unresolve', { schema: { params: CommentIdParams } }, async (request
|
|
57
|
+
typedApp.patch('/api/comments/:id/unresolve', { schema: { params: CommentIdParams } }, async (request) => {
|
|
103
58
|
const updated = unresolveComment(db, request.params.id, eventBus);
|
|
104
59
|
if (!updated) {
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
error: 'Not Found',
|
|
108
|
-
message: `Comment ${request.params.id} not found`,
|
|
109
|
-
statusCode: 404
|
|
110
|
-
};
|
|
60
|
+
throw new NotFoundError(`Comment ${request.params.id} not found`);
|
|
111
61
|
}
|
|
112
62
|
return updated;
|
|
113
63
|
});
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import { BadRequestError } from "../errors/http.js";
|
|
1
2
|
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { getStagedDiff, getUnstagedDiff, getBranchDiff, getCommitsDiff } from "../services/diff.service.js";
|
|
3
|
+
import { getStagedDiff, getUnstagedDiff, getBranchDiff, getCommitsDiff, getDiffSummaries, getFileDiff } from "../services/diff.service.js";
|
|
4
|
+
import { resolveWithinRepo } from "../services/git.service.js";
|
|
5
|
+
import { errorMessage } from "../../shared/utils/error-message.js";
|
|
3
6
|
const BranchQuery = Type.Object({
|
|
4
7
|
base: Type.String({ minLength: 1 })
|
|
5
8
|
});
|
|
@@ -7,6 +10,19 @@ const CommitsQuery = Type.Object({
|
|
|
7
10
|
from: Type.String({ minLength: 1 }),
|
|
8
11
|
to: Type.String({ minLength: 1 })
|
|
9
12
|
});
|
|
13
|
+
const SourceParams = Type.Object({
|
|
14
|
+
source: Type.Union([Type.Literal('staged'), Type.Literal('unstaged')])
|
|
15
|
+
});
|
|
16
|
+
const FileQuery = Type.Object({
|
|
17
|
+
path: Type.String({ minLength: 1 }),
|
|
18
|
+
oldPath: Type.Optional(Type.String({ minLength: 1 })),
|
|
19
|
+
status: Type.Union([
|
|
20
|
+
Type.Literal('added'),
|
|
21
|
+
Type.Literal('modified'),
|
|
22
|
+
Type.Literal('deleted'),
|
|
23
|
+
Type.Literal('renamed')
|
|
24
|
+
])
|
|
25
|
+
});
|
|
10
26
|
export async function diffRoutes(app, opts) {
|
|
11
27
|
const { repositoryPath } = opts;
|
|
12
28
|
const typedApp = app.withTypeProvider();
|
|
@@ -16,24 +32,41 @@ export async function diffRoutes(app, opts) {
|
|
|
16
32
|
typedApp.get('/api/diff/unstaged', async () => {
|
|
17
33
|
return getUnstagedDiff(repositoryPath);
|
|
18
34
|
});
|
|
19
|
-
|
|
35
|
+
// Files without hunks, then one file with hunks: the list stays small (ADR 0033).
|
|
36
|
+
typedApp.get('/api/diff/:source/files', { schema: { params: SourceParams } }, async (request) => getDiffSummaries(repositoryPath, request.params.source));
|
|
37
|
+
typedApp.get('/api/diff/:source/file', { schema: { params: SourceParams, querystring: FileQuery } }, async (request) => {
|
|
38
|
+
const { path, oldPath, status } = request.query;
|
|
39
|
+
// Only a path outside the repository is a bad request. Other errors stay server errors.
|
|
40
|
+
try {
|
|
41
|
+
resolveWithinRepo(repositoryPath, path);
|
|
42
|
+
if (oldPath !== undefined)
|
|
43
|
+
resolveWithinRepo(repositoryPath, oldPath);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
throw new BadRequestError(errorMessage(err), { cause: err });
|
|
47
|
+
}
|
|
48
|
+
return getFileDiff(repositoryPath, request.params.source, {
|
|
49
|
+
oldPath: oldPath ?? path,
|
|
50
|
+
newPath: path,
|
|
51
|
+
status
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
typedApp.get('/api/diff/branch', { schema: { querystring: BranchQuery } }, async (request) => {
|
|
20
55
|
try {
|
|
21
56
|
return await getBranchDiff(repositoryPath, request.query.base);
|
|
22
57
|
}
|
|
23
58
|
catch (err) {
|
|
24
|
-
reply.code(400);
|
|
25
59
|
const message = err instanceof Error ? err.message : 'Bad ref';
|
|
26
|
-
|
|
60
|
+
throw new BadRequestError(message, { cause: err });
|
|
27
61
|
}
|
|
28
62
|
});
|
|
29
|
-
typedApp.get('/api/diff/commits', { schema: { querystring: CommitsQuery } }, async (request
|
|
63
|
+
typedApp.get('/api/diff/commits', { schema: { querystring: CommitsQuery } }, async (request) => {
|
|
30
64
|
try {
|
|
31
65
|
return await getCommitsDiff(repositoryPath, request.query.from, request.query.to);
|
|
32
66
|
}
|
|
33
67
|
catch (err) {
|
|
34
|
-
reply.code(400);
|
|
35
68
|
const message = err instanceof Error ? err.message : 'Bad ref';
|
|
36
|
-
|
|
69
|
+
throw new BadRequestError(message, { cause: err });
|
|
37
70
|
}
|
|
38
71
|
});
|
|
39
72
|
}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import { safeSend } from "../utils/sse.js";
|
|
1
2
|
export async function eventRoutes(app, opts) {
|
|
2
3
|
const { eventBus, instanceId } = opts;
|
|
3
4
|
const hello = { instanceId };
|
|
4
5
|
app.get('/api/events', { sse: true }, async (request, reply) => {
|
|
5
6
|
reply.sse.keepAlive();
|
|
6
7
|
await reply.sse.send({ event: 'connected', data: hello });
|
|
8
|
+
// The client can leave during the await; a later `onClose` would never run.
|
|
9
|
+
if (!reply.sse.isConnected) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
7
12
|
const unsubscribe = eventBus.subscribe(event => {
|
|
8
|
-
|
|
13
|
+
safeSend(reply.sse, {
|
|
9
14
|
event: event.type,
|
|
10
15
|
data: { reviewId: event.reviewId }
|
|
11
16
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { exportAsJson, exportAsMarkdown
|
|
2
|
+
import { exportAsJson, exportAsMarkdown } from "../services/export.service.js";
|
|
3
3
|
const ExportParams = Type.Object({ id: Type.String() });
|
|
4
4
|
const ExportQuery = Type.Object({
|
|
5
5
|
format: Type.Union([Type.Literal('json'), Type.Literal('markdown')])
|
|
@@ -8,20 +8,11 @@ export async function exportRoutes(app, opts) {
|
|
|
8
8
|
const { db, repositoryPath } = opts;
|
|
9
9
|
const typedApp = app.withTypeProvider();
|
|
10
10
|
typedApp.get('/api/reviews/:id/export', { schema: { params: ExportParams, querystring: ExportQuery } }, async (request, reply) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
return exportAsJson(db, request.params.id);
|
|
14
|
-
}
|
|
15
|
-
const markdown = await exportAsMarkdown(db, request.params.id, repositoryPath);
|
|
16
|
-
reply.type('text/markdown');
|
|
17
|
-
return markdown;
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
if (error instanceof ExportNotFoundError) {
|
|
21
|
-
reply.code(404);
|
|
22
|
-
return { error: 'Not Found', message: error.message, statusCode: 404 };
|
|
23
|
-
}
|
|
24
|
-
throw error;
|
|
11
|
+
if (request.query.format === 'json') {
|
|
12
|
+
return exportAsJson(db, request.params.id);
|
|
25
13
|
}
|
|
14
|
+
const markdown = await exportAsMarkdown(db, request.params.id, repositoryPath);
|
|
15
|
+
reply.type('text/markdown');
|
|
16
|
+
return markdown;
|
|
26
17
|
});
|
|
27
18
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { BadRequestError } from "../errors/http.js";
|
|
1
2
|
import { Type } from '@sinclair/typebox';
|
|
2
3
|
import { getRepositoryInfo, stagePaths, unstagePaths, discardPaths, getFilesAtRef, getFileAtRef } from "../services/git.service.js";
|
|
3
4
|
const TreeParams = Type.Object({
|
|
@@ -20,61 +21,52 @@ export async function gitRoutes(app, opts) {
|
|
|
20
21
|
app.get('/api/git/info', async () => {
|
|
21
22
|
return getRepositoryInfo(repositoryPath);
|
|
22
23
|
});
|
|
23
|
-
typedApp.post('/api/git/stage', { schema: { body: OptionalPathsBody } }, async (request
|
|
24
|
+
typedApp.post('/api/git/stage', { schema: { body: OptionalPathsBody } }, async (request) => {
|
|
24
25
|
try {
|
|
25
26
|
await stagePaths(repositoryPath, request.body.paths ?? []);
|
|
26
27
|
return { ok: true };
|
|
27
28
|
}
|
|
28
29
|
catch (err) {
|
|
29
|
-
reply.code(400);
|
|
30
30
|
const message = err instanceof Error ? err.message : 'Failed to stage';
|
|
31
|
-
|
|
31
|
+
throw new BadRequestError(message, { cause: err });
|
|
32
32
|
}
|
|
33
33
|
});
|
|
34
|
-
typedApp.post('/api/git/unstage', { schema: { body: OptionalPathsBody } }, async (request
|
|
34
|
+
typedApp.post('/api/git/unstage', { schema: { body: OptionalPathsBody } }, async (request) => {
|
|
35
35
|
try {
|
|
36
36
|
await unstagePaths(repositoryPath, request.body.paths ?? []);
|
|
37
37
|
return { ok: true };
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
|
-
reply.code(400);
|
|
41
40
|
const message = err instanceof Error ? err.message : 'Failed to unstage';
|
|
42
|
-
|
|
41
|
+
throw new BadRequestError(message, { cause: err });
|
|
43
42
|
}
|
|
44
43
|
});
|
|
45
|
-
typedApp.post('/api/git/discard', { schema: { body: DiscardBody } }, async (request
|
|
44
|
+
typedApp.post('/api/git/discard', { schema: { body: DiscardBody } }, async (request) => {
|
|
46
45
|
try {
|
|
47
46
|
await discardPaths(repositoryPath, request.body.paths);
|
|
48
47
|
return { ok: true };
|
|
49
48
|
}
|
|
50
49
|
catch (err) {
|
|
51
|
-
reply.code(400);
|
|
52
50
|
const message = err instanceof Error ? err.message : 'Failed to discard changes';
|
|
53
|
-
|
|
51
|
+
throw new BadRequestError(message, { cause: err });
|
|
54
52
|
}
|
|
55
53
|
});
|
|
56
|
-
typedApp.get('/api/git/tree/:ref', { schema: { params: TreeParams } }, async (request
|
|
54
|
+
typedApp.get('/api/git/tree/:ref', { schema: { params: TreeParams } }, async (request) => {
|
|
57
55
|
const { ref } = request.params;
|
|
58
56
|
try {
|
|
59
57
|
const files = await getFilesAtRef(repositoryPath, ref);
|
|
60
58
|
return { ref, files };
|
|
61
59
|
}
|
|
62
60
|
catch (err) {
|
|
63
|
-
reply.code(400);
|
|
64
61
|
const message = err instanceof Error ? err.message : 'Failed to list files';
|
|
65
|
-
|
|
62
|
+
throw new BadRequestError(message, { cause: err });
|
|
66
63
|
}
|
|
67
64
|
});
|
|
68
|
-
typedApp.get('/api/git/file/*', { schema: { querystring: FileContentQuery } }, async (request
|
|
65
|
+
typedApp.get('/api/git/file/*', { schema: { querystring: FileContentQuery } }, async (request) => {
|
|
69
66
|
const filePath = request.params['*'];
|
|
70
67
|
const { ref } = request.query;
|
|
71
68
|
if (!filePath) {
|
|
72
|
-
|
|
73
|
-
return {
|
|
74
|
-
error: 'Bad Request',
|
|
75
|
-
message: 'File path is required',
|
|
76
|
-
statusCode: 400
|
|
77
|
-
};
|
|
69
|
+
throw new BadRequestError('File path is required');
|
|
78
70
|
}
|
|
79
71
|
let content;
|
|
80
72
|
let isBinary;
|
|
@@ -83,9 +75,8 @@ export async function gitRoutes(app, opts) {
|
|
|
83
75
|
({ content, isBinary } = await getFileAtRef(repositoryPath, ref, filePath));
|
|
84
76
|
}
|
|
85
77
|
catch (err) {
|
|
86
|
-
reply.code(400);
|
|
87
78
|
const message = err instanceof Error ? err.message : 'Failed to read file';
|
|
88
|
-
|
|
79
|
+
throw new BadRequestError(message, { cause: err });
|
|
89
80
|
}
|
|
90
81
|
const lines = isBinary ? [] : content.split('\n');
|
|
91
82
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { NotFoundError, BadRequestError } from "../errors/http.js";
|
|
1
2
|
import { Type } from '@sinclair/typebox';
|
|
2
|
-
import { createReview, getReview, getReviews, setReviewStatus, removeReview
|
|
3
|
+
import { createReview, getReview, getReviews, setReviewStatus, removeReview } from "../services/review.service.js";
|
|
3
4
|
import { getRepositoryInfo } from "../services/git.service.js";
|
|
4
5
|
const CreateReviewBody = Type.Object({
|
|
5
6
|
sourceType: Type.Optional(Type.Union([
|
|
@@ -33,22 +34,9 @@ export async function reviewRoutes(app, opts) {
|
|
|
33
34
|
const { repositoryPath, db, eventBus } = opts;
|
|
34
35
|
const typedApp = app.withTypeProvider();
|
|
35
36
|
typedApp.post('/api/reviews', { schema: { body: CreateReviewBody } }, async (request, reply) => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return review;
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
if (error instanceof ValidationError) {
|
|
43
|
-
reply.code(400);
|
|
44
|
-
return {
|
|
45
|
-
error: 'Bad Request',
|
|
46
|
-
message: error.message,
|
|
47
|
-
statusCode: 400
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
throw error;
|
|
51
|
-
}
|
|
37
|
+
const review = await createReview(db, repositoryPath, request.body, eventBus);
|
|
38
|
+
reply.code(201);
|
|
39
|
+
return review;
|
|
52
40
|
});
|
|
53
41
|
typedApp.get('/api/reviews', { schema: { querystring: ReviewsQuery } }, async (request) => {
|
|
54
42
|
const { branch, search, createdFrom, createdTo, files } = request.query;
|
|
@@ -63,46 +51,26 @@ export async function reviewRoutes(app, opts) {
|
|
|
63
51
|
: undefined
|
|
64
52
|
});
|
|
65
53
|
});
|
|
66
|
-
typedApp.get('/api/reviews/:id', { schema: { params: ReviewIdParams } }, async (request
|
|
54
|
+
typedApp.get('/api/reviews/:id', { schema: { params: ReviewIdParams } }, async (request) => {
|
|
67
55
|
const review = getReview(db, request.params.id);
|
|
68
56
|
if (!review) {
|
|
69
|
-
|
|
70
|
-
return {
|
|
71
|
-
error: 'Not Found',
|
|
72
|
-
message: `Review ${request.params.id} not found`,
|
|
73
|
-
statusCode: 404
|
|
74
|
-
};
|
|
57
|
+
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
75
58
|
}
|
|
76
59
|
return review;
|
|
77
60
|
});
|
|
78
|
-
typedApp.patch('/api/reviews/:id', { schema: { params: ReviewIdParams, body: UpdateReviewBody } }, async (request
|
|
61
|
+
typedApp.patch('/api/reviews/:id', { schema: { params: ReviewIdParams, body: UpdateReviewBody } }, async (request) => {
|
|
79
62
|
if (!request.body.status) {
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
error: 'Bad Request',
|
|
83
|
-
message: '"status" is required',
|
|
84
|
-
statusCode: 400
|
|
85
|
-
};
|
|
63
|
+
throw new BadRequestError('"status" is required');
|
|
86
64
|
}
|
|
87
65
|
const updated = setReviewStatus(db, request.params.id, request.body.status, eventBus);
|
|
88
66
|
if (!updated) {
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
error: 'Not Found',
|
|
92
|
-
message: `Review ${request.params.id} not found`,
|
|
93
|
-
statusCode: 404
|
|
94
|
-
};
|
|
67
|
+
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
95
68
|
}
|
|
96
69
|
return updated;
|
|
97
70
|
});
|
|
98
71
|
typedApp.delete('/api/reviews/:id', { schema: { params: ReviewIdParams } }, async (request, reply) => {
|
|
99
72
|
if (!getReview(db, request.params.id)) {
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
error: 'Not Found',
|
|
103
|
-
message: `Review ${request.params.id} not found`,
|
|
104
|
-
statusCode: 404
|
|
105
|
-
};
|
|
73
|
+
throw new NotFoundError(`Review ${request.params.id} not found`);
|
|
106
74
|
}
|
|
107
75
|
removeReview(db, request.params.id, eventBus);
|
|
108
76
|
reply.code(204);
|