Skip to content

feat(upload-client): add hashContent, putToPresignedUrl, fetchFromUrl exports#1107

Merged
pyramation merged 5 commits intomainfrom
feat/upload-client-exports
May 10, 2026
Merged

feat(upload-client): add hashContent, putToPresignedUrl, fetchFromUrl exports#1107
pyramation merged 5 commits intomainfrom
feat/upload-client-exports

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

@pyramation pyramation commented May 10, 2026

Summary

Adds three new atomic exports to @constructive-io/upload-client for custom presigned URL flows:

  • hashContent(content: string) — SHA-256 hex digest of a plain string via Web Crypto API (crypto.subtle.digest). Complements hashFile (which accepts File/Blob). New file: hash-content.ts.
  • putToPresignedUrl(url, body, contentType, signal?) — Extracted from the private putWithFetch in upload.ts into a standalone export in put.ts. The internal putToS3 now delegates to this for its fetch path.
  • fetchFromUrl(url, signal?) — Fetch GET wrapper with structured UploadError on failure. Useful for verifying downloads from presigned or CDN URLs.

Isomorphic HTTP via @constructive-io/fetch

put.ts uses createFetch() from @constructive-io/fetch (added as a new dependency) instead of raw globalThis.fetch. This resolves *.localhost subdomain DNS and Host header issues in Node.js — the same pattern used in @constructive-io/graphql-query/runtime. In browsers, createFetch() returns globalThis.fetch as-is.

The internal putWithFetch function is removed; putToS3 now calls putToPresignedUrl directly. Existing tests (24/24) pass unchanged.

Motivation: The constructive-db storage security tests currently hand-roll putToPresignedUrl() and hashContent() helpers inline. After this ships, those tests can import from this package instead — dogfooding our own tools. Related: constructive-db#1093.

Review & Testing Checklist for Human

  • fetchFromUrl reuses PUT_UPLOAD_FAILED error code — both error paths in fetchFromUrl throw with code 'PUT_UPLOAD_FAILED', which is semantically wrong for a GET operation. Decide whether to add a new error code (e.g. 'FETCH_FAILED') to the UploadErrorCode union, or accept the reuse for now.
  • No new unit tests for put.ts or hash-content.ts — the existing upload.test.ts indirectly covers putToPresignedUrl via the uploadFile orchestrator, but hashContent and fetchFromUrl have zero test coverage. Consider whether to add them before merging.
  • Module-level createFetch() callconst fetch = createFetch() in put.ts runs at import time. createFetch() is synchronous and cached, so this should be safe, but verify no side effects at import in edge/serverless environments.
  • Run pnpm test in packages/upload-client and verify 24/24 pass after merge.

Notes

  • body: BodyInit | stringstring is technically already in BodyInit in modern TS DOM libs, but kept explicit for compatibility with environments that may not include it. The body is cast to BodyInit when passed to the createFetch() result since the wrapper's type signature expects standard fetch args.
  • The putToPresignedUrl return type is Promise<Response> (the old putWithFetch returned void). The internal putToS3 caller discards the return value, so no behavioral change for uploadFile.
  • The lockfile diff is large due to formatting normalization by pnpm; the only substantive addition is @constructive-io/fetch@1.0.0.

Link to Devin session: https://app.devin.ai/sessions/7903d2a3e7a34c6daa605e12d6b80d9e
Requested by: @pyramation

… exports

- hashContent: Node.js SHA-256 for strings/Buffers (tests, scripts, CLI)
- putToPresignedUrl: extracted from private putWithFetch, now a named export
- fetchFromUrl: fetch GET wrapper for presigned/CDN URL downloads
- uploadFile's internal putToS3 now delegates to putToPresignedUrl
- Updated README with unified API docs
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

Drop hash-content.ts — it imported Node.js 'crypto' module which breaks
the client-side contract of this package. All exports now use Web
standards only (fetch, Web Crypto).
@devin-ai-integration devin-ai-integration Bot changed the title feat(upload-client): add hashContent, putToPresignedUrl, fetchFromUrl exports feat(upload-client): add putToPresignedUrl, fetchFromUrl exports May 10, 2026
hashContent(string) computes SHA-256 using crypto.subtle.digest —
same Web Crypto API as hashFile. No Node.js-only dependencies.
Developers get one package for the complete upload pipeline:
hashContent/hashFile → putToPresignedUrl → fetchFromUrl
@devin-ai-integration devin-ai-integration Bot changed the title feat(upload-client): add putToPresignedUrl, fetchFromUrl exports feat(upload-client): add hashContent, putToPresignedUrl, fetchFromUrl exports May 10, 2026
@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​constructive-io/​fetch@​1.0.0701009489100

View full report

@pyramation pyramation merged commit 7f3cde1 into main May 10, 2026
54 checks passed
@pyramation pyramation deleted the feat/upload-client-exports branch May 10, 2026 22:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant