Skip to content

refactor(upload): remove runtime option validator, trust the types#87

Merged
rubenhensen merged 1 commit into
mainfrom
fix/upload-validator-accepts-onUploadInit
Jun 3, 2026
Merged

refactor(upload): remove runtime option validator, trust the types#87
rubenhensen merged 1 commit into
mainfrom
fix/upload-validator-accepts-onUploadInit

Conversation

@rubenhensen
Copy link
Copy Markdown
Contributor

@rubenhensen rubenhensen commented Jun 3, 2026

Summary

  • `validateUploadOptions` duplicated the shape of `UploadOptions`: a hand-maintained allowlist of top-level keys, a second allowlist of `notify` subkeys, and per-key `typeof` checks for the leaf values. Same information as the TS type, written twice.
  • That duplication just drifted: `onUploadInit` was added to the type and wired through to `cryptify.upload`, but the validator's allowlist wasn't touched. Calling the documented option therefore threw `TypeError: sealed.upload(opts): unknown option "onUploadInit"` and broke postguard-website's staging encrypt flow.
  • Removed the validator. TS callers already get the same diagnosis at compile time from `tsc`, and that's the only place this contract can be enforced without re-declaring it. JS callers fall through to a downstream failure on bad shapes — no worse than how most JS libraries handle this.
  • No public API change. The 12 deleted tests all exercised the validator itself; nothing exercising real upload behaviour was touched.

Test plan

  • `npm test` — 139 passed (was 151; -12 validator tests).
  • `npm run typecheck` clean.
  • `npm run build` clean.
  • Once released, postguard-website's staging-email-preview flow stops throwing on encrypt.

Unblocks encryption4all/postguard-website#244.

@dobby-coder
Copy link
Copy Markdown
Contributor

dobby-coder Bot commented Jun 3, 2026

Dobby has received the request! Routing to the right specialist now...

Copy link
Copy Markdown
Contributor

@dobby-coder dobby-coder Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review

Minimal, correct fix: adds onUploadInit to the upload-options whitelist and a fail-loud type-check matching the existing style. Downstream wiring (sealed.ts:131 → encryptPipeline → cryptify.ts) was already in place; only the validator was rejecting the documented option. Tests cover happy and negative paths. Tests pass.

Rule compliance

Nit: Unblocks encryption4all/postguard-website#244. won't auto-close on merge — only Closes / Fixes / Resolves are GitHub keywords. Swap if you want the website issue to close automatically when this merges (cross-repo Closes works with the org/repo#N form you already use).

`validateUploadOptions` duplicated the shape of `UploadOptions` as a
hand-maintained set of allowed keys + per-key value-type checks. The
type already encodes all of that — and when we recently added
`onUploadInit` to `UploadOptions` we updated the type but forgot the
validator, so callers passing the documented option saw
`TypeError: sealed.upload(opts): unknown option "onUploadInit"`.

Two declarations of "what a valid option looks like" inside one
package is a drift trap. Drop the runtime check: `tsc` already gives
TS callers the friendly compile-time error, and untyped JS callers
will get a downstream failure that's no worse than what most JS
libraries provide. No public API change; the deleted code path was
purely defensive.
@rubenhensen rubenhensen force-pushed the fix/upload-validator-accepts-onUploadInit branch from 6ad40c5 to f24762c Compare June 3, 2026 09:24
@rubenhensen rubenhensen changed the title fix(upload): accept onUploadInit in option validator refactor(upload): remove runtime option validator, trust the types Jun 3, 2026
@rubenhensen rubenhensen merged commit 1026108 into main Jun 3, 2026
6 checks passed
@rubenhensen rubenhensen deleted the fix/upload-validator-accepts-onUploadInit branch June 3, 2026 09:25
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jun 3, 2026

🎉 This PR is included in version 2.0.1 🎉

The release is available on:

Your semantic-release bot 📦🚀

rubenhensen added a commit to encryption4all/postguard-website that referenced this pull request Jun 3, 2026
Picks up the upload-validator removal from
encryption4all/postguard-js#87 (released as 2.0.1 via the follow-up
fix: commit in #88), which unblocks the staging email-preview modal
flow — passing `onUploadInit` no longer throws
`TypeError: unknown option "onUploadInit"`.
rubenhensen added a commit to encryption4all/postguard-website that referenced this pull request Jun 3, 2026
…er (#246)

* chore: bump @e4a/pg-js to ^2.0.1

Picks up the upload-validator removal from
encryption4all/postguard-js#87 (released as 2.0.1 via the follow-up
fix: commit in #88), which unblocks the staging email-preview modal
flow — passing `onUploadInit` no longer throws
`TypeError: unknown option "onUploadInit"`.

* fix(i18n): move emailPreview keys out of encryptPanel

The staging email-preview modal calls
\`\$_('filesharing.emailPreview.*')\`, but the keys had been added one
level deeper at \`filesharing.encryptPanel.emailPreview.*\` — the modal
therefore rendered the raw key paths instead of the translated strings.

The modal isn't part of the encrypt panel (it pops after a successful
upload), so move the block up to live as a sibling of \`encryptPanel\`
under \`filesharing\` in both en.json and nl.json. Path now matches the
call sites, no component changes needed.

* fix(emailPreview): open in-body links in a new tab via injected <base>

Clicking the "Download your files" link (or the link-text link) inside
the preview iframe tried to navigate the iframe itself. The sandbox
deliberately omits `allow-top-navigation`, so the navigation was
blocked and the iframe blanked out — leaving the modal open with an
empty body.

Splice `<base target="_blank" rel="noopener">` into the rendered
email's `<head>` (or prepend it if the template ever stops having
one) before handing the HTML to `srcdoc`. Anchors then open in a new
top-level tab, which the existing `allow-popups` /
`allow-popups-to-escape-sandbox` flags already permit. No cryptify
change needed — the real email never needed `target="_blank"`, only
the preview surface does.

* feat(emailPreview): HTML / plain-text body switcher

Cryptify already returns both MIME alternatives on
`/staging/preview/<uuid>` (`html` and `text` on each `RenderedEmail`),
but the modal only showed the HTML branch. Add a small tab strip
above the body that toggles between the two — the plain-text view
renders as a monospace `<pre>` (no iframe, no `<base>` injection
needed) so what you see is exactly what a text-only client would
receive.

Defaults to HTML since that's what most clients pick. Tab state is
per-modal-open and resets between recipients via the same active-idx
binding (intentional — switching recipients usually means starting
fresh).

* chore(dev-stack): dedupe FileInput; bump cryptify + pkg submodules

- `FileInput.svelte`: drop duplicate adds in the dropzone. A file
  already in the list (matched by name + size + lastModified) is
  silently removed from the dropzone instead of getting a second
  entry — same behaviour as Gmail's attach UI.
- `cryptify` → v0.1.27-9-g63066a1 (picks up the staging preview
  endpoint that the email-preview modal depends on, plus the
  `build.rs` Cargo.lock-driven `PG_CORE_VERSION` change).
- `postguard` (pg-pkg) → pg-ffi-v0.1.2-9-gcc47b60 (accepts Yivi
  condiscon in IrmaAuthRequest, which pg-js 2.0 emits for the
  optional name disjunction).
- `docker-compose.yml`: mount `cryptify/build.rs` and
  `cryptify/Cargo.lock` into the dev container; the bumped cryptify
  needs both at compile time to set the `X-PostGuard` mail header.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant