Skip to content

Commit 6ad40c5

Browse files
committed
fix(upload): accept onUploadInit in option validator
The `onUploadInit` callback was added to `UploadOptions` and wired through to `cryptify.upload`, but `validateUploadOptions`'s top-level whitelist was missed. Passing `{ onUploadInit: fn }` therefore threw `TypeError: sealed.upload(opts): unknown option "onUploadInit"` — breaking any consumer who tried to capture the uuid as soon as `upload_init` resolved (e.g. postguard-website's staging email-preview modal needs it to render the /download link before chunks finish). Adds `onUploadInit` to `VALID_UPLOAD_KEYS`, plus a value-type check that the callback is a function (consistent with the other "fail loud" checks in this validator). Tests: - accepts a top-level `onUploadInit` function - rejects a non-function value with a clear error
1 parent d3a7168 commit 6ad40c5

2 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/sealed.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export function resolveFiles(options: EncryptInput): File[] {
169169
}
170170

171171
const VALID_NOTIFY_KEYS = new Set(['recipients', 'sender', 'message', 'language']);
172-
const VALID_UPLOAD_KEYS = new Set(['notify']);
172+
const VALID_UPLOAD_KEYS = new Set(['notify', 'onUploadInit']);
173173
const VALID_LANGUAGES: ReadonlySet<string> = new Set(['EN', 'NL']);
174174

175175
/** Tracks which PostGuard configs have already seen the silent-default
@@ -194,11 +194,18 @@ function validateUploadOptions(opts: UploadOptions | undefined): void {
194194
throw new TypeError(
195195
`sealed.upload(opts): unknown option "${key}". ` +
196196
`Did you mean to nest it under "notify"? Expected shape: ` +
197-
`{ notify: { recipients?: boolean, sender?: boolean, message?: string, language?: 'EN' | 'NL' } }`
197+
`{ notify: { recipients?: boolean, sender?: boolean, message?: string, language?: 'EN' | 'NL' }, onUploadInit?: (info) => void }`
198198
);
199199
}
200200
}
201201

202+
if (opts.onUploadInit !== undefined && typeof opts.onUploadInit !== 'function') {
203+
throw new TypeError(
204+
`sealed.upload({ onUploadInit }) must be a function (info: { uuid, recoveryToken }) => void, ` +
205+
`got ${describeValue(opts.onUploadInit)}.`
206+
);
207+
}
208+
202209
const { notify } = opts;
203210
if (notify === undefined) return;
204211
if (notify === null || typeof notify !== 'object' || Array.isArray(notify)) {

tests/postguard.test.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,24 @@ describe('PostGuard', () => {
227227
);
228228
});
229229

230+
it('accepts onUploadInit callback at top level', async () => {
231+
const sealed = newSealed();
232+
// Validator must not flag `onUploadInit` — it sits at top level
233+
// alongside `notify`, not under it. Pre-fix this threw
234+
// `unknown option "onUploadInit"` and broke the upload flow.
235+
await expect(
236+
sealed.upload({ onUploadInit: () => {} })
237+
).rejects.not.toThrow(/unknown option "onUploadInit"/);
238+
});
239+
240+
it('rejects non-function onUploadInit', async () => {
241+
const sealed = newSealed();
242+
// @ts-expect-error — intentionally wrong type
243+
await expect(sealed.upload({ onUploadInit: 'cb' })).rejects.toThrow(
244+
/onUploadInit[^]*must be a function/
245+
);
246+
});
247+
230248
it('refuses to upload a ReadableStream payload', async () => {
231249
const sealed = pg.encrypt({
232250
data: new ReadableStream<Uint8Array>(),

0 commit comments

Comments
 (0)