MCPcopy
hub / github.com/simstudioai/sim / readMultipart

Function readMultipart

apps/sim/lib/core/utils/multipart.ts:94–256  ·  view source on GitHub ↗
(
  request: MultipartRequest,
  options: ReadMultipartOptions
)

Source from the content-addressed store, hash-verified

92 * error on `result.file.stream`.
93 */
94export function readMultipart(
95 request: MultipartRequest,
96 options: ReadMultipartOptions
97): Promise<ParsedMultipart> {
98 const { maxFileBytes, requiredFieldsBeforeFile = [], fileFieldName = 'file', signal } = options
99
100 return new Promise<ParsedMultipart>((resolve, reject) => {
101 const contentType = request.headers.get('content-type')
102 if (!contentType || !contentType.toLowerCase().includes('multipart/form-data')) {
103 reject(new MultipartError('NOT_MULTIPART', 'Expected multipart/form-data request'))
104 return
105 }
106 if (!request.body) {
107 reject(new MultipartError('NO_BODY', 'Request has no body'))
108 return
109 }
110
111 let bb: busboy.Busboy
112 try {
113 bb = busboy({
114 headers: { 'content-type': contentType },
115 limits: { fileSize: maxFileBytes, files: 1 },
116 })
117 } catch (err) {
118 reject(
119 new MultipartError(
120 'NOT_MULTIPART',
121 err instanceof Error ? err.message : 'Invalid multipart request'
122 )
123 )
124 return
125 }
126
127 // double-cast-allowed: the web ReadableStream on request.body isn't structurally assignable to the Node type Readable.fromWeb expects
128 const nodeStream = Readable.fromWeb(request.body as unknown as NodeReadableStream<Uint8Array>)
129 const fields: Record<string, string> = {}
130 let settled = false
131 let fileSeen = false
132
133 const onAbort = () => {
134 const reason = signal?.reason instanceof Error ? signal.reason : new Error('Aborted')
135 nodeStream.destroy(reason)
136 bb.destroy()
137 if (!settled) {
138 settled = true
139 reject(reason)
140 }
141 }
142
143 const cleanup = () => {
144 signal?.removeEventListener('abort', onAbort)
145 }
146
147 const settle = (fn: () => void) => {
148 if (settled) return
149 settled = true
150 cleanup()
151 fn()

Callers 3

multipart.test.tsFile · 0.90
route.tsFile · 0.90
route.tsFile · 0.90

Calls 7

onStreamAbortFunction · 0.85
onMethod · 0.80
joinMethod · 0.80
settleFunction · 0.70
getMethod · 0.65
destroyMethod · 0.65
resolveFunction · 0.50

Tested by

no test coverage detected