MCPcopy Create free account
hub / github.com/anomalyco/opencode / recordingLayer

Function recordingLayer

packages/http-recorder/src/internal-effect.ts:93–182  ·  view source on GitHub ↗
(
  name: string,
  options: Omit<RecordReplayOptions, "directory"> = {},
)

Source from the content-addressed store, hash-verified

91 })
92
93export const recordingLayer = (
94 name: string,
95 options: Omit<RecordReplayOptions, "directory"> = {},
96): Layer.Layer<HttpClient.HttpClient, never, HttpClient.HttpClient | CassetteService.Service> =>
97 Layer.effect(
98 HttpClient.HttpClient,
99 Effect.gen(function* () {
100 const upstream = yield* HttpClient.HttpClient
101 const cassetteService = yield* CassetteService.Service
102 const redactor = options.redactor ?? make()
103 const match = options.match ?? defaultMatcher
104 const requested = options.mode ?? "auto"
105 const mode = requested === "auto" ? yield* resolveAutoMode(cassetteService, name) : requested
106
107 const snapshotRequest = (request: HttpClientRequest.HttpClientRequest) =>
108 Effect.gen(function* () {
109 const web = yield* HttpClientRequest.toWeb(request).pipe(Effect.orDie)
110 return redactor.request({
111 method: web.method,
112 url: web.url,
113 headers: Object.fromEntries(web.headers.entries()),
114 body: yield* Effect.promise(() => web.text()),
115 })
116 })
117
118 if (mode === "passthrough") return upstream
119
120 if (mode === "record") {
121 const initial = yield* Deferred.make<void>()
122 yield* Deferred.succeed(initial, undefined)
123 const tail = yield* Ref.make(initial)
124 return HttpClient.make((request) =>
125 Effect.gen(function* () {
126 const completed = yield* Deferred.make<void>()
127 const previous = yield* Ref.modify(tail, (current) => [current, completed])
128 return yield* Effect.gen(function* () {
129 const incoming = yield* snapshotRequest(request)
130 const response = yield* upstream.execute(request)
131 const captured = yield* captureResponseBody(response, response.headers["content-type"])
132 const responseSnapshot: ResponseSnapshot = {
133 status: response.status,
134 headers: response.headers as Record<string, string>,
135 ...captured,
136 }
137 const interaction: HttpInteraction = {
138 transport: "http",
139 request: incoming,
140 response: redactor.response(responseSnapshot),
141 }
142 yield* Deferred.await(previous)
143 yield* cassetteService
144 .append(name, interaction, options.metadata)
145 .pipe(
146 Effect.catchTag("UnsafeCassetteError", (error) =>
147 Effect.fail(transportError(request, error.message)),
148 ),
149 )
150 return responseFromSnapshot(request, responseSnapshot)

Callers 1

cassetteLayerFunction · 0.70

Calls 12

resolveAutoModeFunction · 0.85
snapshotRequestFunction · 0.85
captureResponseBodyFunction · 0.85
responseFromSnapshotFunction · 0.85
makeReplayStateFunction · 0.85
selectSequentialFunction · 0.85
makeFunction · 0.70
transportErrorFunction · 0.70
executeMethod · 0.65
makeMethod · 0.45
responseMethod · 0.45
appendMethod · 0.45

Tested by

no test coverage detected