@effect/platform 0.69.14 → 0.69.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/HttpApiBuilder.js +27 -25
- package/dist/cjs/HttpApiBuilder.js.map +1 -1
- package/dist/cjs/internal/multipart.js +50 -64
- package/dist/cjs/internal/multipart.js.map +1 -1
- package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
- package/dist/esm/HttpApiBuilder.js +27 -25
- package/dist/esm/HttpApiBuilder.js.map +1 -1
- package/dist/esm/internal/multipart.js +50 -64
- package/dist/esm/internal/multipart.js.map +1 -1
- package/package.json +1 -1
- package/src/HttpApiBuilder.ts +35 -56
- package/src/internal/multipart.ts +120 -150
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import * as Cause from "effect/Cause"
|
|
2
1
|
import * as Channel from "effect/Channel"
|
|
3
2
|
import * as Chunk from "effect/Chunk"
|
|
4
3
|
import * as Effect from "effect/Effect"
|
|
4
|
+
import * as Exit from "effect/Exit"
|
|
5
5
|
import * as FiberRef from "effect/FiberRef"
|
|
6
|
-
import { dual
|
|
6
|
+
import { dual } from "effect/Function"
|
|
7
7
|
import { globalValue } from "effect/GlobalValue"
|
|
8
8
|
import * as Inspectable from "effect/Inspectable"
|
|
9
|
+
import * as Mailbox from "effect/Mailbox"
|
|
9
10
|
import * as Option from "effect/Option"
|
|
10
11
|
import type * as ParseResult from "effect/ParseResult"
|
|
11
12
|
import * as Predicate from "effect/Predicate"
|
|
12
|
-
import * as Queue from "effect/Queue"
|
|
13
13
|
import * as Schema from "effect/Schema"
|
|
14
14
|
import type { ParseOptions } from "effect/SchemaAST"
|
|
15
15
|
import type * as Scope from "effect/Scope"
|
|
@@ -169,28 +169,21 @@ export const schemaJson = <A, I, R>(schema: Schema.Schema<A, I, R>, options?: Pa
|
|
|
169
169
|
export const makeConfig = (
|
|
170
170
|
headers: Record<string, string>
|
|
171
171
|
): Effect.Effect<MP.BaseConfig> =>
|
|
172
|
-
Effect.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
mimeTypes,
|
|
188
|
-
(_) => info.contentType.includes(_)
|
|
189
|
-
) && MP.defaultIsFile(info)
|
|
190
|
-
})
|
|
191
|
-
}),
|
|
192
|
-
(_) => ({ ..._, headers })
|
|
193
|
-
)
|
|
172
|
+
Effect.withFiberRuntime((fiber) => {
|
|
173
|
+
const mimeTypes = fiber.getFiberRef(fieldMimeTypes)
|
|
174
|
+
return Effect.succeed<MP.BaseConfig>({
|
|
175
|
+
headers,
|
|
176
|
+
maxParts: Option.getOrUndefined(fiber.getFiberRef(maxParts)),
|
|
177
|
+
maxFieldSize: Number(fiber.getFiberRef(maxFieldSize)),
|
|
178
|
+
maxPartSize: fiber.getFiberRef(maxFileSize).pipe(Option.map(Number), Option.getOrUndefined),
|
|
179
|
+
maxTotalSize: fiber.getFiberRef(IncomingMessage.maxBodySize).pipe(Option.map(Number), Option.getOrUndefined),
|
|
180
|
+
isFile: mimeTypes.length === 0 ? undefined : (info: MP.PartInfo): boolean =>
|
|
181
|
+
!Chunk.some(
|
|
182
|
+
mimeTypes,
|
|
183
|
+
(_) => info.contentType.includes(_)
|
|
184
|
+
) && MP.defaultIsFile(info)
|
|
185
|
+
})
|
|
186
|
+
})
|
|
194
187
|
|
|
195
188
|
/** @internal */
|
|
196
189
|
export const makeChannel = <IE>(
|
|
@@ -207,52 +200,35 @@ export const makeChannel = <IE>(
|
|
|
207
200
|
Channel.acquireUseRelease(
|
|
208
201
|
Effect.all([
|
|
209
202
|
makeConfig(headers),
|
|
210
|
-
|
|
203
|
+
Mailbox.make<Chunk.Chunk<Uint8Array>>(bufferSize)
|
|
211
204
|
]),
|
|
212
|
-
([config,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
let error = Option.none<Cause.Cause<IE | Multipart.MultipartError>>()
|
|
229
|
-
let partsBuffer: Array<Multipart.Part> = []
|
|
230
|
-
let partsFinished = false
|
|
231
|
-
|
|
232
|
-
const input: AsyncInput.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array>, unknown> = {
|
|
233
|
-
awaitRead: () => Effect.void,
|
|
234
|
-
emit(element) {
|
|
235
|
-
return Queue.offer(queue, element)
|
|
236
|
-
},
|
|
237
|
-
error(cause) {
|
|
238
|
-
error = Option.some(cause)
|
|
239
|
-
return Queue.offer(queue, null)
|
|
240
|
-
},
|
|
241
|
-
done(_value) {
|
|
242
|
-
return Queue.offer(queue, null)
|
|
205
|
+
([config, mailbox]) => {
|
|
206
|
+
let partsBuffer: Array<Multipart.Part> = []
|
|
207
|
+
let exit = Option.none<Exit.Exit<void, IE | Multipart.MultipartError>>()
|
|
208
|
+
|
|
209
|
+
const input: AsyncInput.AsyncInputProducer<IE, Chunk.Chunk<Uint8Array>, unknown> = {
|
|
210
|
+
awaitRead: () => Effect.void,
|
|
211
|
+
emit(element) {
|
|
212
|
+
return mailbox.offer(element)
|
|
213
|
+
},
|
|
214
|
+
error(cause) {
|
|
215
|
+
exit = Option.some(Exit.failCause(cause))
|
|
216
|
+
return mailbox.end
|
|
217
|
+
},
|
|
218
|
+
done(_value) {
|
|
219
|
+
return mailbox.end
|
|
220
|
+
}
|
|
243
221
|
}
|
|
244
|
-
}
|
|
245
222
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
.suspend(() => {
|
|
223
|
+
const parser = MP.make({
|
|
224
|
+
...config,
|
|
225
|
+
onField(info, value) {
|
|
226
|
+
partsBuffer.push(new FieldImpl(info.name, info.contentType, MP.decodeField(info, value)))
|
|
227
|
+
},
|
|
228
|
+
onFile(info) {
|
|
229
|
+
let chunks: Array<Uint8Array> = []
|
|
230
|
+
let finished = false
|
|
231
|
+
const take: Channel.Channel<Chunk.Chunk<Uint8Array>> = Channel.suspend(() => {
|
|
256
232
|
if (chunks.length === 0) {
|
|
257
233
|
return finished ? Channel.void : Channel.zipRight(pump, take)
|
|
258
234
|
}
|
|
@@ -263,65 +239,61 @@ const makeFromQueue = <IE>(
|
|
|
263
239
|
Channel.zipRight(pump, take)
|
|
264
240
|
)
|
|
265
241
|
})
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
242
|
+
partsBuffer.push(new FileImpl(info, take))
|
|
243
|
+
return function(chunk) {
|
|
244
|
+
if (chunk === null) {
|
|
245
|
+
finished = true
|
|
246
|
+
} else {
|
|
247
|
+
chunks.push(chunk)
|
|
248
|
+
}
|
|
272
249
|
}
|
|
250
|
+
},
|
|
251
|
+
onError(error_) {
|
|
252
|
+
exit = Option.some(Exit.fail(convertError(error_)))
|
|
253
|
+
},
|
|
254
|
+
onDone() {
|
|
255
|
+
exit = Option.some(Exit.void)
|
|
273
256
|
}
|
|
274
|
-
}
|
|
275
|
-
onError(error_) {
|
|
276
|
-
error = Option.some(Cause.fail(convertError(error_)))
|
|
277
|
-
},
|
|
278
|
-
onDone() {
|
|
279
|
-
partsFinished = true
|
|
280
|
-
}
|
|
281
|
-
})
|
|
257
|
+
})
|
|
282
258
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
)
|
|
259
|
+
const pump = Channel.flatMap(
|
|
260
|
+
mailbox.takeAll,
|
|
261
|
+
([chunks, done]) =>
|
|
262
|
+
Channel.sync(() => {
|
|
263
|
+
Chunk.forEach(chunks, Chunk.forEach(parser.write))
|
|
264
|
+
if (done) {
|
|
265
|
+
parser.end()
|
|
266
|
+
}
|
|
267
|
+
})
|
|
268
|
+
)
|
|
294
269
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
270
|
+
const partsChannel: Channel.Channel<
|
|
271
|
+
Chunk.Chunk<Multipart.Part>,
|
|
272
|
+
unknown,
|
|
273
|
+
IE | Multipart.MultipartError
|
|
274
|
+
> = Channel.flatMap(
|
|
275
|
+
pump,
|
|
276
|
+
() => {
|
|
277
|
+
if (partsBuffer.length === 0) {
|
|
278
|
+
return exit._tag === "None" ? partsChannel : writeExit(exit.value)
|
|
279
|
+
}
|
|
280
|
+
const chunk = Chunk.unsafeFromArray(partsBuffer)
|
|
281
|
+
partsBuffer = []
|
|
282
|
+
return Channel.zipRight(
|
|
283
|
+
Channel.write(chunk),
|
|
284
|
+
exit._tag === "None" ? partsChannel : writeExit(exit.value)
|
|
285
|
+
)
|
|
300
286
|
}
|
|
301
|
-
|
|
302
|
-
partsBuffer = []
|
|
303
|
-
return Channel.write(parts)
|
|
304
|
-
})
|
|
305
|
-
)
|
|
287
|
+
)
|
|
306
288
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
unknown,
|
|
312
|
-
void,
|
|
313
|
-
unknown
|
|
314
|
-
> = Channel.suspend(() => {
|
|
315
|
-
if (error._tag === "Some") {
|
|
316
|
-
return Channel.failCause(error.value)
|
|
317
|
-
} else if (partsFinished) {
|
|
318
|
-
return Channel.void
|
|
319
|
-
}
|
|
320
|
-
return Channel.zipRight(takeParts, partsChannel)
|
|
321
|
-
})
|
|
289
|
+
return Channel.embedInput(partsChannel, input)
|
|
290
|
+
},
|
|
291
|
+
([, mailbox]) => mailbox.shutdown
|
|
292
|
+
)
|
|
322
293
|
|
|
323
|
-
|
|
324
|
-
|
|
294
|
+
const writeExit = <A, E>(
|
|
295
|
+
self: Exit.Exit<A, E>
|
|
296
|
+
): Channel.Channel<never, unknown, E> => self._tag === "Success" ? Channel.void : Channel.failCause(self.cause)
|
|
325
297
|
|
|
326
298
|
function convertError(cause: MP.MultipartError): Multipart.MultipartError {
|
|
327
299
|
switch (cause._tag) {
|
|
@@ -421,37 +393,35 @@ export const toPersisted = (
|
|
|
421
393
|
stream: Stream.Stream<Multipart.Part, Multipart.MultipartError>,
|
|
422
394
|
writeFile = defaultWriteFile
|
|
423
395
|
): Effect.Effect<Multipart.Persisted, Multipart.MultipartError, FileSystem.FileSystem | Path.Path | Scope.Scope> =>
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
persisted[part.key] = part.value
|
|
436
|
-
return Effect.succeed(persisted)
|
|
437
|
-
}
|
|
438
|
-
const file = part
|
|
439
|
-
const path = path_.join(dir, path_.basename(file.name).slice(-128))
|
|
440
|
-
if (!Array.isArray(persisted[part.key])) {
|
|
441
|
-
persisted[part.key] = []
|
|
442
|
-
}
|
|
443
|
-
;(persisted[part.key] as Array<Multipart.PersistedFile>).push(
|
|
444
|
-
new PersistedFileImpl(
|
|
445
|
-
file.key,
|
|
446
|
-
file.name,
|
|
447
|
-
file.contentType,
|
|
448
|
-
path
|
|
449
|
-
)
|
|
450
|
-
)
|
|
451
|
-
return Effect.as(writeFile(path, file), persisted)
|
|
396
|
+
Effect.gen(function*() {
|
|
397
|
+
const fs = yield* FileSystem.FileSystem
|
|
398
|
+
const path_ = yield* Path.Path
|
|
399
|
+
const dir = yield* fs.makeTempDirectoryScoped()
|
|
400
|
+
return yield* Stream.runFoldEffect(
|
|
401
|
+
stream,
|
|
402
|
+
Object.create(null) as Record<string, Array<Multipart.PersistedFile> | string>,
|
|
403
|
+
(persisted, part) => {
|
|
404
|
+
if (part._tag === "Field") {
|
|
405
|
+
persisted[part.key] = part.value
|
|
406
|
+
return Effect.succeed(persisted)
|
|
452
407
|
}
|
|
453
|
-
|
|
454
|
-
|
|
408
|
+
const file = part
|
|
409
|
+
const path = path_.join(dir, path_.basename(file.name).slice(-128))
|
|
410
|
+
if (!Array.isArray(persisted[part.key])) {
|
|
411
|
+
persisted[part.key] = []
|
|
412
|
+
}
|
|
413
|
+
;(persisted[part.key] as Array<Multipart.PersistedFile>).push(
|
|
414
|
+
new PersistedFileImpl(
|
|
415
|
+
file.key,
|
|
416
|
+
file.name,
|
|
417
|
+
file.contentType,
|
|
418
|
+
path
|
|
419
|
+
)
|
|
420
|
+
)
|
|
421
|
+
return Effect.as(writeFile(path, file), persisted)
|
|
422
|
+
}
|
|
423
|
+
)
|
|
424
|
+
}).pipe(
|
|
455
425
|
Effect.catchTags({
|
|
456
426
|
SystemError: (cause) => Effect.fail(new MultipartError({ reason: "InternalError", cause })),
|
|
457
427
|
BadArgument: (cause) => Effect.fail(new MultipartError({ reason: "InternalError", cause }))
|