From 4a59f0d4336e4941dbc11b90b0fa46261a09e388 Mon Sep 17 00:00:00 2001 From: Michael Ramos Date: Thu, 3 Sep 2026 09:32:35 -0700 Subject: [PATCH] feat: download immutable artifact versions --- apps/web/src/api/client.ts | 5 + apps/web/src/review/review-app.tsx | 81 ++- apps/web/src/review/review-share.tsx | 3 +- apps/web/src/review/review.css | 24 + package.json | 1 + pnpm-lock.yaml | 3 + project/evidence/aws-pulumi.json | 2 +- project/evidence/browser.json | 2 +- .../compact-compose-runtime-baseline.json | 8 +- project/evidence/compact-compose.json | 2 +- ...rnal-storage-compose-runtime-baseline.json | 16 +- .../evidence/external-storage-compose.json | 2 +- .../evidence/external-storage-runtime.json | 2 +- project/evidence/gcp-pulumi.json | 2 +- .../helm-kubernetes-runtime-baseline.json | 20 +- project/evidence/helm-kubernetes.json | 2 +- project/evidence/local-capacity-baseline.json | 638 +++++++++--------- project/evidence/local-foundation.json | 2 +- project/evidence/local-package-build.json | 4 +- project/evidence/local-package.json | 2 +- .../evidence/local-performance-baseline.json | 158 ++--- project/evidence/native-cloud-storage.json | 2 +- project/evidence/observability.json | 2 +- project/evidence/oidc-keycloak.json | 2 +- project/evidence/s3-minio.json | 2 +- project/spec/artifact-comments-spec.md | 13 + .../spec/artifact-server-product-spec.html | 8 +- project/spec/artifact-server-product-spec.md | 2 + project/spec/conformance.yml | 24 + src/application/artifact-management.ts | 21 +- src/http/content-disposition.ts | 33 + src/http/create-http-app.ts | 62 +- src/http/version-archive.ts | 390 +++++++++++ tests/browser/frontend-mvp.spec.ts | 140 ++++ .../cmt-013-version-file-route.test.ts | 9 +- .../cmt-021-version-download.test.ts | 239 +++++++ .../prv-003-media-preview-route.test.ts | 5 +- tests/http/content-disposition.test.ts | 25 + 38 files changed, 1512 insertions(+), 446 deletions(-) create mode 100644 src/http/content-disposition.ts create mode 100644 src/http/version-archive.ts create mode 100644 tests/conformance/cmt-021-version-download.test.ts create mode 100644 tests/http/content-disposition.test.ts diff --git a/apps/web/src/api/client.ts b/apps/web/src/api/client.ts index 841b3f1..55c4314 100644 --- a/apps/web/src/api/client.ts +++ b/apps/web/src/api/client.ts @@ -1150,6 +1150,11 @@ export const api = { versionId: string, path: string, ) => versionAssetUrl("file", projectId, artifactId, versionId, path), + versionArchiveUrl: ( + projectId: string, + artifactId: string, + versionId: string, + ) => `/api/v1/artifacts/${encodeURIComponent(artifactId)}/versions/${encodeURIComponent(versionId)}/archive?${projectQuery(projectId)}`, versionMediaUrl: ( projectId: string, artifactId: string, diff --git a/apps/web/src/review/review-app.tsx b/apps/web/src/review/review-app.tsx index 97063b4..18e2f10 100644 --- a/apps/web/src/review/review-app.tsx +++ b/apps/web/src/review/review-app.tsx @@ -15,6 +15,7 @@ import { BookOpen01Icon, Cancel01Icon, Comment01Icon, + Download04Icon, Edit02Icon, ExternalLinkIcon, File01Icon, @@ -134,6 +135,11 @@ interface VersionListItem { readonly version: Version; } +interface ReviewDownload { + readonly href: string; + readonly title: string; +} + interface ReviewLocation { readonly artifactId: string | null; readonly path: string | null; @@ -960,6 +966,11 @@ function ArtifactReview({ && session.principal.membershipRole === "administrator" ) || session.principal.capabilities.includes("artifact:manage:any"); const previewKind = reviewPreviewKind(selectedVersion, selectedPath); + const download = reviewDownload( + projectId, + selectedArtifactId, + selectedVersion, + ); const selectArtifact = useCallback((artifactId: string, versionId?: string): void => { setDetailError(null); setSelectedArtifactId(artifactId); @@ -1657,6 +1668,10 @@ function ArtifactReview({ selectedVersion={selectedVersion} triggerClassName="as-button as-focus-controls__button" /> + + {!inspectorOpen ? ( +