effect 4.0.0-beta.60 → 4.0.0-beta.63

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 (35) hide show
  1. package/dist/Effect.d.ts +82 -0
  2. package/dist/Effect.d.ts.map +1 -1
  3. package/dist/Effect.js +82 -0
  4. package/dist/Effect.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/internal/effect.js +17 -1
  8. package/dist/internal/effect.js.map +1 -1
  9. package/dist/unstable/cluster/K8sHttpClient.d.ts +1 -1
  10. package/dist/unstable/cluster/K8sHttpClient.js +1 -1
  11. package/dist/unstable/cluster/K8sHttpClient.js.map +1 -1
  12. package/dist/unstable/httpapi/HttpApiBuilder.d.ts +1 -1
  13. package/dist/unstable/httpapi/HttpApiBuilder.d.ts.map +1 -1
  14. package/dist/unstable/httpapi/HttpApiBuilder.js +27 -17
  15. package/dist/unstable/httpapi/HttpApiBuilder.js.map +1 -1
  16. package/dist/unstable/httpapi/HttpApiClient.d.ts.map +1 -1
  17. package/dist/unstable/httpapi/HttpApiClient.js +2 -1
  18. package/dist/unstable/httpapi/HttpApiClient.js.map +1 -1
  19. package/dist/unstable/httpapi/HttpApiTest.d.ts +20 -0
  20. package/dist/unstable/httpapi/HttpApiTest.d.ts.map +1 -0
  21. package/dist/unstable/httpapi/HttpApiTest.js +54 -0
  22. package/dist/unstable/httpapi/HttpApiTest.js.map +1 -0
  23. package/dist/unstable/httpapi/index.d.ts +4 -0
  24. package/dist/unstable/httpapi/index.d.ts.map +1 -1
  25. package/dist/unstable/httpapi/index.js +4 -0
  26. package/dist/unstable/httpapi/index.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/Effect.ts +88 -0
  29. package/src/index.ts +1 -1
  30. package/src/internal/effect.ts +28 -1
  31. package/src/unstable/cluster/K8sHttpClient.ts +3 -3
  32. package/src/unstable/httpapi/HttpApiBuilder.ts +38 -22
  33. package/src/unstable/httpapi/HttpApiClient.ts +4 -3
  34. package/src/unstable/httpapi/HttpApiTest.ts +95 -0
  35. package/src/unstable/httpapi/index.ts +5 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "effect",
3
3
  "type": "module",
4
- "version": "4.0.0-beta.60",
4
+ "version": "4.0.0-beta.63",
5
5
  "license": "MIT",
6
6
  "description": "The missing standard library for TypeScript, for writing production-grade software.",
7
7
  "homepage": "https://effect.website",
package/src/Effect.ts CHANGED
@@ -7762,6 +7762,53 @@ export const orElseSucceed: {
7762
7762
  <A, E, R, A2>(self: Effect<A, E, R>, evaluate: LazyArg<A2>): Effect<A | A2, never, R>
7763
7763
  } = internal.orElseSucceed
7764
7764
 
7765
+ /**
7766
+ * Runs a sequence of effects and returns the result of the first successful
7767
+ * one.
7768
+ *
7769
+ * **Details**
7770
+ *
7771
+ * This function executes the provided effects in sequence, stopping at the
7772
+ * first success. If an effect succeeds, its result is returned immediately and
7773
+ * no further effects in the sequence are executed.
7774
+ *
7775
+ * If all effects fail, the returned effect fails with the error from the last
7776
+ * effect. If the collection is empty, the returned effect defects with an
7777
+ * `Error` whose message is `"Received an empty collection of effects"`.
7778
+ *
7779
+ * **When to Use**
7780
+ *
7781
+ * Use `firstSuccessOf` when you have prioritized fallback strategies, such as
7782
+ * attempting multiple APIs, reading configuration from several sources, or
7783
+ * trying alternative resource locations in order.
7784
+ *
7785
+ * @example
7786
+ * ```ts
7787
+ * import { Effect } from "effect"
7788
+ *
7789
+ * const primary = Effect.fail("primary unavailable")
7790
+ * const secondary = Effect.succeed("secondary result")
7791
+ * const tertiary = Effect.sync(() => {
7792
+ * throw new Error("not evaluated")
7793
+ * })
7794
+ *
7795
+ * const program = Effect.firstSuccessOf([
7796
+ * primary,
7797
+ * secondary,
7798
+ * tertiary
7799
+ * ])
7800
+ *
7801
+ * console.log(Effect.runSync(program))
7802
+ * // Output: "secondary result"
7803
+ * ```
7804
+ *
7805
+ * @since 2.0.0
7806
+ * @category Fallback
7807
+ */
7808
+ export const firstSuccessOf: <Eff extends Effect<any, any, any>>(
7809
+ effects: Iterable<Eff>
7810
+ ) => Effect<Success<Eff>, Error<Eff>, Services<Eff>> = internal.firstSuccessOf
7811
+
7765
7812
  // -----------------------------------------------------------------------------
7766
7813
  // Delays & timeouts
7767
7814
  // -----------------------------------------------------------------------------
@@ -11992,6 +12039,47 @@ export const acquireRelease: <A, E, R, R2>(
11992
12039
  options?: { readonly interruptible?: boolean }
11993
12040
  ) => Effect<A, E, R | R2 | Scope> = internal.acquireRelease
11994
12041
 
12042
+ /**
12043
+ * This function constructs a scoped resource from an Effect that acquires a
12044
+ * disposable value.
12045
+ *
12046
+ * The resource is automatically disposed when the surrounding
12047
+ * {@link Scope} is closed, using {@link Symbol.dispose} for
12048
+ * synchronous disposables or {@link Symbol.asyncDispose} for asynchronous
12049
+ * disposables.
12050
+ *
12051
+ * This is similar to {@link acquireRelease}, but uses the standard
12052
+ * JavaScript disposal protocal instead of requiring an explicit release
12053
+ * function.
12054
+ *
12055
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/using}
12056
+ *
12057
+ * @example
12058
+ * ```ts
12059
+ * import sqlite from "node:sqlite";
12060
+ * import { Effect } from "effect";
12061
+ *
12062
+ * const program = Effect.scoped(
12063
+ * Effect.gen(function* () {
12064
+ * // acquire database connection
12065
+ * // database will be closed when the scope is closed
12066
+ * const db = yield* Effect.acquireDisposable(
12067
+ * Effect.sync(() => new sqlite.DatabaseSync(":memory:"))
12068
+ * )
12069
+ *
12070
+ * const row = db.prepare("SELECT 1 AS value").get()
12071
+ * yield* Effect.log(row) // { value: 1 }
12072
+ * })
12073
+ * )
12074
+ * ```
12075
+ *
12076
+ * @since 4.0.0
12077
+ * @category Resource Management & Finalization
12078
+ */
12079
+ export const acquireDisposable: <A extends AsyncDisposable | Disposable, E, R>(
12080
+ acquire: Effect<A, E, R>
12081
+ ) => Effect<A, E, R | Scope> = internal.acquireDisposable
12082
+
11995
12083
  /**
11996
12084
  * This function is used to ensure that an `Effect` value that represents the
11997
12085
  * acquisition of a resource (for example, opening a file, launching a thread,
package/src/index.ts CHANGED
@@ -3796,7 +3796,7 @@ export * as SchemaRepresentation from "./SchemaRepresentation.ts"
3796
3796
  * - Trim/case strings → {@link trim}, {@link toLowerCase}, {@link toUpperCase}, {@link capitalize}, {@link uncapitalize}, {@link snakeToCamel}
3797
3797
  * - Parse key-value strings → {@link splitKeyValue}
3798
3798
  * - Coerce string ↔ number/bigint → {@link numberFromString}, {@link bigintFromString}
3799
- * - Coerce string ↔ Date → {@link dateFromString}
3799
+ * - Coerce string ↔ Date/Duration → {@link dateFromString}, {@link durationFromString}
3800
3800
  * - Decode durations → {@link durationFromNanos}, {@link durationFromMillis}
3801
3801
  * - Wrap nullable/optional as Option → {@link optionFromNullOr}, {@link optionFromOptionalKey}, {@link optionFromOptional}
3802
3802
  * - Parse URLs → {@link urlFromString}
@@ -511,6 +511,7 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
511
511
  this._children = undefined
512
512
  this._interruptedCause = undefined
513
513
  this._yielded = undefined
514
+ this.runtimeMetrics?.recordFiberStart(this.context)
514
515
  }
515
516
 
516
517
  readonly [FiberTypeId]: Fiber.Fiber.Variance<A, E>
@@ -582,7 +583,6 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
582
583
  return this._exit
583
584
  }
584
585
  evaluate(effect: Primitive): void {
585
- this.runtimeMetrics?.recordFiberStart(this.context)
586
586
  if (this._exit) {
587
587
  return
588
588
  } else if (this._yielded !== undefined) {
@@ -3183,6 +3183,24 @@ export const orElseSucceed: {
3183
3183
  ): Effect.Effect<A | B, never, R> => catch_(self, (_) => sync(f))
3184
3184
  )
3185
3185
 
3186
+ /** @internal */
3187
+ export const firstSuccessOf = <Eff extends Effect.Effect<any, any, any>>(
3188
+ effects: Iterable<Eff>
3189
+ ): Effect.Effect<Effect.Success<Eff>, Effect.Error<Eff>, Effect.Services<Eff>> =>
3190
+ suspend(() => {
3191
+ const iterator = effects[Symbol.iterator]()
3192
+ let state = iterator.next()
3193
+ if (state.done) {
3194
+ return die(new Error("Received an empty collection of effects"))
3195
+ }
3196
+ function loop(current: IteratorYieldResult<Eff>): Eff {
3197
+ const next = iterator.next()
3198
+ if (next.done) return current.value
3199
+ return catch_(current.value, (_) => loop(next)) as any
3200
+ }
3201
+ return loop(state)
3202
+ })
3203
+
3186
3204
  /** @internal */
3187
3205
  export const eventually = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<A, never, R> =>
3188
3206
  catch_(self, (_) => flatMap(yieldNow, () => eventually(self)))
@@ -4035,6 +4053,15 @@ export const acquireUseRelease = <Resource, E, R, A, E2, R2, E3, R3>(
4035
4053
  ))
4036
4054
  )
4037
4055
 
4056
+ /** @internal */
4057
+ export const acquireDisposable = <A extends AsyncDisposable | Disposable, E, R>(
4058
+ acquire: Effect.Effect<A, E, R>
4059
+ ): Effect.Effect<A, E, R | Scope.Scope> =>
4060
+ acquireRelease(acquire, (resource) =>
4061
+ hasProperty(resource, Symbol.asyncDispose)
4062
+ ? promise(() => resource[Symbol.asyncDispose]())
4063
+ : sync(() => resource[Symbol.dispose]()))
4064
+
4038
4065
  // ----------------------------------------------------------------------------
4039
4066
  // Caching
4040
4067
  // ----------------------------------------------------------------------------
@@ -203,7 +203,7 @@ export class PodStatus extends Schema.Class<PodStatus>("@effect/cluster/K8sHttpC
203
203
  conditions: Schema.Array(Schema.Struct({
204
204
  type: Schema.String,
205
205
  status: Schema.String,
206
- lastTransitionTime: Schema.String
206
+ lastTransitionTime: Schema.NullOr(Schema.String)
207
207
  })),
208
208
  podIP: Schema.String,
209
209
  hostIP: Schema.String
@@ -227,8 +227,8 @@ export class Pod extends Schema.Class<Pod>("@effect/cluster/K8sHttpClient/Pod")(
227
227
  }
228
228
 
229
229
  get isReadyOrInitializing(): boolean {
230
- let initializedAt: string | undefined
231
- let readyAt: string | undefined
230
+ let initializedAt: string | null | undefined
231
+ let readyAt: string | null | undefined
232
232
  for (let i = 0; i < this.status.conditions.length; i++) {
233
233
  const condition = this.status.conditions[i]
234
234
  switch (condition.type) {
@@ -15,7 +15,7 @@ import { type Pipeable, pipeArguments } from "../../Pipeable.ts"
15
15
  import * as Redacted from "../../Redacted.ts"
16
16
  import * as Result from "../../Result.ts"
17
17
  import * as Schema from "../../Schema.ts"
18
- import type * as AST from "../../SchemaAST.ts"
18
+ import * as AST from "../../SchemaAST.ts"
19
19
  import * as Issue from "../../SchemaIssue.ts"
20
20
  import * as Transformation from "../../SchemaTransformation.ts"
21
21
  import * as Scope from "../../Scope.ts"
@@ -77,7 +77,7 @@ export const layer = <Id extends string, Groups extends HttpApiGroup.Any>(
77
77
  key.startsWith("effect/httpapi/HttpApiGroup/")
78
78
  )
79
79
  for (const group of Object.values(api.groups)) {
80
- const groupRoutes = services.mapUnsafe.get(group.key) as Array<HttpRouter.Route<any, any>>
80
+ const groupRoutes = services.mapUnsafe.get(group.key)?.routes as Array<HttpRouter.Route<any, any>>
81
81
  if (groupRoutes === undefined) {
82
82
  const available = availableGroups.length === 0 ? "none" : availableGroups.join(", ")
83
83
  return yield* Effect.die(
@@ -130,10 +130,15 @@ export const group = <
130
130
  ? (yield* result as Effect.Effect<any, any, any>)
131
131
  : result
132
132
  const routes: Array<HttpRouter.Route<any, any>> = []
133
- for (const item of handlers.handlers) {
133
+ for (const item of handlers.handlers.values()) {
134
134
  routes.push(handlerToRoute(group as any, item, services))
135
135
  }
136
- return Context.makeUnsafe(new Map([[group.key, routes]]))
136
+ return Context.makeUnsafe(
137
+ new Map([[group.key, {
138
+ routes,
139
+ handlers: handlers.handlers
140
+ }]])
141
+ )
137
142
  })) as any
138
143
 
139
144
  /**
@@ -162,7 +167,7 @@ export interface Handlers<
162
167
  _Endpoints: Covariant<Endpoints>
163
168
  }
164
169
  readonly group: HttpApiGroup.AnyWithProps
165
- readonly handlers: Set<Handlers.Item<R>>
170
+ readonly handlers: Map<string, Handlers.Item<R>>
166
171
 
167
172
  /**
168
173
  * Add the implementation for an `HttpApiEndpoint` to a `Handlers` group.
@@ -449,7 +454,7 @@ const HandlersProto = {
449
454
  options?: { readonly uninterruptible?: boolean | undefined } | undefined
450
455
  ) {
451
456
  const endpoint = this.group.endpoints[name]
452
- this.handlers.add({
457
+ this.handlers.set(name, {
453
458
  endpoint,
454
459
  handler,
455
460
  isRaw: false,
@@ -464,7 +469,7 @@ const HandlersProto = {
464
469
  options?: { readonly uninterruptible?: boolean | undefined } | undefined
465
470
  ) {
466
471
  const endpoint = this.group.endpoints[name]
467
- this.handlers.add({
472
+ this.handlers.set(name, {
468
473
  endpoint,
469
474
  handler,
470
475
  isRaw: true,
@@ -479,7 +484,7 @@ const makeHandlers = <R, Endpoints extends HttpApiEndpoint.Any>(
479
484
  ): Handlers<R, Endpoints> => {
480
485
  const self = Object.create(HandlersProto)
481
486
  self.group = group
482
- self.handlers = new Set<Handlers.Item<R>>()
487
+ self.handlers = new Map<string, Handlers.Item<R>>()
483
488
  return self
484
489
  }
485
490
 
@@ -492,6 +497,7 @@ type PayloadDecoder =
492
497
  }
493
498
  | {
494
499
  readonly _tag: "Json" | "FormUrlEncoded" | "Uint8Array" | "Text"
500
+ readonly nullOnEmpty: boolean
495
501
  readonly decode: (input: unknown) => Effect.Effect<unknown, Schema.SchemaError, unknown>
496
502
  }
497
503
 
@@ -504,7 +510,11 @@ function buildPayloadDecoders(
504
510
  if (encoding._tag === "Multipart") {
505
511
  result.set(contentType, { _tag: "Multipart", mode: encoding.mode, limits: encoding.limits, decode })
506
512
  } else {
507
- result.set(contentType, { _tag: encoding._tag, decode })
513
+ result.set(contentType, {
514
+ _tag: encoding._tag,
515
+ decode,
516
+ nullOnEmpty: schemas.some((s) => AST.isNull(AST.toEncoded(s.ast)))
517
+ })
508
518
  }
509
519
  })
510
520
  return result
@@ -527,21 +537,26 @@ function decodePayload(
527
537
  switch (_tag) {
528
538
  case "Multipart": {
529
539
  if (existing.mode === "buffered") {
530
- return Effect.flatMap(
531
- Effect.orDie(UndefinedOr.match(existing.limits, {
532
- onUndefined: () => httpRequest.multipart,
533
- onDefined: (limits) => Effect.provideContext(httpRequest.multipart, Multipart.limitsServices(limits))
534
- })),
535
- decode
536
- )
540
+ let eff = Effect.orDie(httpRequest.multipart)
541
+ if (existing.limits) {
542
+ eff = Effect.provideContext(eff, Multipart.limitsServices(existing.limits))
543
+ }
544
+ return Effect.flatMap(eff, decode)
537
545
  }
538
- return Effect.succeed(UndefinedOr.match(existing.limits, {
539
- onUndefined: () => httpRequest.multipartStream,
540
- onDefined: (limits) => Stream.provideContext(httpRequest.multipartStream, Multipart.limitsServices(limits))
541
- }))
546
+ return Effect.succeed(
547
+ existing.limits
548
+ ? Stream.provideContext(httpRequest.multipartStream, Multipart.limitsServices(existing.limits))
549
+ : httpRequest.multipartStream
550
+ )
542
551
  }
543
552
  case "Json":
544
- return Effect.flatMap(Effect.orDie(httpRequest.json), decode)
553
+ const json = Effect.orDie(Effect.flatMap(httpRequest.text, (text) => {
554
+ if (text === "") {
555
+ return existing.nullOnEmpty ? Effect.succeed(null) : Effect.undefined
556
+ }
557
+ return Effect.succeed(JSON.parse(text))
558
+ }))
559
+ return Effect.flatMap(json, decode)
545
560
  case "Text":
546
561
  return Effect.flatMap(Effect.orDie(httpRequest.text), decode)
547
562
  case "FormUrlEncoded": {
@@ -622,7 +637,8 @@ function handlerToHttpEffect(
622
637
  )
623
638
  }
624
639
 
625
- function handlerToRoute(
640
+ /** @internal */
641
+ export function handlerToRoute(
626
642
  group: HttpApiGroup.AnyWithProps,
627
643
  handler: Handlers.Item<any>,
628
644
  context: Context.Context<any>
@@ -175,7 +175,8 @@ type UrlBuilderTopLevelMethods<Groups extends HttpApiGroup.Any> = Extract<Groups
175
175
  : never :
176
176
  never
177
177
 
178
- const makeClient = <ApiId extends string, Groups extends HttpApiGroup.Any, E, R>(
178
+ /** @internal */
179
+ export const makeClient = <ApiId extends string, Groups extends HttpApiGroup.Any, E, R>(
179
180
  api: HttpApi.HttpApi<ApiId, Groups>,
180
181
  options: {
181
182
  readonly httpClient: HttpClient.HttpClient.With<E, R>
@@ -201,9 +202,9 @@ const makeClient = <ApiId extends string, Groups extends HttpApiGroup.Any, E, R>
201
202
  | undefined
202
203
  readonly baseUrl?: URL | string | undefined
203
204
  }
204
- ): Effect.Effect<void, unknown, unknown> =>
205
+ ): Effect.Effect<void> =>
205
206
  Effect.gen(function*() {
206
- const services = yield* Effect.context<any>()
207
+ const services = yield* Effect.context()
207
208
 
208
209
  const httpClient = options.httpClient.pipe(
209
210
  options?.baseUrl === undefined
@@ -0,0 +1,95 @@
1
+ /**
2
+ * @since 4.0.0
3
+ */
4
+ import * as Context from "../../Context.ts"
5
+ import * as Effect from "../../Effect.ts"
6
+ import type { FileSystem } from "../../FileSystem.ts"
7
+ import * as Layer from "../../Layer.ts"
8
+ import type { Path } from "../../Path.ts"
9
+ import type { Scope } from "../../Scope.ts"
10
+ import type { Generator } from "../http/Etag.ts"
11
+ import * as HttpClient from "../http/HttpClient.ts"
12
+ import type { HttpPlatform } from "../http/HttpPlatform.ts"
13
+ import * as HttpRouter from "../http/HttpRouter.ts"
14
+ import * as HttpServerRequest from "../http/HttpServerRequest.ts"
15
+ import * as HttpServerResponse from "../http/HttpServerResponse.ts"
16
+ import type * as HttpApi from "./HttpApi.ts"
17
+ import type { Handlers } from "./HttpApiBuilder.ts"
18
+ import * as HttpApiBuilder from "./HttpApiBuilder.ts"
19
+ import * as HttpApiClient from "./HttpApiClient.ts"
20
+ import type * as HttpApiEndpoint from "./HttpApiEndpoint.ts"
21
+ import type * as HttpApiGroup from "./HttpApiGroup.ts"
22
+
23
+ /**
24
+ * @since 4.0.0
25
+ * @category Testing
26
+ */
27
+ export const groups = Effect.fnUntraced(function*<
28
+ ApiId extends string,
29
+ Groups extends HttpApiGroup.Any,
30
+ const Names extends ReadonlyArray<HttpApiGroup.Name<Groups>>,
31
+ SelectedGroups = HttpApiGroup.WithName<Groups, Names[number]>
32
+ >(
33
+ api: HttpApi.HttpApi<ApiId, Groups>,
34
+ groupNames: Names
35
+ ): Effect.fn.Return<
36
+ HttpApiClient.Client<Groups>,
37
+ never,
38
+ | HttpApiGroup.ToService<ApiId, SelectedGroups>
39
+ | HttpApiGroup.MiddlewareClient<Groups>
40
+ | HttpApiEndpoint.Middleware<HttpApiGroup.Endpoints<Groups>>
41
+ | FileSystem
42
+ | Generator
43
+ | HttpPlatform
44
+ | Path
45
+ | Scope
46
+ > {
47
+ let context = yield* Effect.context<HttpApiGroup.ToService<ApiId, SelectedGroups>>()
48
+
49
+ for (const name in api.groups) {
50
+ const group = api.groups[name]
51
+ if (groupNames.includes(name as any)) {
52
+ continue
53
+ }
54
+ const handlers = new Map<string, Handlers.Item<never>>()
55
+ const routes: Array<HttpRouter.Route<any, any>> = []
56
+ for (const endpointName in group.endpoints) {
57
+ const endpoint = group.endpoints[endpointName]
58
+ const handler: Handlers.Item<never> = {
59
+ endpoint: endpoint as any,
60
+ handler: () => Effect.die(new Error(`Unhandled endpoint: ${endpointName}`)),
61
+ isRaw: false,
62
+ uninterruptible: false
63
+ }
64
+ handlers.set(endpointName, handler)
65
+ routes.push(HttpApiBuilder.handlerToRoute(group as any, handler, context))
66
+ }
67
+ context = Context.add(context, group as any, { handlers, routes })
68
+ }
69
+
70
+ const layer: Layer.Layer<
71
+ never,
72
+ never,
73
+ | FileSystem
74
+ | Generator
75
+ | HttpPlatform
76
+ | HttpRouter.HttpRouter
77
+ | Path
78
+ > = HttpApiBuilder.layer(api).pipe(
79
+ Layer.provide(Layer.succeedContext(context))
80
+ ) as any
81
+ const handler = yield* HttpRouter.toHttpEffect(layer)
82
+ const httpClient = HttpClient.make(Effect.fnUntraced(function*(request) {
83
+ const serverRequest = HttpServerRequest.fromClientRequest(request)
84
+ const response = yield* handler.pipe(
85
+ Effect.provideService(HttpServerRequest.HttpServerRequest, serverRequest),
86
+ Effect.orDie
87
+ )
88
+ return HttpServerResponse.toClientResponse(response)
89
+ }, Effect.scoped))
90
+
91
+ return yield* HttpApiClient.makeWith(api, {
92
+ httpClient,
93
+ baseUrl: "http://localhost:3000"
94
+ })
95
+ })
@@ -85,6 +85,11 @@ export * as HttpApiSecurity from "./HttpApiSecurity.ts"
85
85
  */
86
86
  export * as HttpApiSwagger from "./HttpApiSwagger.ts"
87
87
 
88
+ /**
89
+ * @since 4.0.0
90
+ */
91
+ export * as HttpApiTest from "./HttpApiTest.ts"
92
+
88
93
  /**
89
94
  * @since 4.0.0
90
95
  */