@effect/platform 0.69.14 → 0.69.16

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.
@@ -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, flow, pipe } from "effect/Function"
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.map(
173
- Effect.all({
174
- maxParts: Effect.map(FiberRef.get(maxParts), Option.getOrUndefined),
175
- maxFieldSize: Effect.map(FiberRef.get(maxFieldSize), Number),
176
- maxPartSize: Effect.map(FiberRef.get(maxFileSize), flow(Option.map(Number), Option.getOrUndefined)),
177
- maxTotalSize: Effect.map(
178
- FiberRef.get(IncomingMessage.maxBodySize),
179
- flow(Option.map(Number), Option.getOrUndefined)
180
- ),
181
- isFile: Effect.map(FiberRef.get(fieldMimeTypes), (mimeTypes) => {
182
- if (mimeTypes.length === 0) {
183
- return undefined
184
- }
185
- return (info: MP.PartInfo): boolean =>
186
- !Chunk.some(
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
- Queue.bounded<Chunk.Chunk<Uint8Array> | null>(bufferSize)
203
+ Mailbox.make<Chunk.Chunk<Uint8Array>>(bufferSize)
211
204
  ]),
212
- ([config, queue]) => makeFromQueue(config, queue),
213
- ([, queue]) => Queue.shutdown(queue)
214
- )
215
-
216
- const makeFromQueue = <IE>(
217
- config: MP.BaseConfig,
218
- queue: Queue.Queue<Chunk.Chunk<Uint8Array> | null>
219
- ): Channel.Channel<
220
- Chunk.Chunk<Multipart.Part>,
221
- Chunk.Chunk<Uint8Array>,
222
- IE | Multipart.MultipartError,
223
- IE,
224
- unknown,
225
- unknown
226
- > =>
227
- Channel.suspend(() => {
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
- const parser = MP.make({
247
- ...config,
248
- onField(info, value) {
249
- partsBuffer.push(new FieldImpl(info.name, info.contentType, MP.decodeField(info, value)))
250
- },
251
- onFile(info) {
252
- let chunks: Array<Uint8Array> = []
253
- let finished = false
254
- const take: Channel.Channel<Chunk.Chunk<Uint8Array>, unknown, never, unknown, void, unknown> = Channel
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
- partsBuffer.push(new FileImpl(info, take))
267
- return function(chunk) {
268
- if (chunk === null) {
269
- finished = true
270
- } else {
271
- chunks.push(chunk)
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
- const pump = Channel.flatMap(
284
- Queue.take(queue),
285
- (chunk) =>
286
- Channel.sync(() => {
287
- if (chunk === null) {
288
- parser.end()
289
- } else {
290
- Chunk.forEach(chunk, parser.write)
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
- const takeParts = Channel.zipRight(
296
- pump,
297
- Channel.suspend(() => {
298
- if (partsBuffer.length === 0) {
299
- return Channel.void
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
- const parts = Chunk.unsafeFromArray(partsBuffer)
302
- partsBuffer = []
303
- return Channel.write(parts)
304
- })
305
- )
287
+ )
306
288
 
307
- const partsChannel: Channel.Channel<
308
- Chunk.Chunk<Multipart.Part>,
309
- unknown,
310
- IE | Multipart.MultipartError,
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
- return Channel.embedInput(partsChannel, input)
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
- pipe(
425
- Effect.Do,
426
- Effect.bind("fs", () => FileSystem.FileSystem),
427
- Effect.bind("path", () => Path.Path),
428
- Effect.bind("dir", ({ fs }) => fs.makeTempDirectoryScoped()),
429
- Effect.flatMap(({ dir, path: path_ }) =>
430
- Stream.runFoldEffect(
431
- stream,
432
- Object.create(null) as Record<string, Array<Multipart.PersistedFile> | string>,
433
- (persisted, part) => {
434
- if (part._tag === "Field") {
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 }))