@effect/platform 0.69.16 → 0.69.18

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 (44) hide show
  1. package/dist/cjs/HttpApi.js +13 -0
  2. package/dist/cjs/HttpApi.js.map +1 -1
  3. package/dist/cjs/HttpApiBuilder.js +2 -1
  4. package/dist/cjs/HttpApiBuilder.js.map +1 -1
  5. package/dist/cjs/HttpApiGroup.js.map +1 -1
  6. package/dist/cjs/HttpApiSchema.js +1 -3
  7. package/dist/cjs/HttpApiSchema.js.map +1 -1
  8. package/dist/cjs/Socket.js +30 -37
  9. package/dist/cjs/Socket.js.map +1 -1
  10. package/dist/cjs/internal/worker.js +22 -34
  11. package/dist/cjs/internal/worker.js.map +1 -1
  12. package/dist/cjs/internal/workerRunner.js +1 -1
  13. package/dist/cjs/internal/workerRunner.js.map +1 -1
  14. package/dist/dts/HttpApi.d.ts +5 -1
  15. package/dist/dts/HttpApi.d.ts.map +1 -1
  16. package/dist/dts/HttpApiBuilder.d.ts +1 -1
  17. package/dist/dts/HttpApiBuilder.d.ts.map +1 -1
  18. package/dist/dts/HttpApiGroup.d.ts +7 -2
  19. package/dist/dts/HttpApiGroup.d.ts.map +1 -1
  20. package/dist/dts/Socket.d.ts.map +1 -1
  21. package/dist/dts/Worker.d.ts +1 -1
  22. package/dist/dts/Worker.d.ts.map +1 -1
  23. package/dist/esm/HttpApi.js +13 -0
  24. package/dist/esm/HttpApi.js.map +1 -1
  25. package/dist/esm/HttpApiBuilder.js +2 -1
  26. package/dist/esm/HttpApiBuilder.js.map +1 -1
  27. package/dist/esm/HttpApiGroup.js.map +1 -1
  28. package/dist/esm/HttpApiSchema.js +1 -3
  29. package/dist/esm/HttpApiSchema.js.map +1 -1
  30. package/dist/esm/Socket.js +30 -37
  31. package/dist/esm/Socket.js.map +1 -1
  32. package/dist/esm/internal/worker.js +22 -34
  33. package/dist/esm/internal/worker.js.map +1 -1
  34. package/dist/esm/internal/workerRunner.js +1 -1
  35. package/dist/esm/internal/workerRunner.js.map +1 -1
  36. package/package.json +2 -2
  37. package/src/HttpApi.ts +29 -1
  38. package/src/HttpApiBuilder.ts +3 -2
  39. package/src/HttpApiGroup.ts +11 -2
  40. package/src/HttpApiSchema.ts +6 -6
  41. package/src/Socket.ts +34 -48
  42. package/src/Worker.ts +1 -1
  43. package/src/internal/worker.ts +35 -41
  44. package/src/internal/workerRunner.ts +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect/platform",
3
- "version": "0.69.16",
3
+ "version": "0.69.18",
4
4
  "description": "Unified interfaces for common platform-specific services",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,7 +14,7 @@
14
14
  "multipasta": "^0.2.5"
15
15
  },
16
16
  "peerDependencies": {
17
- "effect": "^3.10.10"
17
+ "effect": "^3.10.12"
18
18
  },
19
19
  "publishConfig": {
20
20
  "provenance": true
package/src/HttpApi.ts CHANGED
@@ -9,6 +9,7 @@ import { type Pipeable, pipeArguments } from "effect/Pipeable"
9
9
  import * as Predicate from "effect/Predicate"
10
10
  import type * as Schema from "effect/Schema"
11
11
  import * as AST from "effect/SchemaAST"
12
+ import type { Mutable } from "effect/Types"
12
13
  import type * as HttpApiEndpoint from "./HttpApiEndpoint.js"
13
14
  import { HttpApiDecodeError } from "./HttpApiError.js"
14
15
  import type * as HttpApiGroup from "./HttpApiGroup.js"
@@ -57,9 +58,17 @@ export interface HttpApi<
57
58
  readonly middlewares: HashSet.HashSet<HttpApiMiddleware.TagClassAny>
58
59
 
59
60
  /**
60
- * Add an endpoint to the `HttpApi`.
61
+ * Add a `HttpApiGroup` to the `HttpApi`.
61
62
  */
62
63
  add<A extends HttpApiGroup.HttpApiGroup.Any>(group: A): HttpApi<Groups | A, E, R>
64
+ /**
65
+ * Add another `HttpApi` to the `HttpApi`.
66
+ */
67
+ addHttpApi<Groups2 extends HttpApiGroup.HttpApiGroup.Any, E2, R2>(api: HttpApi<Groups2, E2, R2>): HttpApi<
68
+ Groups | HttpApiGroup.HttpApiGroup.AddContext<Groups2, R2>,
69
+ E | E2,
70
+ R
71
+ >
63
72
  /**
64
73
  * Add an global error to the `HttpApi`.
65
74
  */
@@ -142,6 +151,25 @@ const Proto = {
142
151
  middlewares: this.middlewares
143
152
  })
144
153
  },
154
+ addHttpApi(
155
+ this: HttpApi.AnyWithProps,
156
+ api: HttpApi.AnyWithProps
157
+ ) {
158
+ return makeProto({
159
+ groups: HashMap.union(
160
+ this.groups,
161
+ HashMap.map(api.groups, (group) => {
162
+ const newGroup: Mutable<HttpApiGroup.HttpApiGroup.AnyWithProps> = group.annotateContext(Context.empty())
163
+ newGroup.annotations = Context.merge(api.annotations, newGroup.annotations)
164
+ newGroup.middlewares = HashSet.union(newGroup.middlewares, api.middlewares)
165
+ return newGroup
166
+ })
167
+ ),
168
+ errorSchema: HttpApiSchema.UnionUnify(this.errorSchema, api.errorSchema),
169
+ annotations: this.annotations,
170
+ middlewares: this.middlewares
171
+ })
172
+ },
145
173
  addError(
146
174
  this: HttpApi.AnyWithProps,
147
175
  schema: Schema.Schema.Any,
@@ -438,7 +438,7 @@ export const group = <
438
438
  handlers: Handlers.FromGroup<ApiError, ApiR, HttpApiGroup.HttpApiGroup.WithName<Groups, Name>>
439
439
  ) => Handlers.ValidateReturn<Return>
440
440
  ): Layer.Layer<
441
- HttpApiGroup.Group<Name>,
441
+ HttpApiGroup.ApiGroup<Name>,
442
442
  Handlers.Error<Return>,
443
443
  | Handlers.Context<Return>
444
444
  | HttpApiGroup.HttpApiGroup.ContextWithName<Groups, Name>
@@ -982,6 +982,7 @@ export const middlewareOpenApi = (
982
982
  )
983
983
 
984
984
  const bearerLen = `Bearer `.length
985
+ const basicLen = `Basic `.length
985
986
 
986
987
  /**
987
988
  * @since 1.0.0
@@ -1023,7 +1024,7 @@ export const securityDecode = <Security extends HttpApiSecurity.HttpApiSecurity>
1023
1024
  password: Redacted.make("")
1024
1025
  } as any
1025
1026
  return HttpServerRequest.HttpServerRequest.pipe(
1026
- Effect.flatMap((request) => Encoding.decodeBase64String(request.headers.authorization ?? "")),
1027
+ Effect.flatMap((request) => Encoding.decodeBase64String((request.headers.authorization ?? "").slice(basicLen))),
1027
1028
  Effect.match({
1028
1029
  onFailure: () => empty,
1029
1030
  onSuccess: (header) => {
@@ -140,7 +140,7 @@ export interface HttpApiGroup<
140
140
  * @since 1.0.0
141
141
  * @category models
142
142
  */
143
- export interface Group<Name extends string> {
143
+ export interface ApiGroup<Name extends string> {
144
144
  readonly _: unique symbol
145
145
  readonly name: Name
146
146
  }
@@ -170,7 +170,7 @@ export declare namespace HttpApiGroup {
170
170
  * @category models
171
171
  */
172
172
  export type ToService<A> = A extends
173
- HttpApiGroup<infer Name, infer _Endpoints, infer _Error, infer _R, infer _TopLevel> ? Group<Name>
173
+ HttpApiGroup<infer Name, infer _Endpoints, infer _Error, infer _R, infer _TopLevel> ? ApiGroup<Name>
174
174
  : never
175
175
 
176
176
  /**
@@ -209,6 +209,15 @@ export declare namespace HttpApiGroup {
209
209
  HttpApiGroup<infer _Name, infer _Endpoints, infer _Error, infer _R, infer _TopLevel> ? _Error
210
210
  : never
211
211
 
212
+ /**
213
+ * @since 1.0.0
214
+ * @category models
215
+ */
216
+ export type AddContext<Group, R> = [R] extends [never] ? Group :
217
+ Group extends HttpApiGroup<infer _Name, infer _Endpoints, infer _Error, infer _R, infer _TopLevel> ?
218
+ HttpApiGroup<_Name, _Endpoints, _Error, _R | R, _TopLevel>
219
+ : never
220
+
212
221
  /**
213
222
  * @since 1.0.0
214
223
  * @category models
@@ -147,12 +147,12 @@ export const UnionUnify = <A extends Schema.Schema.All, B extends Schema.Schema.
147
147
  A["Encoded"] | B["Encoded"],
148
148
  A["Context"] | B["Context"]
149
149
  > => {
150
- const selfTypes = self.ast._tag === "Union" ? self.ast.types : [self.ast]
151
- const thatTypes = that.ast._tag === "Union" ? that.ast.types : [that.ast]
152
- return Schema.make(AST.Union.make([
153
- ...selfTypes,
154
- ...thatTypes
155
- ]))
150
+ return Schema.make(AST.Union.make(Array.from(
151
+ new Set<AST.AST>([
152
+ ...(self.ast._tag === "Union" ? self.ast.types : [self.ast]),
153
+ ...(that.ast._tag === "Union" ? that.ast.types : [that.ast])
154
+ ])
155
+ )))
156
156
  }
157
157
 
158
158
  /**
package/src/Socket.ts CHANGED
@@ -397,7 +397,7 @@ export const fromWebSocket = <RO>(
397
397
  ): Effect.Effect<Socket, never, Exclude<RO, Scope.Scope>> =>
398
398
  Effect.gen(function*() {
399
399
  const fiber = Option.getOrThrow(Fiber.getCurrentFiber())
400
- const sendQueue = yield* Mailbox.make<Uint8Array | string, SocketError>({
400
+ const sendQueue = yield* Mailbox.make<Uint8Array | string | CloseEvent>({
401
401
  capacity: fiber.getFiberRef(currentSendQueueCapacity),
402
402
  strategy: "dropping"
403
403
  })
@@ -465,22 +465,25 @@ export const fromWebSocket = <RO>(
465
465
  )
466
466
  }
467
467
  open = true
468
- yield* sendQueue.takeAll.pipe(
469
- Effect.tap(([chunk]) =>
470
- Effect.try({
471
- try: () => {
472
- for (const item of chunk) {
473
- ws.send(item)
474
- }
475
- },
468
+ yield* sendQueue.take.pipe(
469
+ Effect.tap((chunk) => {
470
+ if (isCloseEvent(chunk)) {
471
+ ws.close(chunk.code, chunk.reason)
472
+ return Effect.fail(
473
+ new SocketCloseError({
474
+ reason: "Close",
475
+ code: chunk.code,
476
+ closeReason: chunk.reason
477
+ })
478
+ )
479
+ }
480
+ return Effect.try({
481
+ try: () => ws.send(chunk),
476
482
  catch: (cause) => new SocketGenericError({ reason: "Write", cause })
477
483
  })
478
- ),
479
- Effect.forever,
480
- Effect.catchIf(SocketCloseError.is, (error) => {
481
- ws.close(error.code, error.closeReason)
482
- return Effect.fail(error)
483
484
  }),
485
+ Effect.forever,
486
+ Effect.catchTag("NoSuchElementException", () => Effect.void),
484
487
  FiberSet.run(fiberSet)
485
488
  )
486
489
  return yield* FiberSet.join(fiberSet).pipe(
@@ -503,16 +506,7 @@ export const fromWebSocket = <RO>(
503
506
  : handler(data)
504
507
  )
505
508
 
506
- const write = (chunk: Uint8Array | string | CloseEvent) =>
507
- isCloseEvent(chunk)
508
- ? sendQueue.fail(
509
- new SocketCloseError({
510
- reason: "Close",
511
- code: chunk.code,
512
- closeReason: chunk.reason
513
- })
514
- )
515
- : sendQueue.offer(chunk)
509
+ const write = (chunk: Uint8Array | string | CloseEvent) => sendQueue.offer(chunk)
516
510
  const writer = Effect.succeed(write)
517
511
 
518
512
  return Socket.of({
@@ -584,7 +578,7 @@ export const fromTransformStream = <R>(acquire: Effect.Effect<InputTransformStre
584
578
  }): Effect.Effect<Socket, never, Exclude<R, Scope.Scope>> =>
585
579
  Effect.gen(function*() {
586
580
  const fiber = Option.getOrThrow(Fiber.getCurrentFiber())
587
- const sendQueue = yield* Mailbox.make<Uint8Array | string, SocketError>({
581
+ const sendQueue = yield* Mailbox.make<Uint8Array | string | CloseEvent>({
588
582
  capacity: fiber.getFiberRef(currentSendQueueCapacity),
589
583
  strategy: "dropping"
590
584
  })
@@ -608,23 +602,24 @@ export const fromTransformStream = <R>(acquire: Effect.Effect<InputTransformStre
608
602
  )
609
603
  const fiberSet = yield* FiberSet.make<any, E | SocketError>()
610
604
  const encoder = new TextEncoder()
611
- yield* sendQueue.takeAll.pipe(
612
- Effect.flatMap(([chunk, done]) => {
613
- const write = Effect.try({
614
- try: () => {
615
- for (const item of chunk) {
616
- if (typeof item === "string") {
617
- writer.write(encoder.encode(item))
618
- } else {
619
- writer.write(item)
620
- }
621
- }
622
- },
605
+ yield* sendQueue.take.pipe(
606
+ Effect.tap((chunk) => {
607
+ if (isCloseEvent(chunk)) {
608
+ return Effect.fail(
609
+ new SocketCloseError({
610
+ reason: "Close",
611
+ code: chunk.code,
612
+ closeReason: chunk.reason
613
+ })
614
+ )
615
+ }
616
+ return Effect.tryPromise({
617
+ try: () => writer.write(typeof chunk === "string" ? encoder.encode(chunk) : chunk),
623
618
  catch: (cause) => new SocketGenericError({ reason: "Write", cause })
624
619
  })
625
- return done ? Effect.zipRight(write, Effect.interrupt) : write
626
620
  }),
627
621
  Effect.forever,
622
+ Effect.catchTag("NoSuchElementException", () => Effect.void),
628
623
  Effect.ensuring(Effect.promise(() => writer.close())),
629
624
  FiberSet.run(fiberSet)
630
625
  )
@@ -663,16 +658,7 @@ export const fromTransformStream = <R>(acquire: Effect.Effect<InputTransformStre
663
658
  : handler(data)
664
659
  )
665
660
 
666
- const write = (chunk: Uint8Array | string | CloseEvent) =>
667
- isCloseEvent(chunk) ?
668
- sendQueue.fail(
669
- new SocketCloseError({
670
- reason: "Close",
671
- code: chunk.code,
672
- closeReason: chunk.reason
673
- })
674
- ) :
675
- sendQueue.offer(chunk)
661
+ const write = (chunk: Uint8Array | string | CloseEvent) => sendQueue.offer(chunk)
676
662
  const writer = Effect.acquireRelease(
677
663
  Effect.succeed(write),
678
664
  () => sendQueue.end
package/src/Worker.ts CHANGED
@@ -66,7 +66,7 @@ export const makePlatform: <W>() => <
66
66
  P extends { readonly postMessage: (message: any, transfers?: any | undefined) => void }
67
67
  >(
68
68
  options: {
69
- readonly setup: (options: { readonly worker: W; readonly scope: Scope.Scope }) => Effect.Effect<P>
69
+ readonly setup: (options: { readonly worker: W; readonly scope: Scope.Scope }) => Effect.Effect<P, WorkerError>
70
70
  readonly listen: (
71
71
  options: {
72
72
  readonly port: P
@@ -1,3 +1,4 @@
1
+ import { Runtime } from "effect"
1
2
  import * as Channel from "effect/Channel"
2
3
  import * as Context from "effect/Context"
3
4
  import * as Deferred from "effect/Deferred"
@@ -86,6 +87,7 @@ export const makeManager = Effect.gen(function*() {
86
87
  ? Deferred.failCause(mailbox, cause)
87
88
  : mailbox.failCause(cause))
88
89
  ),
90
+ Effect.tapErrorCause(Effect.logWarning),
89
91
  Effect.retry(Schedule.spaced(1000)),
90
92
  Effect.annotateLogs({
91
93
  package: "@effect/platform",
@@ -401,7 +403,7 @@ export const makePlatform = <W>() =>
401
403
  readonly setup: (options: {
402
404
  readonly worker: W
403
405
  readonly scope: Scope.Scope
404
- }) => Effect.Effect<P>
406
+ }) => Effect.Effect<P, WorkerError>
405
407
  readonly listen: (options: {
406
408
  readonly port: P
407
409
  readonly emit: (data: any) => void
@@ -417,49 +419,41 @@ export const makePlatform = <W>() =>
417
419
  let currentPort: P | undefined
418
420
  const buffer: Array<[I, ReadonlyArray<unknown> | undefined]> = []
419
421
 
420
- const run = <A, E, R>(handler: (_: Worker.BackingWorker.Message<O>) => Effect.Effect<A, E, R>) =>
422
+ const run = <A, E, R>(
423
+ handler: (_: Worker.BackingWorker.Message<O>) => Effect.Effect<A, E, R>
424
+ ): Effect.Effect<never, WorkerError | E, R> =>
421
425
  Effect.uninterruptibleMask((restore) =>
422
- Scope.make().pipe(
423
- Effect.bindTo("scope"),
424
- Effect.bind("port", ({ scope }) => options.setup({ worker: spawn(id), scope })),
425
- Effect.tap(({ port, scope }) => {
426
- currentPort = port
427
- return Scope.addFinalizer(
428
- scope,
429
- Effect.sync(() => {
430
- currentPort = undefined
431
- })
432
- )
433
- }),
434
- Effect.bind("fiberSet", ({ scope }) =>
435
- FiberSet.make<any, WorkerError | E>().pipe(
436
- Scope.extend(scope)
437
- )),
438
- Effect.bind("runFork", ({ fiberSet }) => FiberSet.runtime(fiberSet)<R>()),
439
- Effect.tap(({ fiberSet, port, runFork, scope }) =>
440
- options.listen({
441
- port,
442
- scope,
443
- emit(data) {
444
- runFork(handler(data))
445
- },
446
- deferred: fiberSet.deferred as any
426
+ Effect.gen(function*() {
427
+ const scope = yield* Effect.scope
428
+ const port = yield* options.setup({ worker: spawn(id), scope })
429
+ currentPort = port
430
+ yield* Scope.addFinalizer(
431
+ scope,
432
+ Effect.sync(() => {
433
+ currentPort = undefined
447
434
  })
448
- ),
449
- Effect.tap(({ port }) => {
450
- if (buffer.length > 0) {
451
- for (const [message, transfers] of buffer) {
452
- port.postMessage([0, message], transfers as any)
453
- }
454
- buffer.length = 0
455
- }
456
- }),
457
- Effect.flatMap(({ fiberSet, scope }) =>
458
- (restore(FiberSet.join(fiberSet)) as Effect.Effect<never, WorkerError | E>).pipe(
459
- Effect.ensuring(Scope.close(scope, Exit.void))
460
- )
461
435
  )
462
- )
436
+ const runtime = (yield* Effect.runtime<R | Scope.Scope>()).pipe(
437
+ Runtime.updateContext(Context.omit(Scope.Scope))
438
+ ) as Runtime.Runtime<R>
439
+ const fiberSet = yield* FiberSet.make<any, WorkerError | E>()
440
+ const runFork = Runtime.runFork(runtime)
441
+ yield* options.listen({
442
+ port,
443
+ scope,
444
+ emit(data) {
445
+ FiberSet.unsafeAdd(runFork(handler(data)))
446
+ },
447
+ deferred: fiberSet.deferred as any
448
+ })
449
+ if (buffer.length > 0) {
450
+ for (const [message, transfers] of buffer) {
451
+ port.postMessage([0, message], transfers as any)
452
+ }
453
+ buffer.length = 0
454
+ }
455
+ return (yield* restore(FiberSet.join(fiberSet))) as never
456
+ }).pipe(Effect.scoped)
463
457
  )
464
458
 
465
459
  const send = (message: I, transfers?: ReadonlyArray<unknown>) =>
@@ -127,7 +127,7 @@ export const make = <I, E, R, O>(
127
127
  ): Effect.Effect<void, WorkerError, WorkerRunner.PlatformRunner | R | Scope.Scope> =>
128
128
  Effect.withFiberRuntime<void, never, WorkerRunner.PlatformRunner | R | Scope.Scope>((fiber) =>
129
129
  run(process, options).pipe(
130
- Effect.tapErrorCause(Effect.logDebug),
130
+ Effect.tapErrorCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.void : Effect.logWarning(cause)),
131
131
  Effect.retry(Schedule.spaced(1000)),
132
132
  Effect.annotateLogs({
133
133
  package: "@effect/platform-node",