Skip to content

feat: add @constructive-io/upload-client package (Phase 2B) + README header fixes#960

Merged
pyramation merged 2 commits intomainfrom
devin/1775197970-upload-client
Apr 3, 2026
Merged

feat: add @constructive-io/upload-client package (Phase 2B) + README header fixes#960
pyramation merged 2 commits intomainfrom
devin/1775197970-upload-client

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

@pyramation pyramation commented Apr 3, 2026

Summary

New standalone vanilla TypeScript package (@constructive-io/upload-client) providing client-side presigned URL upload utilities. Framework-agnostic — works with any GraphQL client via a GraphQLExecutor function parameter.

Atomic functions:

  • hashFile(file) — SHA-256 via Web Crypto API (single-pass)
  • hashFileChunked(file, chunkSize?, onProgress?) — chunked SHA-256 with progress callback
  • uploadFile(options) — full orchestrator: hash → requestUploadUrl → PUT to S3 → confirmUpload

Features: deduplication (skip PUT when content hash matches), AbortSignal cancellation, progress tracking (XHR when available, fetch fallback), typed UploadError with error codes.

23 tests across 2 suites, all passing. Build produces CJS + ESM outputs via makage.

Updates since last revision

Added standard README headers (logo, CI badge, license badge, version badge) to 4 READMEs that were missing them:

  • packages/upload-client/README.md — new package from this PR
  • graphile/graphile-presigned-url-plugin/README.md — from earlier storage pipeline work
  • graphql/node-type-registry/README.md
  • jobs/README.md — logo + CI + license only (no version badge since it's a top-level docs file, not a package); also fixed n## typo on line 1

Review & Testing Checklist for Human

  • Verify GraphQL mutation field names in src/queries.ts match the server-side graphile-presigned-url-plugin schema exactly (uploadUrl, fileId, key, deduplicated, expiresAt, status, success). These are plain strings with no compile-time validation.
  • Evaluate hashFileChunked usefulness — it reads chunks sequentially but concatenates them in memory before the final crypto.subtle.digest call, so peak memory is not reduced vs hashFile. The JSDoc is transparent about this. Decide if this API should ship as-is, be renamed, or be deferred until a WASM-based streaming hash is available.
  • XHR code path (putWithXHR) is untested — Node.js test environment has no XMLHttpRequest, so only the putWithFetch path is exercised. Consider whether browser-level testing is needed before merge, or if this is acceptable for v0.1.0.
  • Test plan: cd packages/upload-client && pnpm test (23 tests), pnpm build (CJS + ESM). Lint is broken repo-wide (ESLint 9 migration issue, not specific to this package — packages/csrf has the same failure).

Notes

  • FileInput interface is a minimal abstraction compatible with browser File, Node.js Blob, and custom implementations.
  • UploadError codes: HASH_FAILED, INVALID_FILE, GRAPHQL_ERROR, REQUEST_UPLOAD_URL_FAILED, PUT_UPLOAD_FAILED, CONFIRM_UPLOAD_FAILED, ABORTED.
  • Empty files are accepted by hashFile but rejected by uploadFile (size <= 0). This is intentional — hashing is a pure utility, uploading has business validation.
  • README audit found 63/67 READMEs already had the standard header; the 4 fixed here were the only ones missing it.

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

Vanilla TypeScript utilities for client-side presigned URL uploads:
- hashFile() — SHA-256 via Web Crypto API
- hashFileChunked() — chunked SHA-256 for large files with progress
- uploadFile() — full orchestrator (hash → requestUploadUrl → PUT → confirmUpload)
- GraphQLExecutor type — works with any GraphQL client (urql, Apollo, fetch)
- UploadError class with typed error codes
- AbortSignal support for cancellation
- Progress tracking via XHR upload events
- Deduplication support (skip PUT when file already exists)

23 tests passing across 2 test suites.
@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

@socket-security
Copy link
Copy Markdown

socket-security bot commented Apr 3, 2026

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

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​tanstack/​react-query@​5.90.219910088100100
Added@​testing-library/​react@​11.2.510010010088100
Added@​testing-library/​jest-dom@​5.11.1010010010089100

View full report

…DMEs

- graphile/graphile-presigned-url-plugin/README.md
- graphql/node-type-registry/README.md
- jobs/README.md (no version badge — top-level docs, not a package)
- packages/upload-client/README.md
@devin-ai-integration devin-ai-integration bot changed the title feat: add @constructive-io/upload-client package (Phase 2B) feat: add @constructive-io/upload-client package (Phase 2B) + README header fixes Apr 3, 2026
@pyramation pyramation merged commit d9cae48 into main Apr 3, 2026
46 checks passed
@pyramation pyramation deleted the devin/1775197970-upload-client branch April 3, 2026 07:44
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