Skip to content

v9.0.0: promote the merged chunked-upload stack to master - #40

Merged
dmurphy5 merged 4 commits into
masterfrom
dylan/v9-promote
Sep 3, 2026
Merged

v9.0.0: promote the merged chunked-upload stack to master#40
dmurphy5 merged 4 commits into
masterfrom
dylan/v9-promote

Conversation

@dmurphy5

@dmurphy5 dmurphy5 commented Sep 3, 2026

Copy link
Copy Markdown

This PR moves the merged chunked-upload work onto master. It replaces #39, which conflicted.

The stacked PRs #32#35 were reviewed and merged, but their stack was rooted on dylan/v9-3-configure, so their four squash commits landed on that branch and not on master. This branch carries those same four commits, cherry-picked onto the current master. The tree at the tip is byte-identical to the merged stack. There are no new changes, and there are no conflicts: the branch is a fast-forward of master.

Prefer "Rebase and merge", so the four commits keep their identities on master. After the merge, master is v9.0.0 and gets the v9.0.0 tag.

🤖 Generated with Claude Code

dmurphy5 and others added 4 commits September 3, 2026 11:12
This PR adds the TypeScript surface for chunked uploads. It contains no
engine code.

The new API is `startUpload` with `type: 'chunked'`. The options are:

- `id` — the upload id. The consumer supplies it. It is required.
- `path` — the file to send.
- `parts` — a list of parts. Each part has a URL, headers, and a byte
range.
- `accept` — rules that say which HTTP responses are a success. Each
rule is a status code and optional body text. These rules replace
`acceptStatus` in all upload types, so the library has one shape.
- `expiresAt` — the time when the library must stop the upload. It is
required.

The library validates the parts. The first part must start at byte 0.
Each next part must start where the last part ends.

The PR adds two functions. `chunkPlan` is a pure function: give it a
file size, and it returns the byte ranges for the parts. A property test
sweeps every size at a reduced scale. `removeUpload` releases a stopped
upload's file and records.

Codegen cannot model the raw/chunked union. Thus the native side gets a
separate `startChunkedUpload` entry point. The public `startUpload`
stays one function and routes on `options.type`.

The native functions are stubs on both platforms. They return
`E_NOT_IMPLEMENTED`. Thus this PR compiles and passes CI alone. The
engine PRs above this one replace the stubs.

The PR also turns on `noUncheckedIndexedAccess`. Diana type-checks the
shipped `.ts` files with that setting, so the library must catch those
breaks first.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This PR adds the Android engine for chunked uploads.

At start, the library moves the file into its own directory. The library
then owns the file. The library writes a durable manifest to disk. The
manifest holds the parts, the status of each part, and the options.

The engine sends the parts with WorkManager. A RandomAccessFile-backed
body streams each part directly from the file. There are no chunk copy
files, and disk usage does not double. A maximum of 3 parts transmit at
one time (the window). The engine applies the accept rules and the
expiry time.

The engine writes each event to a journal before it emits the event. The
library deletes the file only when the consumer acknowledges the
`completed` event.

`startUpload` with the same id resumes the upload. The engine skips the
parts that the server accepted. The same id with different parts
recreates the upload. The engine permits a recreate only when the upload
is not running, and the new parts must tile the file exactly.

Review fixes in this slice:

- A job that v8 enqueued replays safely. The engine normalizes the
legacy `acceptStatus` field. A test uses the literal v8 JSON shape.
- The reconcile step is atomic under the store lock.
- The work policy is `APPEND_OR_REPLACE`, and a worker returns success
after it journals a terminal error. Thus a failing chain cannot silently
drop a resume.
- An ack race resolves cleanly. A missing manifest is a no-op.
- A range-tiling guard protects the recreate rule.

84 JVM tests.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This PR adds the iOS engine for chunked uploads. The JS-visible behavior
is the same as Android. The transport is a background `URLSession`.

A background session can send only files. Thus the engine writes one
temporary file for each part in flight, and deletes it when the part
completes. The transient disk usage stays near 60 MB. There is no second
full copy of the source file.

The engine keeps a sliding window of 3 part tasks enqueued in the
session. The session daemon continues the tasks when the app is
suspended or terminated. When iOS relaunches the app, the engine
reconciles the manifest with the daemon's task list, and then fills the
window again. The background completion-handler flow is kept.

Review fixes in this slice:

- Same-id `startUpload` races are serialized natively.
- An incarnation token stops the late callbacks of a removed upload.
Without the token, those callbacks could corrupt a recreated upload with
the same id.
- The recreate rule and the stale temporary-file edges are covered.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This PR sets the library-wide transmission cap to 4 on both platforms.
Before, both platforms sent one request at a time per host (an Android
semaphore of 1; iOS `httpMaximumConnectionsPerHost` of 1). On Android,
the cap is hard: each request must pass the semaphore. On iOS, the cap
is a per-session, connection-level backstop. The window of 3 already
bounds the parts of one chunked upload.

The PR also:

- Adds a chunked demo and a `removeUpload` button to the example app.
- Rewrites the README and the CHANGELOG for v9.
- Sets the version to 9.0.0.

Full suite at the tip: typecheck and lint clean, jest 37, gradle 84,
xcodebuild BUILD SUCCEEDED.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@dmurphy5
dmurphy5 merged commit c3f2f0c into master Sep 3, 2026
2 checks passed
@dmurphy5
dmurphy5 deleted the dylan/v9-promote branch September 3, 2026 15:25
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.

3 participants