collabmd 0.1.44 → 0.1.46
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 +39 -19
- package/bin/collabmd.js +27 -1
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CYM5nxUi.js +1 -0
- package/dist/client/assets/browser-utils--H_DkyEF.js +1 -0
- package/dist/client/assets/chunk-ERSWUTIN-hHdvnhaY.js +41 -0
- package/dist/client/assets/{chunk-CZQZEIFA-DT3N9fo7.js → chunk-YGJVY4SA-CjSe7Bx7.js} +2 -2
- package/dist/client/assets/comment-thread-store-CdcwrObc.js +1 -0
- package/dist/client/assets/comment-threads-9J6Em-_y.js +4 -0
- package/dist/client/assets/comment-ui-shared-BC4pt4w8.js +7 -0
- package/dist/client/assets/dist-B5HTH2BO.js +1 -0
- package/dist/client/assets/{dist-DhLvC8DS.js → dist-B5Jf1SLZ.js} +2 -2
- package/dist/client/assets/{dist-CnL7Y2sM.js → dist-BDt3rz7l.js} +1 -1
- package/dist/client/assets/dist-BEwK4csK.js +1 -0
- package/dist/client/assets/dist-BKW4PALF.js +1 -0
- package/dist/client/assets/dist-BWcHHOx-.js +1 -0
- package/dist/client/assets/{dist-CYZmbokb.js → dist-BalScOwZ.js} +9 -9
- package/dist/client/assets/dist-Bf8f4HmM.js +6 -0
- package/dist/client/assets/dist-BjHJDtyU.js +1 -0
- package/dist/client/assets/dist-Bm6pf9Bl.js +1 -0
- package/dist/client/assets/dist-BoVBdSjk.js +1 -0
- package/dist/client/assets/dist-CC5IrQMG.js +1 -0
- package/dist/client/assets/{dist-C11hHqv_.js → dist-CCP_AKPG.js} +12 -12
- package/dist/client/assets/dist-CZgOmxK6.js +1 -0
- package/dist/client/assets/{dist-D2AQ-76g.js → dist-Cqhv5LAk.js} +1 -1
- package/dist/client/assets/{dist-MvcV4190.js → dist-D-Z6FLem.js} +1 -1
- package/dist/client/assets/dist-DLMiIsNC.js +11 -0
- package/dist/client/assets/{dist-3bgCp7iY.js → dist-DZKu9Cs8.js} +1 -1
- package/dist/client/assets/dist-DaYdhcVs.js +1 -0
- package/dist/client/assets/dist-FWJsgGYI.js +1 -0
- package/dist/client/assets/dist-Kh2COra_.js +1 -0
- package/dist/client/assets/{dist-JYEDRrPT.js → dist-PDZ3bSqm.js} +1 -1
- package/dist/client/assets/{dist-Drp8uu5J.js → dist-TjMh8n1u.js} +1 -1
- package/dist/client/assets/{dist-DGb27Dl6.js → dist-dtAdgL39.js} +1 -1
- package/dist/client/assets/dist-jv4WdPnK.js +9 -0
- package/dist/client/assets/{dist-DK5Gs2wa.js → dist-rGBmqxC7.js} +1 -1
- package/dist/client/assets/{dist-DCzuX1AF.js → dist-shrl6K_0.js} +1 -1
- package/dist/client/assets/dist-tOaegDYS2.js +1 -0
- package/dist/client/assets/dist-ule2n6ak.js +1 -0
- package/dist/client/assets/dist-xElaxbia.js +1 -0
- package/dist/client/assets/drawio-editor-FzY1cVw8.js +9 -0
- package/dist/client/assets/drawioEditor-BjiKJqlc.js +2 -0
- package/dist/client/assets/editor-session-Cm9ekeNo.js +54 -0
- package/dist/client/assets/embedded-editor-base-CwTkk911.css +1 -0
- package/dist/client/assets/{en-GADW4C6J-DQ6zyCEM.js → en-7NFCYGKX-BrgYZAEe.js} +1 -1
- package/dist/client/assets/{preview-render-profile-BZzGSg6K.js → es-DXGV3RHd.js} +13 -13
- package/dist/client/assets/excalidraw-editor-B46zsTFI.js +9 -0
- package/dist/client/assets/excalidraw-editor-Bt7gMSoh.css +1 -0
- package/dist/client/assets/excalidraw-scene-BOben2gr.js +1 -0
- package/dist/client/assets/excalidrawEditor-CT2pHq7e.js +2 -0
- package/dist/client/assets/export-page-A7NHawJY.js +39 -0
- package/dist/client/assets/exportDocument-B2L8fe34.css +1 -0
- package/dist/client/assets/exportDocument-BaqcOAAW.js +2 -0
- package/dist/client/assets/file-icon-svg-CQAjlWjK.js +1 -0
- package/dist/client/assets/file-kind-BbqgGzpc.js +1 -0
- package/dist/client/assets/image-QXRYHUNP-SWEV8H0J.js +1 -0
- package/dist/client/assets/index-ClbjMBVt.js +2 -0
- package/dist/client/assets/index-Pum8ET-5.css +1 -0
- package/dist/client/assets/main-B3uPyqic.js +893 -0
- package/dist/client/assets/modulepreload-polyfill-P2Xu9kJm.js +1 -0
- package/dist/client/assets/pdf-BL9Pr2D5.js +54 -0
- package/dist/client/assets/pdf.worker-CLesOks4.mjs +64857 -0
- package/dist/client/assets/pdf_viewer-FVkkThnz.js +551 -0
- package/dist/client/assets/preview-diagram-utils-txBCoW8d.js +1 -0
- package/dist/client/assets/{preview-render-compiler-BSaQO_p3.js → preview-render-compiler-BD-XhkS2.js} +2 -2
- package/dist/client/assets/preview-render-compiler-BKedaMxx.js +1 -0
- package/dist/client/assets/preview-render-profile-CE-5rVTC.js +1 -0
- package/dist/client/assets/{preview-render-worker-B42LWvrD.js → preview-render-worker-CiPqc9n6.js} +20 -20
- package/dist/client/assets/prod-Dy20X7eT.js +1 -0
- package/dist/client/assets/prod-hnwh7wRM.js +177 -0
- package/dist/client/assets/quick-switcher-controller-fqOYR7qR.js +12 -0
- package/dist/client/assets/runtime-config-BLrEKVO1.js +1 -0
- package/dist/client/assets/vault-api-client-_LpV1aIn.js +6 -0
- package/dist/client/assets/{yjs-provider-reset-guard-Cox4nxwu.js → yjs-provider-reset-guard-DE0vINDP.js} +1 -1
- package/dist/client/drawio-editor.html +3 -2
- package/dist/client/excalidraw-editor.html +3 -2
- package/dist/client/export-document.html +4 -3
- package/dist/client/index.html +109 -27
- package/docker-compose.yml +42 -0
- package/package.json +26 -15
- package/scripts/local-structurizr-compose.mjs +101 -0
- package/src/client/app/drawio-editor-entry.js +0 -3
- package/src/client/app/drawio-editor.html +1 -0
- package/src/client/app/excalidraw-editor-entry.js +0 -3
- package/src/client/app/excalidraw-editor.html +1 -0
- package/src/client/app/export-document-entry.js +0 -3
- package/src/client/app/export-document.html +1 -0
- package/src/client/app/index.html +106 -24
- package/src/client/app/main-entry.js +0 -5
- package/src/client/application/app-shell/chat-feature.js +180 -124
- package/src/client/application/app-shell/comments-feature.js +20 -11
- package/src/client/application/app-shell/git-feature.js +16 -9
- package/src/client/application/app-shell/presence-feature.js +18 -11
- package/src/client/application/app-shell/ui-feature-shell.js +78 -17
- package/src/client/application/app-shell/ui-feature-sidebar.js +74 -1
- package/src/client/application/app-shell/ui-feature-tab-activity.js +2 -2
- package/src/client/application/app-shell/ui-feature-toolbar.js +2 -52
- package/src/client/application/app-shell/workspace-feature.js +27 -84
- package/src/client/application/app-shell-elements.js +9 -1
- package/src/client/application/diagram-chrome.js +161 -166
- package/src/client/application/diagram-preview-export.js +7 -34
- package/src/client/application/diagram-preview-hydrator.js +1 -19
- package/src/client/application/markdown-frontmatter.js +0 -2
- package/src/client/application/mermaid-preview-hydrator.js +1 -14
- package/src/client/application/pdf-preview-controller.js +992 -0
- package/src/client/application/plantuml-preview-hydrator.js +0 -10
- package/src/client/application/preview-diagram-utils.js +31 -2
- package/src/client/application/preview-render-compiler.js +2 -1
- package/src/client/application/preview-renderer.js +2 -2
- package/src/client/application/quick-switcher-loader.js +3 -1
- package/src/client/application/structurizr-preview-controller.js +395 -0
- package/src/client/application/workspace-coordinator.js +25 -3
- package/src/client/application/workspace-preview-controller.js +91 -5
- package/src/client/bootstrap/collabmd-app-shell.js +155 -43
- package/src/client/browser-utils.js +44 -0
- package/src/client/domain/excalidraw-collaboration.js +0 -2
- package/src/client/domain/room.js +38 -0
- package/src/client/domain/runtime-paths.js +26 -5
- package/src/client/domain/structurizr-language.js +99 -0
- package/src/client/domain/vault-paths.js +48 -0
- package/src/client/excalidraw-editor.js +833 -51
- package/src/client/export/export-pipeline.js +6 -34
- package/src/client/infrastructure/auth-client.js +4 -10
- package/src/client/infrastructure/browser-notification-port.js +33 -20
- package/src/client/infrastructure/browser-preferences-port.js +46 -9
- package/src/client/infrastructure/comment-thread-store.js +34 -13
- package/src/client/infrastructure/editor-session.js +10 -0
- package/src/client/infrastructure/editor-view-adapter.js +133 -5
- package/src/client/infrastructure/excalidraw-room-client.js +103 -14
- package/src/client/infrastructure/git-api-client.js +5 -0
- package/src/client/infrastructure/lobby-presence.js +1 -0
- package/src/client/infrastructure/vault-api-client.js +51 -0
- package/src/client/infrastructure/workspace-sync-client.js +13 -1
- package/src/client/presentation/comment-overview-controller.js +18 -53
- package/src/client/presentation/comment-ui/comment-ui-card.js +1 -12
- package/src/client/presentation/comment-ui/comment-ui-layout.js +242 -219
- package/src/client/presentation/comment-ui/comment-ui-render.js +32 -45
- package/src/client/presentation/comment-ui/comment-ui-shared.js +68 -13
- package/src/client/presentation/comment-ui-controller.js +5 -1
- package/src/client/presentation/drawio-embed-controller.js +9 -102
- package/src/client/presentation/drawio-viewer.js +118 -0
- package/src/client/presentation/excalidraw-embed-controller.js +74 -8
- package/src/client/presentation/file-action-controller.js +134 -0
- package/src/client/presentation/file-explorer-controller.js +33 -3
- package/src/client/presentation/file-explorer-view.js +59 -34
- package/src/client/presentation/file-icon-svg.js +40 -0
- package/src/client/presentation/file-tree-state.js +12 -4
- package/src/client/presentation/git-diff-view-controller.js +731 -3
- package/src/client/presentation/outline-controller.js +86 -0
- package/src/client/presentation/quick-switcher-controller.js +257 -74
- package/src/client/presentation/quick-switcher-text-search.js +3 -3
- package/src/client/presentation/toast-controller.js +3 -0
- package/src/client/styles/components/feedback.css +6 -0
- package/src/client/styles/components/navigation.css +11 -1
- package/src/client/styles/features/collaboration-chat.css +49 -53
- package/src/client/styles/features/collaboration-presence.css +9 -7
- package/src/client/styles/features/comment-card.css +25 -11
- package/src/client/styles/features/comment-overlays.css +0 -4
- package/src/client/styles/features/comment-overview.css +44 -21
- package/src/client/styles/features/comments-drawer.css +2 -100
- package/src/client/styles/features/diagram-preview.css +81 -18
- package/src/client/styles/features/diff-special.css +309 -0
- package/src/client/styles/features/embedded-preview.css +8 -33
- package/src/client/styles/features/quick-switcher.css +8 -0
- package/src/client/styles/foundation/themes.css +6 -0
- package/src/client/styles/foundation/tokens.css +1 -0
- package/src/client/styles/layout/editor-page.css +40 -0
- package/src/client/styles/layout/responsive.css +31 -1
- package/src/client/styles/layout/shell.css +383 -7
- package/src/client/styles/layout/sidebar.css +47 -15
- package/src/client/styles/layout/view-modes.css +1 -0
- package/src/client/styles/overrides/codemirror.css +217 -15
- package/src/client/styles/style.css +1 -0
- package/src/client/styles/surfaces/excalidraw-editor.css +294 -0
- package/src/domain/comment-threads.js +63 -5
- package/src/domain/excalidraw-room-codec.js +6 -4
- package/src/domain/file-kind.js +35 -3
- package/src/server/application/workspace-reconciliation.js +1 -1
- package/src/server/config/env.js +79 -0
- package/src/server/create-app-server.js +14 -3
- package/src/server/domain/bases/base-definition.js +1 -1
- package/src/server/domain/bases/base-expression-runtime.js +1 -4
- package/src/server/domain/bases/base-index-snapshot-store.js +1 -4
- package/src/server/domain/bases/base-query-service.js +1 -77
- package/src/server/domain/bases/base-transform.js +1 -4
- package/src/server/domain/collaboration/collaboration-room.js +55 -21
- package/src/server/domain/comment-overview.js +25 -8
- package/src/server/domain/docx-exporter.js +24 -33
- package/src/server/domain/ripgrep-search-service.js +7 -1
- package/src/server/domain/workspace-state.js +32 -11
- package/src/server/infrastructure/git/diff-service.js +29 -13
- package/src/server/infrastructure/git/git-service.js +61 -10
- package/src/server/infrastructure/git/history-service.js +96 -29
- package/src/server/infrastructure/git/image-mime.js +14 -0
- package/src/server/infrastructure/git/path-utils.js +1 -1
- package/src/server/infrastructure/git/responses.js +0 -95
- package/src/server/infrastructure/git/untracked-files.js +28 -1
- package/src/server/infrastructure/github/github-app-client.js +22 -15
- package/src/server/infrastructure/http/create-git-api-query-handler.js +31 -1
- package/src/server/infrastructure/http/create-request-handler.js +18 -31
- package/src/server/infrastructure/http/create-static-handler.js +5 -2
- package/src/server/infrastructure/http/create-structurizr-api-handler.js +145 -0
- package/src/server/infrastructure/http/create-vault-api-command-handler.js +70 -8
- package/src/server/infrastructure/http/create-vault-api-query-handler.js +51 -3
- package/src/server/infrastructure/http/http-errors.js +7 -1
- package/src/server/infrastructure/http/http-request-helpers.js +22 -0
- package/src/server/infrastructure/persistence/path-utils.js +1 -1
- package/src/server/infrastructure/persistence/vault-file-store.js +3 -0
- package/src/server/infrastructure/structurizr/structurizr-embed.css +43 -0
- package/src/server/infrastructure/structurizr/structurizr-workspace-service.js +374 -0
- package/src/server/infrastructure/websocket/attach-collaboration-gateway.js +4 -22
- package/dist/client/assets/CodeMirrorEditor-UCMRAMCV-CbSISC1w.js +0 -1
- package/dist/client/assets/bases-preview-controller-2pLHk0uk.js +0 -143
- package/dist/client/assets/browser-utils-CKtI_rZl.js +0 -1
- package/dist/client/assets/button-BCEiw4Rw.js +0 -1
- package/dist/client/assets/chunk-AXR3VHVW-0GTk_evH.js +0 -41
- package/dist/client/assets/comment-threads-D-iKphIp.js +0 -4
- package/dist/client/assets/diagram-action-icons-aa_KOv9h.js +0 -36
- package/dist/client/assets/dist-BH87JoHx.js +0 -11
- package/dist/client/assets/dist-BIkPRZO7.js +0 -6
- package/dist/client/assets/dist-BqCsFpx4.js +0 -1
- package/dist/client/assets/dist-C2JHCt8U.js +0 -1
- package/dist/client/assets/dist-CZjTtmTu.js +0 -1
- package/dist/client/assets/dist-Cig4Ov_X.js +0 -1
- package/dist/client/assets/dist-CqQEC8gR.js +0 -1
- package/dist/client/assets/dist-D4Rzqod1.js +0 -1
- package/dist/client/assets/dist-D5sn-mwk.js +0 -9
- package/dist/client/assets/dist-D7G-n87v.js +0 -1
- package/dist/client/assets/dist-DRqOrNgN.js +0 -1
- package/dist/client/assets/dist-DetkMS_4.js +0 -1
- package/dist/client/assets/dist-Dp6FVkQ1.js +0 -1
- package/dist/client/assets/dist-DtbjNB28.js +0 -1
- package/dist/client/assets/dist-DyrYcvpo2.js +0 -1
- package/dist/client/assets/dist-GrygD2zE.js +0 -1
- package/dist/client/assets/dist-HmEpWUow.js +0 -1
- package/dist/client/assets/dist-RTa81Km7.js +0 -1
- package/dist/client/assets/drawio-editor-DDvFoarP.js +0 -9
- package/dist/client/assets/drawio-embed-controller-BD1GTRwn.js +0 -1
- package/dist/client/assets/drawioEditor-Bu5grlH5.js +0 -2
- package/dist/client/assets/editor-session-DGlpiAsK.js +0 -16
- package/dist/client/assets/embedded-editor-base-C9roKYpW.css +0 -1
- package/dist/client/assets/excalidraw-editor-BHu6k8d4.js +0 -9
- package/dist/client/assets/excalidraw-editor-Cr_184EF.css +0 -1
- package/dist/client/assets/excalidraw-embed-controller-y2fenN_g.js +0 -1
- package/dist/client/assets/excalidraw-embed-reconciler-DGiagExg.js +0 -1
- package/dist/client/assets/excalidraw-scene-BvsQ6mLP.js +0 -176
- package/dist/client/assets/excalidrawEditor-4vFE--Uw.js +0 -2
- package/dist/client/assets/export-page-Cby8JzPO.js +0 -39
- package/dist/client/assets/exportDocument-C9v-bc7I.css +0 -1
- package/dist/client/assets/exportDocument-CRHCfsdo.js +0 -2
- package/dist/client/assets/file-history-view-controller-BDjEV9xJ.js +0 -112
- package/dist/client/assets/file-kind-BKPOIRPE.js +0 -1
- package/dist/client/assets/git-diff-view-controller-DgdM1aqb.js +0 -150
- package/dist/client/assets/git-panel-controller-BDTR2HVp.js +0 -207
- package/dist/client/assets/image-XGD7JQR6-XDUWHRpO.js +0 -1
- package/dist/client/assets/index-7xOyXDhP.css +0 -1
- package/dist/client/assets/index-Bz1KuQ6y.js +0 -2
- package/dist/client/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/client/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/client/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/client/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/client/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/client/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/client/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-BEIGL1Tu.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-cyrillic-400-normal-ugxPyKxw.woff +0 -0
- package/dist/client/assets/jetbrains-mono-greek-400-normal-B9oWc5Lo.woff +0 -0
- package/dist/client/assets/jetbrains-mono-greek-400-normal-C190GLew.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-400-normal-6-qcROiO.woff +0 -0
- package/dist/client/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
- package/dist/client/assets/jetbrains-mono-latin-ext-400-normal-fXTG6kC5.woff +0 -0
- package/dist/client/assets/jetbrains-mono-vietnamese-400-normal-CqNFfHCs.woff +0 -0
- package/dist/client/assets/main-Ci07m9oP.js +0 -152
- package/dist/client/assets/preview-diagram-utils-n_bbJA9V.js +0 -1
- package/dist/client/assets/preview-render-compiler-csGM5ZFz.js +0 -1
- package/dist/client/assets/quick-switcher-controller-CoiYvllm.js +0 -12
- package/dist/client/assets/room-CDrmIdDt.js +0 -1
- package/dist/client/assets/runtime-config-CDgeuPWN.js +0 -1
- package/dist/client/assets/runtime-config-loader-BHGniHP5.js +0 -1
- package/dist/client/assets/runtime-paths-DvpihCEq.js +0 -1
- package/dist/client/assets/segmented-control-DJrHSz8M.js +0 -1
- package/dist/client/assets/vault-api-client-sLB2bo56.js +0 -1
- package/dist/client/assets/vault-paths-CVkuh0j-.js +0 -5
- package/src/client/bootstrap/lazy-controller-feature.js +0 -433
- package/src/client/infrastructure/backlinks-api-client.js +0 -16
- package/src/client/infrastructure/plantuml-api-client.js +0 -19
- package/src/client/infrastructure/runtime-config-loader.js +0 -56
- package/src/server/domain/collaboration/room-persistence-controller.js +0 -80
- package/src/server/infrastructure/git/command-runner.js +0 -49
- package/src/server/infrastructure/git/errors.js +0 -8
- package/src/server/infrastructure/http/create-esm-proxy-handler.js +0 -130
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { createGitRequestError } from '
|
|
1
|
+
import { createRequestError as createGitRequestError } from '../http/http-errors.js';
|
|
2
2
|
import { getVaultFileKind, isImageAttachmentFilePath } from '../../../domain/file-kind.js';
|
|
3
3
|
import { normalizeRelativeGitPath } from './path-utils.js';
|
|
4
|
+
import { getImageMimeType } from './image-mime.js';
|
|
4
5
|
import {
|
|
5
6
|
countPatchLines,
|
|
6
7
|
parseGitLogHeader,
|
|
@@ -9,11 +10,7 @@ import {
|
|
|
9
10
|
parseUnifiedDiff,
|
|
10
11
|
} from './parsers.js';
|
|
11
12
|
import {
|
|
12
|
-
createCommitDiffResponse,
|
|
13
13
|
createEmptyStats,
|
|
14
|
-
createFileHistoryResponse,
|
|
15
|
-
createFileSnapshotResponse,
|
|
16
|
-
createHistoryResponse,
|
|
17
14
|
} from './responses.js';
|
|
18
15
|
|
|
19
16
|
const EMPTY_TREE_HASH = '4b825dc642cb6eb9a060e54bf8d69288fbee4904';
|
|
@@ -127,13 +124,13 @@ export class GitHistoryService {
|
|
|
127
124
|
const normalizedOffset = normalizeHistoryOffset(offset);
|
|
128
125
|
|
|
129
126
|
if (!isGitRepo) {
|
|
130
|
-
return
|
|
127
|
+
return {
|
|
131
128
|
commits: [],
|
|
132
129
|
hasMore: false,
|
|
133
130
|
isGitRepo: false,
|
|
134
131
|
limit: normalizedLimit,
|
|
135
132
|
offset: normalizedOffset,
|
|
136
|
-
}
|
|
133
|
+
};
|
|
137
134
|
}
|
|
138
135
|
|
|
139
136
|
const cacheKey = JSON.stringify({
|
|
@@ -149,12 +146,13 @@ export class GitHistoryService {
|
|
|
149
146
|
return this.runRequest(cacheKey, async () => {
|
|
150
147
|
const hasHeadCommit = await this.hasHeadCommit();
|
|
151
148
|
if (!hasHeadCommit) {
|
|
152
|
-
return setCachedValue(this.historyCache, cacheKey,
|
|
149
|
+
return setCachedValue(this.historyCache, cacheKey, {
|
|
153
150
|
commits: [],
|
|
154
151
|
hasMore: false,
|
|
152
|
+
isGitRepo: true,
|
|
155
153
|
limit: normalizedLimit,
|
|
156
154
|
offset: normalizedOffset,
|
|
157
|
-
}
|
|
155
|
+
}, this.responseCacheTtlMs);
|
|
158
156
|
}
|
|
159
157
|
|
|
160
158
|
const output = await this.commandRunner.execGit([
|
|
@@ -175,12 +173,13 @@ export class GitHistoryService {
|
|
|
175
173
|
.map((chunk) => this.parseHistoryChunk(chunk));
|
|
176
174
|
|
|
177
175
|
const hasMore = commits.length > normalizedLimit;
|
|
178
|
-
const response =
|
|
176
|
+
const response = {
|
|
179
177
|
commits: commits.slice(0, normalizedLimit),
|
|
180
178
|
hasMore,
|
|
179
|
+
isGitRepo: true,
|
|
181
180
|
limit: normalizedLimit,
|
|
182
181
|
offset: normalizedOffset,
|
|
183
|
-
}
|
|
182
|
+
};
|
|
184
183
|
|
|
185
184
|
return setCachedValue(this.historyCache, cacheKey, response, this.responseCacheTtlMs);
|
|
186
185
|
});
|
|
@@ -193,14 +192,14 @@ export class GitHistoryService {
|
|
|
193
192
|
const normalizedPath = normalizeRelativeGitPath(path);
|
|
194
193
|
|
|
195
194
|
if (!isGitRepo) {
|
|
196
|
-
return
|
|
195
|
+
return {
|
|
197
196
|
commits: [],
|
|
198
197
|
hasMore: false,
|
|
199
198
|
isGitRepo: false,
|
|
200
199
|
limit: normalizedLimit,
|
|
201
200
|
offset: normalizedOffset,
|
|
202
201
|
path: normalizedPath,
|
|
203
|
-
}
|
|
202
|
+
};
|
|
204
203
|
}
|
|
205
204
|
|
|
206
205
|
const cacheKey = JSON.stringify({
|
|
@@ -217,13 +216,14 @@ export class GitHistoryService {
|
|
|
217
216
|
return this.runRequest(cacheKey, async () => {
|
|
218
217
|
const hasHeadCommit = await this.hasHeadCommit();
|
|
219
218
|
if (!hasHeadCommit) {
|
|
220
|
-
return setCachedValue(this.historyCache, cacheKey,
|
|
219
|
+
return setCachedValue(this.historyCache, cacheKey, {
|
|
221
220
|
commits: [],
|
|
222
221
|
hasMore: false,
|
|
222
|
+
isGitRepo: true,
|
|
223
223
|
limit: normalizedLimit,
|
|
224
224
|
offset: normalizedOffset,
|
|
225
225
|
path: normalizedPath,
|
|
226
|
-
}
|
|
226
|
+
}, this.responseCacheTtlMs);
|
|
227
227
|
}
|
|
228
228
|
|
|
229
229
|
const logArgs = [
|
|
@@ -254,13 +254,14 @@ export class GitHistoryService {
|
|
|
254
254
|
|
|
255
255
|
const commits = this.mergeFileHistoryChunks(nameStatusOutput, numstatOutput);
|
|
256
256
|
const hasMore = commits.length > normalizedLimit;
|
|
257
|
-
const response =
|
|
257
|
+
const response = {
|
|
258
258
|
commits: commits.slice(0, normalizedLimit),
|
|
259
259
|
hasMore,
|
|
260
|
+
isGitRepo: true,
|
|
260
261
|
limit: normalizedLimit,
|
|
261
262
|
offset: normalizedOffset,
|
|
262
263
|
path: normalizedPath,
|
|
263
|
-
}
|
|
264
|
+
};
|
|
264
265
|
|
|
265
266
|
return setCachedValue(this.historyCache, cacheKey, response, this.responseCacheTtlMs);
|
|
266
267
|
});
|
|
@@ -269,13 +270,19 @@ export class GitHistoryService {
|
|
|
269
270
|
async getCommit({ allowLargePatch = false, hash, metaOnly = false, path = null } = {}) {
|
|
270
271
|
const isGitRepo = await this.commandRunner.isGitRepo();
|
|
271
272
|
if (!isGitRepo) {
|
|
272
|
-
return
|
|
273
|
+
return {
|
|
273
274
|
commit: null,
|
|
274
275
|
files: [],
|
|
275
276
|
isGitRepo: false,
|
|
276
277
|
metaOnly,
|
|
277
278
|
path: null,
|
|
278
|
-
|
|
279
|
+
source: 'commit',
|
|
280
|
+
summary: {
|
|
281
|
+
additions: 0,
|
|
282
|
+
deletions: 0,
|
|
283
|
+
filesChanged: 0,
|
|
284
|
+
},
|
|
285
|
+
};
|
|
279
286
|
}
|
|
280
287
|
|
|
281
288
|
const normalizedHash = this.normalizeCommitHash(hash);
|
|
@@ -301,13 +308,16 @@ export class GitHistoryService {
|
|
|
301
308
|
const summary = createSummaryFromFiles(meta.files);
|
|
302
309
|
|
|
303
310
|
if (metaOnly) {
|
|
304
|
-
const metaResponse =
|
|
311
|
+
const metaResponse = {
|
|
312
|
+
baseRef: meta.baseRef,
|
|
305
313
|
commit: meta.commit,
|
|
306
314
|
files: meta.files,
|
|
315
|
+
isGitRepo: true,
|
|
307
316
|
metaOnly: true,
|
|
308
317
|
path: normalizedPath,
|
|
318
|
+
source: 'commit',
|
|
309
319
|
summary,
|
|
310
|
-
}
|
|
320
|
+
};
|
|
311
321
|
return setCachedValue(this.commitCache, cacheKey, metaResponse, this.responseCacheTtlMs);
|
|
312
322
|
}
|
|
313
323
|
|
|
@@ -326,7 +336,8 @@ export class GitHistoryService {
|
|
|
326
336
|
!allowLargePatch
|
|
327
337
|
&& (fileSummary.additions + fileSummary.deletions) > this.maxInitialPatchLines
|
|
328
338
|
) {
|
|
329
|
-
const guardedResponse =
|
|
339
|
+
const guardedResponse = {
|
|
340
|
+
baseRef: meta.baseRef,
|
|
330
341
|
commit: meta.commit,
|
|
331
342
|
files: [{
|
|
332
343
|
...baseFile,
|
|
@@ -335,10 +346,12 @@ export class GitHistoryService {
|
|
|
335
346
|
patchLineCount: fileSummary.additions + fileSummary.deletions,
|
|
336
347
|
tooLarge: true,
|
|
337
348
|
}],
|
|
349
|
+
isGitRepo: true,
|
|
338
350
|
metaOnly: false,
|
|
339
351
|
path: normalizedPath,
|
|
352
|
+
source: 'commit',
|
|
340
353
|
summary,
|
|
341
|
-
}
|
|
354
|
+
};
|
|
342
355
|
return setCachedValue(this.commitCache, cacheKey, guardedResponse, this.responseCacheTtlMs);
|
|
343
356
|
}
|
|
344
357
|
|
|
@@ -385,13 +398,16 @@ export class GitHistoryService {
|
|
|
385
398
|
tooLarge: false,
|
|
386
399
|
};
|
|
387
400
|
|
|
388
|
-
const response =
|
|
401
|
+
const response = {
|
|
402
|
+
baseRef: meta.baseRef,
|
|
389
403
|
commit: meta.commit,
|
|
390
404
|
files: [detailedFile],
|
|
405
|
+
isGitRepo: true,
|
|
391
406
|
metaOnly: false,
|
|
392
407
|
path: normalizedPath,
|
|
408
|
+
source: 'commit',
|
|
393
409
|
summary,
|
|
394
|
-
}
|
|
410
|
+
};
|
|
395
411
|
return setCachedValue(this.commitCache, cacheKey, response, this.responseCacheTtlMs);
|
|
396
412
|
});
|
|
397
413
|
}
|
|
@@ -402,13 +418,13 @@ export class GitHistoryService {
|
|
|
402
418
|
const normalizedPath = normalizeRelativeGitPath(path);
|
|
403
419
|
|
|
404
420
|
if (!isGitRepo) {
|
|
405
|
-
return
|
|
421
|
+
return {
|
|
406
422
|
content: '',
|
|
407
423
|
fileKind: getVaultFileKind(normalizedPath),
|
|
408
424
|
hash: normalizedHash,
|
|
409
425
|
isGitRepo: false,
|
|
410
426
|
path: normalizedPath,
|
|
411
|
-
}
|
|
427
|
+
};
|
|
412
428
|
}
|
|
413
429
|
|
|
414
430
|
if (isImageAttachmentFilePath(normalizedPath)) {
|
|
@@ -438,12 +454,53 @@ export class GitHistoryService {
|
|
|
438
454
|
throw createGitRequestError(404, 'Commit file not found');
|
|
439
455
|
}
|
|
440
456
|
|
|
441
|
-
return setCachedValue(this.commitCache, cacheKey,
|
|
457
|
+
return setCachedValue(this.commitCache, cacheKey, {
|
|
442
458
|
content,
|
|
443
459
|
fileKind,
|
|
444
460
|
hash: normalizedHash,
|
|
461
|
+
isGitRepo: true,
|
|
462
|
+
path: normalizedPath,
|
|
463
|
+
}, this.responseCacheTtlMs);
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async getFileAttachment({ hash, path } = {}) {
|
|
468
|
+
const isGitRepo = await this.commandRunner.isGitRepo();
|
|
469
|
+
const normalizedHash = this.normalizeAttachmentRef(hash);
|
|
470
|
+
const normalizedPath = normalizeRelativeGitPath(path);
|
|
471
|
+
|
|
472
|
+
if (!isGitRepo) {
|
|
473
|
+
throw createGitRequestError(404, 'Git repository not found');
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (!isImageAttachmentFilePath(normalizedPath)) {
|
|
477
|
+
throw createGitRequestError(400, 'Unsupported image attachment path');
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const cacheKey = JSON.stringify({
|
|
481
|
+
hash: normalizedHash,
|
|
482
|
+
path: normalizedPath,
|
|
483
|
+
type: 'file-attachment',
|
|
484
|
+
});
|
|
485
|
+
const cached = getCachedValue(this.commitCache, cacheKey);
|
|
486
|
+
if (cached) {
|
|
487
|
+
return cached;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return this.runRequest(cacheKey, async () => {
|
|
491
|
+
let content;
|
|
492
|
+
try {
|
|
493
|
+
content = await this.commandRunner.execGitBuffer(['cat-file', 'blob', `${normalizedHash}:${normalizedPath}`]);
|
|
494
|
+
} catch {
|
|
495
|
+
throw createGitRequestError(404, 'Commit image not found');
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
return setCachedValue(this.commitCache, cacheKey, {
|
|
499
|
+
content,
|
|
500
|
+
hash: normalizedHash,
|
|
501
|
+
mimeType: getImageMimeType(normalizedPath),
|
|
445
502
|
path: normalizedPath,
|
|
446
|
-
}
|
|
503
|
+
}, this.responseCacheTtlMs);
|
|
447
504
|
});
|
|
448
505
|
}
|
|
449
506
|
|
|
@@ -481,6 +538,14 @@ export class GitHistoryService {
|
|
|
481
538
|
return normalizedHash;
|
|
482
539
|
}
|
|
483
540
|
|
|
541
|
+
normalizeAttachmentRef(hash) {
|
|
542
|
+
const normalizedHash = String(hash ?? '').trim();
|
|
543
|
+
if (normalizedHash === 'HEAD') {
|
|
544
|
+
return normalizedHash;
|
|
545
|
+
}
|
|
546
|
+
return this.normalizeCommitHash(normalizedHash);
|
|
547
|
+
}
|
|
548
|
+
|
|
484
549
|
parseHistoryChunk(chunk) {
|
|
485
550
|
const [headerLine = '', ...statLines] = String(chunk ?? '')
|
|
486
551
|
.split(/\r?\n/u)
|
|
@@ -575,6 +640,7 @@ export class GitHistoryService {
|
|
|
575
640
|
authoredAt: chunk.authoredAt,
|
|
576
641
|
deletions: Number(numstatEntry?.deletions || 0),
|
|
577
642
|
filesChanged: 1,
|
|
643
|
+
fileKind: getVaultFileKind(nameStatusEntry?.path),
|
|
578
644
|
hash: chunk.hash,
|
|
579
645
|
isMergeCommit: chunk.isMergeCommit,
|
|
580
646
|
oldPath: nameStatusEntry?.oldPath ?? null,
|
|
@@ -666,6 +732,7 @@ export class GitHistoryService {
|
|
|
666
732
|
const numstatEntry = numstatEntries[index];
|
|
667
733
|
return {
|
|
668
734
|
...entry,
|
|
735
|
+
fileKind: getVaultFileKind(entry.path),
|
|
669
736
|
stats: numstatEntry
|
|
670
737
|
? {
|
|
671
738
|
additions: numstatEntry.additions,
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const IMAGE_MIME_TYPES = Object.freeze({
|
|
2
|
+
'.gif': 'image/gif',
|
|
3
|
+
'.jpeg': 'image/jpeg',
|
|
4
|
+
'.jpg': 'image/jpeg',
|
|
5
|
+
'.png': 'image/png',
|
|
6
|
+
'.svg': 'image/svg+xml',
|
|
7
|
+
'.webp': 'image/webp',
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
export function getImageMimeType(filePath) {
|
|
11
|
+
const normalizedPath = String(filePath ?? '').toLowerCase();
|
|
12
|
+
const extension = Object.keys(IMAGE_MIME_TYPES).find((candidate) => normalizedPath.endsWith(candidate));
|
|
13
|
+
return extension ? IMAGE_MIME_TYPES[extension] : 'application/octet-stream';
|
|
14
|
+
}
|
|
@@ -54,98 +54,3 @@ export function createEmptyStatusResponse() {
|
|
|
54
54
|
summary: createEmptySummary(),
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
|
|
58
|
-
export function createDiffResponse({
|
|
59
|
-
files = [],
|
|
60
|
-
isGitRepo = true,
|
|
61
|
-
metaOnly = false,
|
|
62
|
-
path = null,
|
|
63
|
-
scope = 'working-tree',
|
|
64
|
-
summary = {
|
|
65
|
-
additions: 0,
|
|
66
|
-
deletions: 0,
|
|
67
|
-
filesChanged: 0,
|
|
68
|
-
},
|
|
69
|
-
} = {}) {
|
|
70
|
-
return {
|
|
71
|
-
files,
|
|
72
|
-
isGitRepo,
|
|
73
|
-
metaOnly,
|
|
74
|
-
path,
|
|
75
|
-
scope,
|
|
76
|
-
summary,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
export function createHistoryResponse({
|
|
81
|
-
commits = [],
|
|
82
|
-
hasMore = false,
|
|
83
|
-
isGitRepo = true,
|
|
84
|
-
limit = 30,
|
|
85
|
-
offset = 0,
|
|
86
|
-
} = {}) {
|
|
87
|
-
return {
|
|
88
|
-
commits,
|
|
89
|
-
hasMore,
|
|
90
|
-
isGitRepo,
|
|
91
|
-
limit,
|
|
92
|
-
offset,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function createFileHistoryResponse({
|
|
97
|
-
commits = [],
|
|
98
|
-
hasMore = false,
|
|
99
|
-
isGitRepo = true,
|
|
100
|
-
limit = 30,
|
|
101
|
-
offset = 0,
|
|
102
|
-
path = null,
|
|
103
|
-
} = {}) {
|
|
104
|
-
return {
|
|
105
|
-
commits,
|
|
106
|
-
hasMore,
|
|
107
|
-
isGitRepo,
|
|
108
|
-
limit,
|
|
109
|
-
offset,
|
|
110
|
-
path,
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export function createCommitDiffResponse({
|
|
115
|
-
commit = null,
|
|
116
|
-
files = [],
|
|
117
|
-
isGitRepo = true,
|
|
118
|
-
metaOnly = false,
|
|
119
|
-
path = null,
|
|
120
|
-
summary = {
|
|
121
|
-
additions: 0,
|
|
122
|
-
deletions: 0,
|
|
123
|
-
filesChanged: 0,
|
|
124
|
-
},
|
|
125
|
-
} = {}) {
|
|
126
|
-
return {
|
|
127
|
-
commit,
|
|
128
|
-
files,
|
|
129
|
-
isGitRepo,
|
|
130
|
-
metaOnly,
|
|
131
|
-
path,
|
|
132
|
-
source: 'commit',
|
|
133
|
-
summary,
|
|
134
|
-
};
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function createFileSnapshotResponse({
|
|
138
|
-
content = '',
|
|
139
|
-
fileKind = null,
|
|
140
|
-
hash = null,
|
|
141
|
-
isGitRepo = true,
|
|
142
|
-
path = null,
|
|
143
|
-
} = {}) {
|
|
144
|
-
return {
|
|
145
|
-
content,
|
|
146
|
-
fileKind,
|
|
147
|
-
hash,
|
|
148
|
-
isGitRepo,
|
|
149
|
-
path,
|
|
150
|
-
};
|
|
151
|
-
}
|
|
@@ -2,11 +2,17 @@ import { createReadStream } from 'node:fs';
|
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
|
|
5
|
+
import { getVaultFileKind, isImageAttachmentFilePath } from '../../../domain/file-kind.js';
|
|
5
6
|
import { mapWithConcurrency } from '../../shared/async-utils.js';
|
|
6
7
|
import { createEmptyStats } from './responses.js';
|
|
7
8
|
import { splitContentLines } from './parsers.js';
|
|
9
|
+
import { getImageMimeType } from './image-mime.js';
|
|
8
10
|
|
|
9
11
|
async function countFileLines(filePath) {
|
|
12
|
+
if (isImageAttachmentFilePath(filePath)) {
|
|
13
|
+
return 0;
|
|
14
|
+
}
|
|
15
|
+
|
|
10
16
|
return new Promise((resolve, reject) => {
|
|
11
17
|
const stream = createReadStream(filePath, { encoding: 'utf8' });
|
|
12
18
|
let lineCount = 0;
|
|
@@ -36,6 +42,7 @@ function buildSyntheticAddedFileDiff(pathValue, content) {
|
|
|
36
42
|
const normalizedContent = String(content ?? '').replace(/\r\n/g, '\n');
|
|
37
43
|
const file = {
|
|
38
44
|
code: 'U',
|
|
45
|
+
fileKind: getVaultFileKind(pathValue),
|
|
39
46
|
hunks: [],
|
|
40
47
|
isBinary: false,
|
|
41
48
|
oldPath: null,
|
|
@@ -89,7 +96,27 @@ export class GitUntrackedFileService {
|
|
|
89
96
|
async buildSyntheticDiffs(files = []) {
|
|
90
97
|
const results = await mapWithConcurrency(files, 2, async (file) => {
|
|
91
98
|
try {
|
|
92
|
-
const
|
|
99
|
+
const absolutePath = join(this.vaultDir, file.path);
|
|
100
|
+
if (isImageAttachmentFilePath(file.path)) {
|
|
101
|
+
const content = await readFile(absolutePath);
|
|
102
|
+
return {
|
|
103
|
+
...file,
|
|
104
|
+
binaryMessage: 'Binary image file',
|
|
105
|
+
byteLength: content.byteLength,
|
|
106
|
+
code: file.code || 'U',
|
|
107
|
+
fileKind: 'image',
|
|
108
|
+
hunks: [],
|
|
109
|
+
isBinary: true,
|
|
110
|
+
mimeType: getImageMimeType(file.path),
|
|
111
|
+
oldPath: file.oldPath ?? null,
|
|
112
|
+
path: file.path,
|
|
113
|
+
stats: createEmptyStats(),
|
|
114
|
+
status: file.status || 'untracked',
|
|
115
|
+
synthetic: true,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const content = await readFile(absolutePath, 'utf8');
|
|
93
120
|
return buildSyntheticAddedFileDiff(file.path, content);
|
|
94
121
|
} catch {
|
|
95
122
|
return null;
|
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
import { createSign } from 'node:crypto';
|
|
2
2
|
|
|
3
|
+
import { createRequestError } from '../http/http-errors.js';
|
|
4
|
+
|
|
3
5
|
function base64UrlJson(value) {
|
|
4
6
|
return Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
5
7
|
}
|
|
6
8
|
|
|
7
|
-
function createRequestError(statusCode, message, code) {
|
|
8
|
-
const error = new Error(message);
|
|
9
|
-
error.statusCode = statusCode;
|
|
10
|
-
error.code = code;
|
|
11
|
-
return error;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
9
|
function normalizeBaseUrl(value) {
|
|
15
10
|
return String(value ?? 'https://api.github.com').trim().replace(/\/+$/u, '') || 'https://api.github.com';
|
|
16
11
|
}
|
|
@@ -80,7 +75,9 @@ export class GitHubAppClient {
|
|
|
80
75
|
|
|
81
76
|
createAppJwt() {
|
|
82
77
|
if (!this.isConfigured()) {
|
|
83
|
-
throw createRequestError(503, 'GitHub App setup is not configured.',
|
|
78
|
+
throw createRequestError(503, 'GitHub App setup is not configured.', {
|
|
79
|
+
code: 'HOSTED_GITHUB_APP_NOT_CONFIGURED',
|
|
80
|
+
});
|
|
84
81
|
}
|
|
85
82
|
|
|
86
83
|
return createGitHubAppJwt({
|
|
@@ -91,7 +88,9 @@ export class GitHubAppClient {
|
|
|
91
88
|
|
|
92
89
|
createInstallationSetupUrl({ state } = {}) {
|
|
93
90
|
if (!this.appSlug) {
|
|
94
|
-
throw createRequestError(503, 'GitHub App slug is not configured.',
|
|
91
|
+
throw createRequestError(503, 'GitHub App slug is not configured.', {
|
|
92
|
+
code: 'HOSTED_GITHUB_APP_NOT_CONFIGURED',
|
|
93
|
+
});
|
|
95
94
|
}
|
|
96
95
|
|
|
97
96
|
const setupUrl = new URL(`/apps/${encodeURIComponent(this.appSlug)}/installations/new`, this.htmlBaseUrl);
|
|
@@ -131,7 +130,7 @@ export class GitHubAppClient {
|
|
|
131
130
|
throw createRequestError(
|
|
132
131
|
response.status,
|
|
133
132
|
payload?.message || 'GitHub API request failed.',
|
|
134
|
-
'HOSTED_GITHUB_API_FAILED',
|
|
133
|
+
{ code: 'HOSTED_GITHUB_API_FAILED' },
|
|
135
134
|
);
|
|
136
135
|
}
|
|
137
136
|
|
|
@@ -141,7 +140,9 @@ export class GitHubAppClient {
|
|
|
141
140
|
async createInstallationAccessToken(installationId, { appJwt = null } = {}) {
|
|
142
141
|
const normalizedInstallationId = String(installationId ?? '').trim();
|
|
143
142
|
if (!normalizedInstallationId) {
|
|
144
|
-
throw createRequestError(400, 'GitHub installation id is required.',
|
|
143
|
+
throw createRequestError(400, 'GitHub installation id is required.', {
|
|
144
|
+
code: 'HOSTED_GITHUB_INSTALLATION_REQUIRED',
|
|
145
|
+
});
|
|
145
146
|
}
|
|
146
147
|
|
|
147
148
|
const payload = await this.request(`/app/installations/${encodeURIComponent(normalizedInstallationId)}/access_tokens`, {
|
|
@@ -150,7 +151,9 @@ export class GitHubAppClient {
|
|
|
150
151
|
});
|
|
151
152
|
const token = String(payload?.token ?? '').trim();
|
|
152
153
|
if (!token) {
|
|
153
|
-
throw createRequestError(502, 'GitHub did not return an installation access token.',
|
|
154
|
+
throw createRequestError(502, 'GitHub did not return an installation access token.', {
|
|
155
|
+
code: 'HOSTED_GITHUB_TOKEN_MISSING',
|
|
156
|
+
});
|
|
154
157
|
}
|
|
155
158
|
return token;
|
|
156
159
|
}
|
|
@@ -158,7 +161,9 @@ export class GitHubAppClient {
|
|
|
158
161
|
async getInstallation(installationId, { appJwt = null } = {}) {
|
|
159
162
|
const normalizedInstallationId = String(installationId ?? '').trim();
|
|
160
163
|
if (!normalizedInstallationId) {
|
|
161
|
-
throw createRequestError(400, 'GitHub installation id is required.',
|
|
164
|
+
throw createRequestError(400, 'GitHub installation id is required.', {
|
|
165
|
+
code: 'HOSTED_GITHUB_INSTALLATION_REQUIRED',
|
|
166
|
+
});
|
|
162
167
|
}
|
|
163
168
|
|
|
164
169
|
return this.request(`/app/installations/${encodeURIComponent(normalizedInstallationId)}`, {
|
|
@@ -176,7 +181,9 @@ export class GitHubAppClient {
|
|
|
176
181
|
async resolveVaultSourceFromInstallation(installationId) {
|
|
177
182
|
const normalizedInstallationId = String(installationId ?? '').trim();
|
|
178
183
|
if (!normalizedInstallationId) {
|
|
179
|
-
throw createRequestError(400, 'GitHub installation id is required.',
|
|
184
|
+
throw createRequestError(400, 'GitHub installation id is required.', {
|
|
185
|
+
code: 'HOSTED_GITHUB_INSTALLATION_REQUIRED',
|
|
186
|
+
});
|
|
180
187
|
}
|
|
181
188
|
|
|
182
189
|
const appJwt = this.createAppJwt();
|
|
@@ -189,7 +196,7 @@ export class GitHubAppClient {
|
|
|
189
196
|
throw createRequestError(
|
|
190
197
|
409,
|
|
191
198
|
'GitHub App installation must grant access to exactly one selected repository.',
|
|
192
|
-
'HOSTED_GITHUB_REPOSITORY_SELECTION_REQUIRED',
|
|
199
|
+
{ code: 'HOSTED_GITHUB_REPOSITORY_SELECTION_REQUIRED' },
|
|
193
200
|
);
|
|
194
201
|
}
|
|
195
202
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import { basename } from 'node:path';
|
|
2
|
+
|
|
1
3
|
import { handleApiError } from './http-request-helpers.js';
|
|
2
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
createSafeAsciiFilename,
|
|
6
|
+
encodeContentDispositionFilename,
|
|
7
|
+
jsonResponse,
|
|
8
|
+
sendResponse,
|
|
9
|
+
} from './http-response.js';
|
|
3
10
|
|
|
4
11
|
function resolveDiffScope(requestUrl) {
|
|
5
12
|
const scope = String(requestUrl.searchParams.get('scope') || '').trim().toLowerCase();
|
|
@@ -106,6 +113,29 @@ export function createGitApiQueryHandler({ gitService }) {
|
|
|
106
113
|
return true;
|
|
107
114
|
}
|
|
108
115
|
|
|
116
|
+
if (requestUrl.pathname === '/api/git/file-attachment' && req.method === 'GET') {
|
|
117
|
+
try {
|
|
118
|
+
const attachment = await gitService.getFileAttachment({
|
|
119
|
+
hash: requestUrl.searchParams.get('hash'),
|
|
120
|
+
path: requestUrl.searchParams.get('path'),
|
|
121
|
+
});
|
|
122
|
+
const fileName = basename(String(attachment.path || 'image'));
|
|
123
|
+
sendResponse(req, res, {
|
|
124
|
+
body: attachment.content,
|
|
125
|
+
headers: {
|
|
126
|
+
'Cache-Control': 'private, max-age=300, stale-while-revalidate=3600',
|
|
127
|
+
'Content-Disposition': `inline; filename="${createSafeAsciiFilename(fileName)}"; filename*=UTF-8''${encodeContentDispositionFilename(fileName)}`,
|
|
128
|
+
'Content-Type': attachment.mimeType || 'application/octet-stream',
|
|
129
|
+
'X-Content-Type-Options': 'nosniff',
|
|
130
|
+
},
|
|
131
|
+
statusCode: 200,
|
|
132
|
+
});
|
|
133
|
+
} catch (error) {
|
|
134
|
+
handleApiError(req, res, error, '[api] Failed to read git file attachment:', 'Failed to read git file attachment');
|
|
135
|
+
}
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
|
|
109
139
|
if (requestUrl.pathname === '/api/git/pull-backups' && req.method === 'GET') {
|
|
110
140
|
try {
|
|
111
141
|
jsonResponse(req, res, 200, {
|