@effect/platform 0.84.11 → 0.85.0

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.
Files changed (39) hide show
  1. package/README.md +4 -7
  2. package/dist/cjs/HttpApi.js +1 -1
  3. package/dist/cjs/HttpApi.js.map +1 -1
  4. package/dist/cjs/HttpApiBuilder.js +15 -8
  5. package/dist/cjs/HttpApiBuilder.js.map +1 -1
  6. package/dist/cjs/HttpApiEndpoint.js.map +1 -1
  7. package/dist/cjs/HttpApiSchema.js +28 -1
  8. package/dist/cjs/HttpApiSchema.js.map +1 -1
  9. package/dist/cjs/Multipart.js +5 -1
  10. package/dist/cjs/Multipart.js.map +1 -1
  11. package/dist/cjs/internal/multipart.js +24 -1
  12. package/dist/cjs/internal/multipart.js.map +1 -1
  13. package/dist/dts/HttpApi.d.ts.map +1 -1
  14. package/dist/dts/HttpApiBuilder.d.ts +2 -2
  15. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  16. package/dist/dts/HttpApiEndpoint.d.ts +26 -6
  17. package/dist/dts/HttpApiEndpoint.d.ts.map +1 -1
  18. package/dist/dts/HttpApiSchema.d.ts +31 -0
  19. package/dist/dts/HttpApiSchema.d.ts.map +1 -1
  20. package/dist/dts/Multipart.d.ts +5 -0
  21. package/dist/dts/Multipart.d.ts.map +1 -1
  22. package/dist/esm/HttpApi.js +1 -1
  23. package/dist/esm/HttpApi.js.map +1 -1
  24. package/dist/esm/HttpApiBuilder.js +16 -9
  25. package/dist/esm/HttpApiBuilder.js.map +1 -1
  26. package/dist/esm/HttpApiEndpoint.js.map +1 -1
  27. package/dist/esm/HttpApiSchema.js +25 -0
  28. package/dist/esm/HttpApiSchema.js.map +1 -1
  29. package/dist/esm/Multipart.js +4 -0
  30. package/dist/esm/Multipart.js.map +1 -1
  31. package/dist/esm/internal/multipart.js +23 -0
  32. package/dist/esm/internal/multipart.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/HttpApi.ts +3 -1
  35. package/src/HttpApiBuilder.ts +19 -13
  36. package/src/HttpApiEndpoint.ts +38 -7
  37. package/src/HttpApiSchema.ts +51 -0
  38. package/src/Multipart.ts +12 -0
  39. package/src/internal/multipart.ts +32 -0
@@ -8,12 +8,15 @@ import * as Option from "effect/Option"
8
8
  import { type Pipeable, pipeArguments } from "effect/Pipeable"
9
9
  import * as Predicate from "effect/Predicate"
10
10
  import * as Schema from "effect/Schema"
11
+ import type * as Stream from "effect/Stream"
11
12
  import type * as Types from "effect/Types"
12
13
  import type * as HttpApiMiddleware from "./HttpApiMiddleware.js"
13
14
  import * as HttpApiSchema from "./HttpApiSchema.js"
14
15
  import type { HttpMethod } from "./HttpMethod.js"
15
16
  import * as HttpRouter from "./HttpRouter.js"
17
+ import type { HttpServerRequest } from "./HttpServerRequest.js"
16
18
  import type { HttpServerResponse } from "./HttpServerResponse.js"
19
+ import type * as Multipart from "./Multipart.js"
17
20
 
18
21
  /**
19
22
  * @since 1.0.0
@@ -402,8 +405,34 @@ export declare namespace HttpApiEndpoint {
402
405
  > ?
403
406
  & ([_Path] extends [never] ? {} : { readonly path: _Path })
404
407
  & ([_UrlParams] extends [never] ? {} : { readonly urlParams: _UrlParams })
405
- & ([_Payload] extends [never] ? {} : { readonly payload: _Payload })
408
+ & ([_Payload] extends [never] ? {}
409
+ : _Payload extends Brand<HttpApiSchema.MultipartStreamTypeId> ?
410
+ { readonly payload: Stream.Stream<Multipart.Part, Multipart.MultipartError> }
411
+ : { readonly payload: _Payload })
406
412
  & ([_Headers] extends [never] ? {} : { readonly headers: _Headers })
413
+ & { readonly request: HttpServerRequest }
414
+ : {}
415
+
416
+ /**
417
+ * @since 1.0.0
418
+ * @category models
419
+ */
420
+ export type RequestRaw<Endpoint extends Any> = Endpoint extends HttpApiEndpoint<
421
+ infer _Name,
422
+ infer _Method,
423
+ infer _Path,
424
+ infer _UrlParams,
425
+ infer _Payload,
426
+ infer _Headers,
427
+ infer _Success,
428
+ infer _Error,
429
+ infer _R,
430
+ infer _RE
431
+ > ?
432
+ & ([_Path] extends [never] ? {} : { readonly path: _Path })
433
+ & ([_UrlParams] extends [never] ? {} : { readonly urlParams: _UrlParams })
434
+ & ([_Headers] extends [never] ? {} : { readonly headers: _Headers })
435
+ & { readonly request: HttpServerRequest }
407
436
  : {}
408
437
 
409
438
  /**
@@ -416,7 +445,9 @@ export declare namespace HttpApiEndpoint {
416
445
  & ([Headers] extends [never] ? {} : { readonly headers: Headers })
417
446
  & ([Payload] extends [never] ? {}
418
447
  : Payload extends infer P ?
419
- P extends Brand<HttpApiSchema.MultipartTypeId> ? { readonly payload: FormData } : { readonly payload: P }
448
+ P extends Brand<HttpApiSchema.MultipartTypeId> | Brand<HttpApiSchema.MultipartStreamTypeId>
449
+ ? { readonly payload: FormData }
450
+ : { readonly payload: P }
420
451
  : { readonly payload: Payload })
421
452
  ) extends infer Req ? keyof Req extends never ? (void | { readonly withResponse?: WithResponse }) :
422
453
  Req & { readonly withResponse?: WithResponse } :
@@ -464,15 +495,15 @@ export declare namespace HttpApiEndpoint {
464
495
  */
465
496
  export type Handler<Endpoint extends Any, E, R> = (
466
497
  request: Types.Simplify<Request<Endpoint>>
467
- ) => Effect<Success<Endpoint>, Error<Endpoint> | E, R>
498
+ ) => Effect<Success<Endpoint> | HttpServerResponse, Error<Endpoint> | E, R>
468
499
 
469
500
  /**
470
501
  * @since 1.0.0
471
502
  * @category models
472
503
  */
473
- export type HandlerResponse<Endpoint extends Any, E, R> = (
474
- request: Types.Simplify<Request<Endpoint>>
475
- ) => Effect<HttpServerResponse, Error<Endpoint> | E, R>
504
+ export type HandlerRaw<Endpoint extends Any, E, R> = (
505
+ request: Types.Simplify<RequestRaw<Endpoint>>
506
+ ) => Effect<Success<Endpoint> | HttpServerResponse, Error<Endpoint> | E, R>
476
507
 
477
508
  /**
478
509
  * @since 1.0.0
@@ -500,7 +531,7 @@ export declare namespace HttpApiEndpoint {
500
531
  * @since 1.0.0
501
532
  * @category models
502
533
  */
503
- export type HandlerResponseWithName<Endpoints extends Any, Name extends string, E, R> = HandlerResponse<
534
+ export type HandlerRawWithName<Endpoints extends Any, Name extends string, E, R> = HandlerRaw<
504
535
  WithName<Endpoints, Name>,
505
536
  E,
506
537
  R
@@ -20,6 +20,14 @@ export const AnnotationMultipart: unique symbol = Symbol.for(
20
20
  "@effect/platform/HttpApiSchema/AnnotationMultipart"
21
21
  )
22
22
 
23
+ /**
24
+ * @since 1.0.0
25
+ * @category annotations
26
+ */
27
+ export const AnnotationMultipartStream: unique symbol = Symbol.for(
28
+ "@effect/platform/HttpApiSchema/AnnotationMultipartStream"
29
+ )
30
+
23
31
  /**
24
32
  * @since 1.0.0
25
33
  * @category annotations
@@ -69,6 +77,9 @@ export const extractAnnotations = (ast: AST.Annotations): AST.Annotations => {
69
77
  if (AnnotationMultipart in ast) {
70
78
  result[AnnotationMultipart] = ast[AnnotationMultipart]
71
79
  }
80
+ if (AnnotationMultipartStream in ast) {
81
+ result[AnnotationMultipartStream] = ast[AnnotationMultipartStream]
82
+ }
72
83
  return result
73
84
  }
74
85
 
@@ -102,6 +113,13 @@ export const getEmptyDecodeable = (ast: AST.AST): boolean =>
102
113
  */
103
114
  export const getMultipart = (ast: AST.AST): boolean => getAnnotation<boolean>(ast, AnnotationMultipart) ?? false
104
115
 
116
+ /**
117
+ * @since 1.0.0
118
+ * @category annotations
119
+ */
120
+ export const getMultipartStream = (ast: AST.AST): boolean =>
121
+ getAnnotation<boolean>(ast, AnnotationMultipartStream) ?? false
122
+
105
123
  const encodingJson: Encoding = {
106
124
  kind: "Json",
107
125
  contentType: "application/json"
@@ -411,6 +429,39 @@ export const Multipart = <S extends Schema.Schema.Any>(self: S): Multipart<S> =>
411
429
  [AnnotationMultipart]: true
412
430
  }) as any
413
431
 
432
+ /**
433
+ * @since 1.0.0
434
+ * @category multipart
435
+ */
436
+ export const MultipartStreamTypeId: unique symbol = Symbol.for("@effect/platform/HttpApiSchema/MultipartStream")
437
+
438
+ /**
439
+ * @since 1.0.0
440
+ * @category multipart
441
+ */
442
+ export type MultipartStreamTypeId = typeof MultipartStreamTypeId
443
+
444
+ /**
445
+ * @since 1.0.0
446
+ * @category multipart
447
+ */
448
+ export interface MultipartStream<S extends Schema.Schema.Any> extends
449
+ Schema.Schema<
450
+ Schema.Schema.Type<S> & Brand<MultipartStreamTypeId>,
451
+ Schema.Schema.Encoded<S>,
452
+ Schema.Schema.Context<S>
453
+ >
454
+ {}
455
+
456
+ /**
457
+ * @since 1.0.0
458
+ * @category multipart
459
+ */
460
+ export const MultipartStream = <S extends Schema.Schema.Any>(self: S): MultipartStream<S> =>
461
+ self.annotations({
462
+ [AnnotationMultipartStream]: true
463
+ }) as any
464
+
414
465
  const defaultContentType = (encoding: Encoding["kind"]) => {
415
466
  switch (encoding) {
416
467
  case "Json": {
package/src/Multipart.ts CHANGED
@@ -83,6 +83,7 @@ export interface File extends Part.Proto {
83
83
  readonly name: string
84
84
  readonly contentType: string
85
85
  readonly content: Stream.Stream<Uint8Array, MultipartError>
86
+ readonly contentEffect: Effect.Effect<Uint8Array, MultipartError>
86
87
  }
87
88
 
88
89
  /**
@@ -310,3 +311,14 @@ export const toPersisted: (
310
311
  stream: Stream.Stream<Part, MultipartError>,
311
312
  writeFile?: (path: string, file: File) => Effect.Effect<void, MultipartError, FileSystem.FileSystem>
312
313
  ) => Effect.Effect<Persisted, MultipartError, FileSystem.FileSystem | Path.Path | Scope.Scope> = internal.toPersisted
314
+
315
+ /**
316
+ * @since 1.0.0
317
+ */
318
+ export const collectUint8Array: Channel.Channel<
319
+ never,
320
+ Chunk.Chunk<Uint8Array>,
321
+ unknown,
322
+ unknown,
323
+ Uint8Array
324
+ > = internal.collectUint8Array
@@ -1,3 +1,4 @@
1
+ import type { Cause } from "effect/Cause"
1
2
  import * as Channel from "effect/Channel"
2
3
  import * as Chunk from "effect/Chunk"
3
4
  import * as Effect from "effect/Effect"
@@ -355,6 +356,7 @@ class FileImpl extends PartBase implements Multipart.File {
355
356
  readonly name: string
356
357
  readonly contentType: string
357
358
  readonly content: Stream.Stream<Uint8Array, Multipart.MultipartError>
359
+ readonly contentEffect: Effect.Effect<Uint8Array, Multipart.MultipartError>
358
360
 
359
361
  constructor(
360
362
  info: MP.PartInfo,
@@ -365,6 +367,11 @@ class FileImpl extends PartBase implements Multipart.File {
365
367
  this.name = info.filename ?? info.name
366
368
  this.contentType = info.contentType
367
369
  this.content = Stream.fromChannel(channel)
370
+ this.contentEffect = channel.pipe(
371
+ Channel.pipeTo(collectUint8Array),
372
+ Channel.run,
373
+ Effect.mapError((cause) => new MultipartError({ reason: "InternalError", cause }))
374
+ )
368
375
  }
369
376
 
370
377
  toJSON(): unknown {
@@ -388,6 +395,31 @@ const defaultWriteFile = (path: string, file: Multipart.File) =>
388
395
  )
389
396
  )
390
397
 
398
+ /** @internal */
399
+ export const collectUint8Array = Channel.suspend(() => {
400
+ let accumulator = new Uint8Array(0)
401
+ const loop: Channel.Channel<
402
+ never,
403
+ Chunk.Chunk<Uint8Array>,
404
+ unknown,
405
+ unknown,
406
+ Uint8Array
407
+ > = Channel.readWithCause({
408
+ onInput(chunk: Chunk.Chunk<Uint8Array>) {
409
+ for (const element of chunk) {
410
+ const newAccumulator = new Uint8Array(accumulator.length + element.length)
411
+ newAccumulator.set(accumulator, 0)
412
+ newAccumulator.set(element, accumulator.length)
413
+ accumulator = newAccumulator
414
+ }
415
+ return loop
416
+ },
417
+ onFailure: (cause: Cause<unknown>) => Channel.failCause(cause),
418
+ onDone: () => Channel.succeed(accumulator)
419
+ })
420
+ return loop
421
+ })
422
+
391
423
  /** @internal */
392
424
  export const toPersisted = (
393
425
  stream: Stream.Stream<Multipart.Part, Multipart.MultipartError>,