| import * as MP from "./index.js" | |
| export type { MultipartError, PartInfo } from "./index.js" | |
| export { decodeField } from "./index.js" | |
| export type Part = Field | File | |
| export interface Field { | |
| readonly _tag: "Field" | |
| readonly info: MP.PartInfo | |
| readonly value: Uint8Array | |
| } | |
| export interface File { | |
| readonly _tag: "File" | |
| readonly info: MP.PartInfo | |
| readonly readable: ReadableStream<Uint8Array> | |
| } | |
| export interface MultipastaStream { | |
| readonly writable: WritableStream<Uint8Array> | |
| readonly readable: ReadableStream<Part> | |
| } | |
| export type WebConfig = Omit<MP.BaseConfig, "headers"> & { | |
| readonly headers: Headers | |
| } | |
| export const make = (config: WebConfig): MultipastaStream => { | |
| const headers = Object.fromEntries([...config.headers]) | |
| let error: MP.MultipartError | undefined | |
| let partBuffer: Array<Part> = [] | |
| let readResolve: (() => void) | undefined | |
| let chunkResolve: (() => void) | undefined | |
| let finished = false | |
| const parser = MP.make({ | |
| ...config, | |
| headers, | |
| onField(info, value) { | |
| partBuffer.push({ _tag: "Field", info, value }) | |
| if (readResolve !== undefined) readResolve() | |
| }, | |
| onFile(info) { | |
| let chunkBuffer: Array<Uint8Array> = [] | |
| let finished = false | |
| const readable = new ReadableStream<Uint8Array>({ | |
| pull(controller) { | |
| if (chunkBuffer.length > 0) { | |
| const chunks = chunkBuffer | |
| chunkBuffer = [] | |
| for (const chunk of chunks) { | |
| controller.enqueue(chunk) | |
| } | |
| } else if (error) { | |
| controller.error(error) | |
| } else if (finished) { | |
| controller.close() | |
| } else { | |
| return new Promise<void>(resolve => { | |
| chunkResolve = () => { | |
| chunkResolve = undefined | |
| resolve() | |
| } | |
| }).then(() => this.pull!(controller)) | |
| } | |
| }, | |
| }) | |
| partBuffer.push({ _tag: "File", info, readable }) | |
| if (readResolve !== undefined) readResolve() | |
| return function (chunk) { | |
| if (chunk === null) { | |
| finished = true | |
| } else { | |
| chunkBuffer.push(chunk) | |
| } | |
| if (chunkResolve !== undefined) { | |
| chunkResolve() | |
| } | |
| } | |
| }, | |
| onError(error_) { | |
| if (error !== undefined) return | |
| error = error_ | |
| if (chunkResolve !== undefined) chunkResolve() | |
| if (readResolve !== undefined) readResolve() | |
| }, | |
| onDone() { | |
| finished = true | |
| if (readResolve !== undefined) readResolve() | |
| }, | |
| }) | |
| const writable = new WritableStream<Uint8Array>({ | |
| write(chunk, _controller) { | |
| parser.write(chunk) | |
| }, | |
| close() { | |
| parser.end() | |
| }, | |
| }) | |
| const readable = new ReadableStream<Part>({ | |
| pull(controller) { | |
| if (partBuffer.length > 0) { | |
| const parts = partBuffer | |
| partBuffer = [] | |
| for (const part of parts) { | |
| controller.enqueue(part) | |
| } | |
| } else if (error) { | |
| controller.error(error) | |
| } else if (finished) { | |
| controller.close() | |
| } else { | |
| return new Promise<void>(resolve => { | |
| readResolve = () => { | |
| readResolve = undefined | |
| resolve() | |
| } | |
| }).then(() => this.pull!(controller)) | |
| } | |
| }, | |
| }) | |
| return { | |
| writable, | |
| readable, | |
| } | |
| } | |
Xet Storage Details
- Size:
- 3.35 kB
- Xet hash:
- e7ba1a7838c7fa64f1e2b3b630af95566befbb085e01433df9330e32cd5cd334
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.