fix(client): satisfy upload package gates

This commit is contained in:
creatixchu committed 2026-09-03 14:11:41 +08:00
1 parent 6acc828f81
commit 7a99e70372
5 files changed
+50 -28

No files matched your search

+2 -2
View File
@@ -2,5 +2,5 @@
# side as of the last confirmed-consistent state. Both languages carry equal authority;
# after editing either side, bring the other along and re-record with:
# pnpm run verify-translation-pairing --write packages/client/file-upload/README.md
README.md: d7cac384c1cef4f52f4f7c09838697d53bba7c4d
README.zh.md: 6ced413d1813c1808daef10e0cf283cd973eff68
README.md: 9abd4106dbd7cd1cb9671f8f830952f6faff11c6
README.zh.md: 4ad14ca5a91acd482f54cab74fb974208a264a5a
+2
View File
@@ -52,6 +52,8 @@ The Client plugin provides one inherited `ctx.fileUpload` service. Its provider
</details>
**Runtime invariant:** No companion is published. Each request uses exactly one selected carrier, and fixture or unsupported-browser requests fail before the body is sent.
-----
<a id="further-exploration"></a>
+2
View File
@@ -52,6 +52,8 @@ Client 插件提供可被各级上下文继承的 `ctx.fileUpload` 服务。其
</details>
**运行时不变式:** 不发布伴生入口。每个请求只使用一个已选定载体;fixture 或不支持的浏览器请求会在发送请求体前失败。
-----
<a id="further-exploration"></a>
@@ -124,7 +124,7 @@ describe('file upload worker body', () => {
fileUploadWorker(
scope,
() => { throw new Error('unused') },
() => Promise.reject('offline'),
() => Promise.reject(new Error('offline')),
)
scope.onmessage?.({ data: { url: '/upload', body, headers: {} } } as never)
await vi.waitFor(() => {
@@ -190,9 +190,10 @@ describe('file upload service', () => {
const ctx = new Context()
const fiber = ctx.plugin({ apply })
await fiber
await ctx.fileUpload.post({ path: '/fallback', body: new Blob() })
const body = new Blob()
await ctx.fileUpload.post({ path: '/fallback', body })
expect(fetch).toHaveBeenCalledWith(new URL('http://dsh.internal/fallback'), {
method: 'POST', body: expect.any(Blob),
method: 'POST', body,
})
await fiber.dispose()
})
@@ -6,7 +6,9 @@
import { Context } from '@deepseek-ai/cordis'
import { describe, expect, it, vi } from 'vitest'
import { makeTranslate, RemoteError, SlotTestRuntime } from '@deepseek-ai/dsh-client-test-runtime'
import type { QueuedMessage } from '@deepseek-ai/dsh-api-session-controller/client'
import type {
BeginSubmissionInput, PendingSubmissionRetirement, QueuedMessage,
} from '@deepseek-ai/dsh-api-session-controller/client'
import { ComposerBlockRegistry } from '../src/client/input/blocks.ts'
import { InputHub } from '../src/client/input/hub.ts'
import { ConversationController } from '../src/client/service.ts'
@@ -453,9 +455,9 @@ describe('sendSession submission echo', () => {
/** Bench with an observable beginSubmission on the session face. */
async function echoBench() {
const b = await bench()
const retire: { onRetire?: ((retirement: unknown) => void) | undefined } = {}
const retire: { onRetire?: ((retirement: PendingSubmissionRetirement) => void) | undefined } = {}
const abandon = vi.fn()
const beginSubmission = vi.fn((input: { onRetire?: (retirement: unknown) => void }) => {
const beginSubmission = vi.fn((input: BeginSubmissionInput) => {
retire.onRetire = input.onRetire
return { requestId: 'req-echo' as never, abandon }
})
@@ -480,14 +482,12 @@ describe('sendSession submission echo', () => {
const session = b.runtime.sessions.binding('s1')!.session
const sending = b.root.sendSession(session, '带图', [attachment!.id], 'queue')
// Synchronous: the echo is registered before any encoding starts.
expect(b.beginSubmission).toHaveBeenCalledWith(expect.objectContaining({
mode: 'queue',
text: '带图',
attachments: [{
type: 'image',
value: expect.objectContaining({ previewUrl: 'blob:echo-1', name: 'a.png' }),
}],
}))
const echo = b.beginSubmission.mock.calls[0]?.[0]
expect(echo?.mode).toBe('queue')
expect(echo?.text).toBe('带图')
expect(echo?.attachments).toHaveLength(1)
expect(echo?.attachments[0]?.type).toBe('image')
expect(echo?.attachments[0]?.value).toMatchObject({ previewUrl: 'blob:echo-1', name: 'a.png' })
expect(b.prompt).not.toHaveBeenCalled()
await vi.waitFor(() => { expect(b.prompt).toHaveBeenCalledOnce() })
expect(b.prompt).toHaveBeenCalledWith(
@@ -531,14 +531,15 @@ describe('sendSession submission echo', () => {
expect(b.root.fileUploads.getSnapshot()[drafts[1]!.id]?.status).toBe('ready')
})
const sending = b.root.sendSession(session, 'ordered', drafts.map(draft => draft.id), 'steer')
expect(b.beginSubmission).toHaveBeenCalledWith(expect.objectContaining({
mode: 'steer',
attachments: [
{ type: 'image', value: expect.objectContaining({ name: 'first.png' }) },
{ type: 'file', value: { attachmentId: 'mixed-file', name: 'notes.txt', bytes: 1 } },
{ type: 'image', value: expect.objectContaining({ name: 'last.png' }) },
],
}))
const echo = b.beginSubmission.mock.calls[0]?.[0]
expect(echo?.mode).toBe('steer')
expect(echo?.attachments.map(attachment => attachment.type === 'image'
? { type: attachment.type, name: attachment.value.name }
: { type: attachment.type, value: attachment.value })).toEqual([
{ type: 'image', name: 'first.png' },
{ type: 'file', value: { attachmentId: 'mixed-file', name: 'notes.txt', bytes: 1 } },
{ type: 'image', name: 'last.png' },
])
await vi.waitFor(() => { expect(b.prompt).toHaveBeenCalledOnce() })
expect(b.prompt.mock.calls[0]?.[0]).toEqual([
{ type: 'image', mediaType: 'image/png', data: expect.any(String) as string, name: 'first.png' },
@@ -549,9 +550,21 @@ describe('sendSession submission echo', () => {
b.retire.onRetire?.({
reason: 'observed',
attachments: [
{ attachmentId: 'image-first', mediaType: 'image/png' },
{ attachmentId: 'mixed-file', name: 'notes.txt', bytes: 1 },
{ attachmentId: 'image-last', mediaType: 'image/png' },
{
attachmentId: 'image-first' as never,
mediaType: 'image/png',
bytes: 1,
width: 1,
height: 1,
},
{ attachmentId: 'mixed-file' as never, name: 'notes.txt', bytes: 1 },
{
attachmentId: 'image-last' as never,
mediaType: 'image/png',
bytes: 1,
width: 1,
height: 1,
},
],
})
await expect(sending).resolves.toEqual({ kind: 'success' })
@@ -598,7 +611,11 @@ describe('sendSession submission echo', () => {
const sending = b.root.sendSession(session, '', [attachment!.id], 'queue')
await vi.waitFor(() => { expect(b.prompt).toHaveBeenCalledOnce() })
const ref = {
attachmentId: 'att-1', mediaType: 'image/png', bytes: 1, width: 1, height: 1,
attachmentId: 'att-1' as never,
mediaType: 'image/png' as const,
bytes: 1,
width: 1,
height: 1,
}
b.retire.onRetire?.({ reason: 'observed', attachments: [ref] })
await expect(sending).resolves.toEqual({ kind: 'success' })