1
/** Wire-form admission of base64-encoded image uploads. @module @deepseek-ai/dsh-attachment/admission */3
import { Buffer } from 'node:buffer'4
import { AttachmentError } from './error.ts'5
import type { AttachmentStore } from './index.ts'6
import type {7
EncodedFileAttachment,8
EncodedImageAttachment,9
FileAttachmentRef,10
ImageAttachmentRef,11
SaveImageAttachment,12
} from './types.ts'14
/** Decode one upload payload while rejecting non-canonical base64 forms. */15
function 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
}26
function decodeBase64(data: string): Uint8Array {27
return decodeCanonicalBase64(data, 'reject', 'INVALID_IMAGE_BASE64')28
}30
/** Store input for one decoded upload. */31
function saveInput(image: EncodedImageAttachment): SaveImageAttachment {32
return {33
data: decodeBase64(image.data),34
mediaType: image.mediaType,35
...image.name === undefined ? {} : { name: image.name },36
}37
}39
/**40
* Admit one wire image batch: enforce canonical base64 on every member, then41
* delegate batch admission — count and aggregate-byte limits, media-type and42
* 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
*/49
export async function admitEncodedImages(50
attachments: AttachmentStore,51
images: readonly EncodedImageAttachment[],52
): Promise<readonly ImageAttachmentRef[]> {53
return attachments.saveImages(images.map(saveInput))54
}56
/**57
* Admit one wire file upload: enforce canonical base64 (an empty file is a58
* valid zero-byte payload), then delegate verbatim commit to59
* {@link AttachmentStore.saveFile}. The shared entry for every RPC endpoint60
* 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
*/66
export 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
}