fix(client): satisfy upload package gates
This commit is contained in:
1 parent
6acc828f81
commit
7a99e70372
5 files changed
+50
-28
No files matched your search
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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' })
|
||||
|
||||
Reference in new issue
Block a user