feat: remove uploadUnit — a pulse uploads as one video (#64) - #66
Merged
Merged
Conversation
A pulse is one video plus related captions, beat manifest and thumbnail; a beat is a timestamp range inside that video, not an upload strategy. - Drop the `uploadUnit` option from the Fastify plugin and the core. Passing it now throws at boot via one shared `rejectRemovedOptions` check in lib/options.ts, called from both entry points. - Build the /capabilities body once in lib/capabilities.ts, without `uploadUnit`; drop it from the OpenAPI schema. - `buildUploadLink` no longer accepts or emits `uploadUnit`. - PROTOCOL §2/§3/§8, README and CHANGELOG describe the one upload set. - Tests: option rejected at boot on plugin and core, links never carry the param, /capabilities has no `uploadUnit`; e2e asserts it's absent. BREAKING CHANGE: `uploadUnit` option, link param and /capabilities field are removed. Release together with mieweb/pulse#213.
…de (#64) - fastify-demo, fastify-auth-demo, meteor-demo: remove UPLOAD_UNIT from .env.example / compose.yaml, the `uploadUnit` plugin/core option, the `?uploadUnit=` query param on /deeplinks, the per-link selector and the "Upload unit default" chip. - Feed/library viewers: describe a pulse as one video plus captions, beat manifest and thumbnail; the multi-clip playback path stays only for uploads made by older Pulse builds.
The server returns a path-only Location (/pulsevault/upload/...), which fetch() rejects as an invalid URL, so the e2e smoke test failed at the first PATCH. This was already broken on main.
…ress bar The feeds in fastify-demo, fastify-auth-demo and meteor-demo played a pulse clip by clip (ordering manifest, clip index, one progress bar per clip). A pulse is now one looping video: the beat manifest splits the progress bar into one track per beat, sized to its length, and the badge counts beats. The servers pair captions with their video through relatedTo instead of by filename.
This was referenced Sep 24, 2026
morepriyam
added a commit
that referenced
this pull request
Sep 24, 2026
…, checked in CI (#68) * feat!: protocol 2, with the version read from package.json and a client version check - The protocol this release implements is package.json `pulseProtocol` ({ version: "2.1", min: 2, max: 2 }). src/lib/protocol.ts reads it; /capabilities and the Protocol-Version header come from it, and /capabilities adds `protocolRevision`. Protocol 2 records the breaking change in #66 (uploadUnit removed); 2.1 adds the items below. - Clients may send `Pulse-Client: <product>/<version>; protocol=<min>-<max>`. A client whose newest protocol is older than this server's oldest gets 426 Upgrade Required with the supported range, on uploads and artifact GET/DELETE. /capabilities always answers. No header, no change. - `Upload-Metadata.appVersion` is trimmed, capped at 64 code points, stored with the artifact by both storage adapters, and reported on the complete/reject events. Tests read the expected protocol from package.json, so a version bump doesn't need test edits. * feat: the protocol written down as schema files protocol/ is now the single source for the wire contract (PROTOCOL.md §7): - protocol/schemas/*.schema.json (JSON Schema 2020-12): the /capabilities body, pairing link params, Upload-Metadata keys, the beat manifest, capability-token claims and the Pulse-Client header. Every field has a description; the /capabilities route schema is loaded from its file. - protocol/openapi.json: the HTTP surface, generated from the plugin's route schemas by scripts/gen-protocol.mjs (npm run protocol). - PROTOCOL.md: the version, the /capabilities and Upload-Metadata field tables and the list of schema files are generated into marked sections. §7 now covers major.minor versioning, the rules for changing the protocol, Pulse-Client and 426, and a version history (1.0, 2.0, 2.1). - npm run protocol:check fails if openapi.json or PROTOCOL.md are stale. - New tests validate what the code produces against the schemas: the live /capabilities body, buildUploadLink params, token claims, upload metadata, a beat manifest and Pulse-Client values. * ci: tests, protocol versioning checks, e2e and the Pulse contract suite - scripts/check-protocol.mjs enforces PROTOCOL.md §7.1 against a base ref: any change under protocol/ (descriptions and titles aside) needs a pulseProtocol.version bump, a breaking change needs a major bump, the version never goes backwards, and PROTOCOL.md needs a history row for it. oasdiff decides what's breaking in protocol/openapi.json; a conservative diff decides it for protocol/schemas/ (removed or newly required fields, removed values, changed types/formats/patterns/defaults are breaking). - .github/workflows/ci.yml, on PRs and pushes to main: - test: npm test on Node 22 and 24 - protocol: npm run protocol:check, then the versioning check with oasdiff - e2e: npm run e2e against a Postgres service container - pulse: mieweb/pulse's contract suite (the app's real pairing and upload code) against this build. Tests Pulse main, or the branch named by a `Pulse-Ref: <branch>` line in the PR description when the server and the app change together. Skips if that Pulse has no suite yet. * docs: protocol versioning in the README and CHANGELOG README: a Protocol versioning section (pulseProtocol, Pulse-Client and 426, the protocol/ folder, what CI enforces), the protocol-2 /capabilities example, appVersion in the Upload-Metadata table and the artifact event, and the npm scripts. CHANGELOG: the protocol 2.1 entries under Unreleased. * fix(protocol): escape backslashes in generated PROTOCOL.md table cells A description with a backslash before a pipe would have unescaped the pipe and broken the table. Escape backslashes first. (CodeQL js/incomplete-sanitization.) No change to the generated output today.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #64
A pulse is one video (the artifact named by the pairing link) plus its related captions, beat manifest and thumbnail. A beat is a timestamp range inside that video, not an upload strategy, so
uploadUnitis removed everywhere.Release order
Ship after / together with mieweb/pulse#213. Older Pulse builds require
uploadUnitin/capabilities, so a server running this without the matching app update would break pairing for them.Library
uploadUnitoption removed from the Fastify plugin (src/app.ts) and the core (src/core.ts). Passing it now throws aTypeErrorat boot: "uploadUnitwas removed — delete the option". This is one shared check (rejectRemovedOptionsinsrc/lib/options.ts), called from both entry points.validateUploadUnitis gone./capabilitiesbody (built only in the core; the plugin delegates to it) moved into its own module,buildCapabilitiesin the newsrc/lib/capabilities.ts, next to the protocol-version constants, and no longer includesuploadUnit. The field is also gone from the OpenAPI schema.PROTOCOL_VERSIONis still exported fromcoreandroutes.buildUploadLinkno longer accepts or emitsuploadUnit.capability-token.ts,storage/types.tsand the routes OpenAPI text no longer describe segment sessions.Protocol / docs
uploadUnitfrom the/capabilitiesfields. §3: removed the link param and added a SHOULD saying clients ignore unknown params. §8 is retitled "Artifact relationships (relatedTo)" and now describes the one upload set: video, captions, beat manifest and thumbnail. A beat is defined there as a timestamp range inside the video. The beat manifest JSON shape is unchanged.buildUploadLinkoverride example, and updated the/capabilitiesrow and example. Added a paragraph under the pairing flow describing the upload set and what a beat is.[Unreleased].Examples, scripts, tests
UPLOAD_UNIT(.env.example,compose.yaml), the option, the?uploadUnit=query param, the per-link selector and the "Upload unit default" chip.relatedTo, not by matching filenames.scripts/e2e-tus.mjsnow asserts thatuploadUnitis absent from/capabilities.scripts/e2e-tus.mjsalso resolves the TUS createLocationagainst the server base. The server returns a path-onlyLocation, whichfetch()rejects, so the e2e failed at the first PATCH. This was already broken onmain./capabilitieshas nouploadUnit(plugin and core).Verification
npm run build(tsc): cleannpm test: 127 tests, 124 pass, 0 fail, 3 skipped (the existing ffmpeg-dependent web-ready tests)UPLOAD_UNIT:/capabilitieshas nouploadUnit,/deeplinksemitsv/artifactId/serveronly, and the pairing page loads.npm run e2eagainst a local Postgres (DATABASE_URLset): all checks pass, including/capabilitieswith nouploadUnit.