返回源码地图

packages/attachment/attachment/src/admission.ts

main snapshot · da00f7f5358f · 正文引用章节 15;完整原文可核对,不声称全文件人工逐行审计

完整原文供逐行核对;页面收录不代表每行都经过人工语义审核。MIT 许可见 许可证。

1/** Wire-form admission of base64-encoded image uploads. @module @deepseek-ai/dsh-attachment/admission */
2
3import { Buffer } from 'node:buffer'
4import { AttachmentError } from './error.ts'
5import type { AttachmentStore } from './index.ts'
6import type {
7 EncodedFileAttachment,
8 EncodedImageAttachment,
9 FileAttachmentRef,
10 ImageAttachmentRef,
11 SaveImageAttachment,
12} from './types.ts'
13
14/** Decode one upload payload while rejecting non-canonical base64 forms. */
15function decodeCanonicalBase64(data: string, empty: 'reject' | 'accept', code: 'INVALID_IMAGE_BASE64' | 'INVALID_FILE_BASE64'): Uint8Array {
16 const decoded = Buffer.from(data, 'base64')
17 if ((data.length === 0 && empty === 'reject') || decoded.toString('base64') !== data) {
18 throw new AttachmentError(
19 code === 'INVALID_IMAGE_BASE64' ? 'Image upload is not canonical base64.' : 'File upload is not canonical base64.',
20 code,
21 )
22 }
23 return new Uint8Array(decoded)
24}
25
26function decodeBase64(data: string): Uint8Array {
27 return decodeCanonicalBase64(data, 'reject', 'INVALID_IMAGE_BASE64')
28}
29
30/** Store input for one decoded upload. */
31function saveInput(image: EncodedImageAttachment): SaveImageAttachment {
32 return {
33 data: decodeBase64(image.data),
34 mediaType: image.mediaType,
35 ...image.name === undefined ? {} : { name: image.name },
36 }
37}
38
39/**
40 * Admit one wire image batch: enforce canonical base64 on every member, then
41 * delegate batch admission — count and aggregate-byte limits, media-type and
42 * per-image validation, ordered commit — to {@link AttachmentStore.saveImages}.
43 * The shared entry for every RPC endpoint accepting browser uploads.
44 * @param attachments - the deployment attachment store owning batch policy.
45 * @param images - base64-encoded uploads in caller order.
46 * @returns durable references in the same order as `images`.
47 * @throws AttachmentError on a non-canonical payload or a refused batch.
48 */
49export async function admitEncodedImages(
50 attachments: AttachmentStore,
51 images: readonly EncodedImageAttachment[],
52): Promise<readonly ImageAttachmentRef[]> {
53 return attachments.saveImages(images.map(saveInput))
54}
55
56/**
57 * Admit one wire file upload: enforce canonical base64 (an empty file is a
58 * valid zero-byte payload), then delegate verbatim commit to
59 * {@link AttachmentStore.saveFile}. The shared entry for every RPC endpoint
60 * accepting browser file uploads.
61 * @param attachments - the deployment attachment store.
62 * @param file - base64-encoded upload and optional display name.
63 * @returns the durable content-addressed file reference.
64 * @throws AttachmentError on a non-canonical payload or a storage failure.
65 */
66export async function admitEncodedFile(
67 attachments: AttachmentStore,
68 file: EncodedFileAttachment,
69): Promise<FileAttachmentRef> {
70 return attachments.saveFile({
71 data: decodeCanonicalBase64(file.data, 'accept', 'INVALID_FILE_BASE64'),
72 ...file.name === undefined ? {} : { name: file.name },
73 })
74}