@effect-app/infra 4.0.0-beta.237 → 4.0.0-beta.239
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.
- package/CHANGELOG.md +14 -0
- package/dist/api/internal/RequestContextMiddleware.d.ts +2 -2
- package/dist/api/internal/RequestContextMiddleware.d.ts.map +1 -1
- package/dist/api/internal/RequestContextMiddleware.js +9 -2
- package/dist/api/internal/events.d.ts +2 -2
- package/dist/api/internal/events.d.ts.map +1 -1
- package/dist/api/internal/events.js +3 -3
- package/dist/api/setupRequest.d.ts +4 -1
- package/dist/api/setupRequest.d.ts.map +1 -1
- package/dist/api/setupRequest.js +15 -1
- package/package.json +2 -2
- package/src/api/internal/RequestContextMiddleware.ts +8 -1
- package/src/api/internal/events.ts +2 -2
- package/src/api/setupRequest.ts +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @effect-app/infra
|
|
2
2
|
|
|
3
|
+
## 4.0.0-beta.239
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- bb3f51d: Fix ContextMap finalizer running mid-stream on SSE responses. SSE handler now binds ContextMapContainer to the request scope via a shared `provideOnRequestScope` helper (also used by `RequestContextMiddleware`), so finalizers only run after the response body is fully drained. Adds `setupStreamingRequestContextFromCurrent` for use by streaming HTTP handlers.
|
|
8
|
+
- effect-app@4.0.0-beta.239
|
|
9
|
+
|
|
10
|
+
## 4.0.0-beta.238
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- b63f10f: fix: nasty request scope bug
|
|
15
|
+
- effect-app@4.0.0-beta.238
|
|
16
|
+
|
|
3
17
|
## 4.0.0-beta.237
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as Effect from "effect-app/Effect";
|
|
2
2
|
import { HttpServerRequest, HttpServerResponse } from "effect-app/http";
|
|
3
3
|
import { Locale } from "../../RequestContext.js";
|
|
4
|
-
export declare const RequestContextMiddleware: (defaultLocale?: Locale) => <E, R>(app: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R | HttpServerRequest.HttpServerRequest>) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | Exclude<R, never>>;
|
|
5
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
4
|
+
export declare const RequestContextMiddleware: (defaultLocale?: Locale) => <E, R>(app: Effect.Effect<HttpServerResponse.HttpServerResponse, E, R | HttpServerRequest.HttpServerRequest>) => Effect.Effect<HttpServerResponse.HttpServerResponse, E, HttpServerRequest.HttpServerRequest | import("effect/Scope").Scope | Exclude<R, never>>;
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVxdWVzdENvbnRleHRNaWRkbGV3YXJlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL2ludGVybmFsL1JlcXVlc3RDb250ZXh0TWlkZGxld2FyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sRUFBa0IsaUJBQWlCLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUd2RixPQUFPLEVBQUUsTUFBTSxFQUE2QyxNQUFNLHlCQUF5QixDQUFBO0FBSTNGLGVBQU8sTUFBTSx3QkFBd0IsbUJBQW1CLE1BQU0scVFBb0QzRCxDQUFBIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RequestContextMiddleware.d.ts","sourceRoot":"","sources":["../../../src/api/internal/RequestContextMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAkB,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAGvF,OAAO,EAAE,MAAM,EAA6C,MAAM,yBAAyB,CAAA;
|
|
1
|
+
{"version":3,"file":"RequestContextMiddleware.d.ts","sourceRoot":"","sources":["../../../src/api/internal/RequestContextMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAkB,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AAGvF,OAAO,EAAE,MAAM,EAA6C,MAAM,yBAAyB,CAAA;AAK3F,eAAO,MAAM,wBAAwB,mBAAmB,MAAM,qQAkD3D,CAAA"}
|
|
@@ -5,6 +5,7 @@ import { NonEmptyString255 } from "effect-app/Schema";
|
|
|
5
5
|
import { Locale, LocaleRef, RequestContext, spanAttributes } from "../../RequestContext.js";
|
|
6
6
|
import { ContextMapContainer } from "../../Store/ContextMapContainer.js";
|
|
7
7
|
import { storeId } from "../../Store/Memory.js";
|
|
8
|
+
import { provideOnRequestScope } from "../setupRequest.js";
|
|
8
9
|
export const RequestContextMiddleware = (defaultLocale = "en") => HttpMiddleware.make((app) => Effect.gen(function* () {
|
|
9
10
|
const req = yield* HttpServerRequest.HttpServerRequest;
|
|
10
11
|
const currentSpan = yield* Effect.currentSpan.pipe(Effect.orDie);
|
|
@@ -30,10 +31,16 @@ export const RequestContextMiddleware = (defaultLocale = "en") => HttpMiddleware
|
|
|
30
31
|
});
|
|
31
32
|
yield* Effect.annotateCurrentSpan(spanAttributes(requestContext));
|
|
32
33
|
const layer = Layer.mergeAll(ContextMapContainer.layer, Layer.succeed(LocaleRef, requestContext.locale), Layer.succeed(storeId, requestContext.namespace));
|
|
33
|
-
|
|
34
|
+
// Bind layer to the request scope so ContextMap's finalizer (clear()) runs only
|
|
35
|
+
// after the response body is fully drained — not when `app` returns its
|
|
36
|
+
// HttpServerResponse value. Streaming RPC responses keep producing chunks (and
|
|
37
|
+
// using ContextMap-cached etags) after `app` returns; a sub-scope from
|
|
38
|
+
// `Effect.provide(layer)` would close too early and wipe etags mid-stream,
|
|
39
|
+
// causing spurious OptimisticConcurrencyException on later writes.
|
|
40
|
+
const res = yield* app.pipe(Effect.withLogSpan(requestContext.name), provideOnRequestScope(layer));
|
|
34
41
|
// TODO: how to set also on errors?
|
|
35
42
|
return HttpServerResponse.setHeaders(res, {
|
|
36
43
|
"Content-Language": requestContext.locale
|
|
37
44
|
});
|
|
38
45
|
}));
|
|
39
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
46
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVxdWVzdENvbnRleHRNaWRkbGV3YXJlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9pbnRlcm5hbC9SZXF1ZXN0Q29udGV4dE1pZGRsZXdhcmUudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE1BQU0sTUFBTSxtQkFBbUIsQ0FBQTtBQUMzQyxPQUFPLEVBQUUsY0FBYyxFQUFFLGlCQUFpQixFQUFFLGtCQUFrQixFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDdkYsT0FBTyxLQUFLLEtBQUssTUFBTSxrQkFBa0IsQ0FBQTtBQUN6QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSxtQkFBbUIsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxjQUFjLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDM0YsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sb0NBQW9DLENBQUE7QUFDeEUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLHVCQUF1QixDQUFBO0FBQy9DLE9BQU8sRUFBRSxxQkFBcUIsRUFBRSxNQUFNLG9CQUFvQixDQUFBO0FBRTFELE1BQU0sQ0FBQyxNQUFNLHdCQUF3QixHQUFHLENBQUMsYUFBYSxHQUFXLElBQUksRUFBRSxFQUFFLENBQ3ZFLGNBQWMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUMxQixNQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQztJQUNsQixNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBaUIsQ0FBQTtJQUV0RCxNQUFNLFdBQVcsR0FBRyxLQUFLLENBQUMsQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDaEUsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLFFBQVEsQ0FBQTtJQUNqQyxNQUFNLGFBQWEsR0FBRyxHQUFHLENBQUMsT0FBTyxDQUFDLFVBQVUsQ0FBQyxDQUFBO0lBQzdDLHFHQUFxRztJQUNyRyxNQUFNLE1BQU0sR0FBRyxhQUFhLElBQUksU0FBUyxDQUFDLFFBQVEsQ0FBQyxhQUFvQixDQUFDO1FBQ3RFLENBQUMsQ0FBRSxhQUEwQztRQUM3QyxDQUFDLENBQUMsYUFBYSxDQUFBO0lBRWpCLE1BQU0sRUFBRSxHQUFHLEdBQUcsQ0FBQyxPQUFPLENBQUMsWUFBWSxDQUFDLENBQUE7SUFDcEMsTUFBTSxTQUFTLEdBQUcsaUJBQWlCLENBQUMsQ0FBQyxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLElBQUksU0FBUyxDQUFDLENBQUE7SUFDMUYsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLE9BQU8sQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFBO0lBRTlDLE1BQU0sY0FBYyxHQUFHLGNBQWMsQ0FBQyxJQUFJLENBQUM7UUFDekMsSUFBSSxFQUFFO1lBQ0osT0FBTyxFQUFFLFdBQVcsQ0FBQyxPQUFPO1lBQzVCLE1BQU0sRUFBRSxXQUFXLENBQUMsTUFBTTtZQUMxQixPQUFPLEVBQUUsV0FBVyxDQUFDLE9BQU87U0FDN0I7UUFDRCxJQUFJLEVBQUUsaUJBQWlCLENBQUMsUUFBUSxHQUFHLENBQUMsTUFBTSxJQUFJLEdBQUcsQ0FBQyxXQUFXLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSw4QkFBOEI7UUFDOUcsTUFBTTtRQUNOLFNBQVM7UUFDVCxRQUFRLEVBQUUsUUFBUSxDQUFDLENBQUMsQ0FBQyxpQkFBaUIsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUztLQUM3RCxDQUFDLENBQUE7SUFDRixLQUFLLENBQUMsQ0FBQyxNQUFNLENBQUMsbUJBQW1CLENBQUMsY0FBYyxDQUFDLGNBQWMsQ0FBQyxDQUFDLENBQUE7SUFDakUsTUFBTSxLQUFLLEdBQUcsS0FBSyxDQUFDLFFBQVEsQ0FDMUIsbUJBQW1CLENBQUMsS0FBSyxFQUN6QixLQUFLLENBQUMsT0FBTyxDQUFDLFNBQVMsRUFBRSxjQUFjLENBQUMsTUFBTSxDQUFDLEVBQy9DLEtBQUssQ0FBQyxPQUFPLENBQUMsT0FBTyxFQUFFLGNBQWMsQ0FBQyxTQUFTLENBQUMsQ0FDakQsQ0FBQTtJQUNELGdGQUFnRjtJQUNoRix3RUFBd0U7SUFDeEUsK0VBQStFO0lBQy9FLHVFQUF1RTtJQUN2RSwyRUFBMkU7SUFDM0UsbUVBQW1FO0lBQ25FLE1BQU0sR0FBRyxHQUFHLEtBQUssQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQ3pCLE1BQU0sQ0FBQyxXQUFXLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxFQUN2QyxxQkFBcUIsQ0FBQyxLQUFLLENBQUMsQ0FDN0IsQ0FBQTtJQUVELG1DQUFtQztJQUNuQyxPQUFPLGtCQUFrQixDQUFDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7UUFDeEMsa0JBQWtCLEVBQUUsY0FBYyxDQUFDLE1BQU07S0FDMUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFDLENBQ0gsQ0FBQSJ9
|
|
@@ -7,5 +7,5 @@ export declare const makeSSE: <A extends {
|
|
|
7
7
|
}, SI, SRD, SRE>(schema: S.Codec<A, SI, SRD, SRE>) => <E, R>(events: Stream.Stream<{
|
|
8
8
|
evt: A;
|
|
9
9
|
namespace: string;
|
|
10
|
-
}, E, R>) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, Exclude<Exclude<R, import("effect/Tracer").ParentSpan>, never> | Exclude<Exclude<SRD, import("effect/Tracer").ParentSpan>, never> | Exclude<Exclude<SRE, import("effect/Tracer").ParentSpan>, never>>;
|
|
11
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
10
|
+
}, E, R>) => Effect.Effect<HttpServerResponse.HttpServerResponse, never, import("effect/Scope").Scope | Exclude<Exclude<R, import("effect/Tracer").ParentSpan>, never> | Exclude<Exclude<SRD, import("effect/Tracer").ParentSpan>, never> | Exclude<Exclude<SRE, import("effect/Tracer").ParentSpan>, never>>;
|
|
11
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRzLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvYXBpL2ludGVybmFsL2V2ZW50cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sRUFBZSxrQkFBa0IsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQ2pFLE9BQU8sS0FBSyxDQUFDLE1BQU0sbUJBQW1CLENBQUE7QUFJdEMsT0FBTyxLQUFLLE1BQU0sTUFBTSxlQUFlLENBQUE7QUFXdkMsZUFBTyxNQUFNLE9BQU8sR0FBSSxDQUFDLFNBQVM7SUFBRSxFQUFFLEVBQUUsR0FBRyxDQUFBO0NBQUUsRUFBRSxFQUFFLEVBQUUsR0FBRyxFQUFFLEdBQUcsVUFDakQsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLEdBQUcsRUFBRSxHQUFHLENBQUMsTUFFakMsQ0FBQyxFQUFFLENBQUMsVUFBVSxNQUFNLENBQUMsTUFBTSxDQUFDO0lBQUUsR0FBRyxFQUFFLENBQUMsQ0FBQztJQUFDLFNBQVMsRUFBRSxNQUFNLENBQUE7Q0FBRSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMscVNBd0RvQyxDQUFBIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/api/internal/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAe,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,CAAC,MAAM,mBAAmB,CAAA;AAItC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAWvC,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS;IAAE,EAAE,EAAE,GAAG,CAAA;CAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,UACjD,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,MAEjC,CAAC,EAAE,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC;IAAE,GAAG,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../../src/api/internal/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,EAAe,kBAAkB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,CAAC,MAAM,mBAAmB,CAAA;AAItC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAWvC,eAAO,MAAM,OAAO,GAAI,CAAC,SAAS;IAAE,EAAE,EAAE,GAAG,CAAA;CAAE,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,UACjD,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,MAEjC,CAAC,EAAE,CAAC,UAAU,MAAM,CAAC,MAAM,CAAC;IAAE,GAAG,EAAE,CAAC,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,EAAE,CAAC,EAAE,CAAC,CAAC,qSAwDoC,CAAA"}
|
|
@@ -7,7 +7,7 @@ import * as Schedule from "effect/Schedule";
|
|
|
7
7
|
import * as Stream from "effect/Stream";
|
|
8
8
|
import { reportError } from "../../errorReporter.js";
|
|
9
9
|
import { storeId } from "../../Store/Memory.js";
|
|
10
|
-
import {
|
|
10
|
+
import { setupStreamingRequestContextFromCurrent } from "../setupRequest.js";
|
|
11
11
|
// Tell the client to retry every 10 seconds if connectivity is lost
|
|
12
12
|
const setRetry = Stream.succeed("retry: 10000");
|
|
13
13
|
const keepAlive = Stream.fromEffectSchedule(Effect.succeed(":keep-alive"), Schedule.fixed(Duration.seconds(15)));
|
|
@@ -45,5 +45,5 @@ export const makeSSE = (schema) => (events) => Effect
|
|
|
45
45
|
});
|
|
46
46
|
return res;
|
|
47
47
|
})
|
|
48
|
-
.pipe(Effect.tapCause(reportError("Request")),
|
|
49
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
48
|
+
.pipe(Effect.tapCause(reportError("Request")), setupStreamingRequestContextFromCurrent("events"));
|
|
49
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnRzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2FwaS9pbnRlcm5hbC9ldmVudHMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE1BQU0sTUFBTSxtQkFBbUIsQ0FBQTtBQUMzQyxPQUFPLEVBQUUsV0FBVyxFQUFFLGtCQUFrQixFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDakUsT0FBTyxLQUFLLENBQUMsTUFBTSxtQkFBbUIsQ0FBQTtBQUN0QyxPQUFPLEtBQUssUUFBUSxNQUFNLGlCQUFpQixDQUFBO0FBQzNDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQTtBQUN0QyxPQUFPLEtBQUssUUFBUSxNQUFNLGlCQUFpQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxNQUFNLE1BQU0sZUFBZSxDQUFBO0FBQ3ZDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSx3QkFBd0IsQ0FBQTtBQUNwRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sdUJBQXVCLENBQUE7QUFDL0MsT0FBTyxFQUFFLHVDQUF1QyxFQUFFLE1BQU0sb0JBQW9CLENBQUE7QUFFNUUsb0VBQW9FO0FBQ3BFLE1BQU0sUUFBUSxHQUFHLE1BQU0sQ0FBQyxPQUFPLENBQUMsY0FBYyxDQUFDLENBQUE7QUFDL0MsTUFBTSxTQUFTLEdBQUcsTUFBTSxDQUFDLGtCQUFrQixDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLEVBQUUsUUFBUSxDQUFDLEtBQUssQ0FBQyxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtBQUVoSCxJQUFJLE1BQU0sR0FBRyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUE7QUFFdEIsTUFBTSxDQUFDLE1BQU0sT0FBTyxHQUFHLENBQ3JCLE1BQWdDLEVBQ2hDLEVBQUUsQ0FDSixDQUFPLE1BQTBELEVBQUUsRUFBRSxDQUNuRSxNQUFNO0tBQ0gsR0FBRyxDQUFD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
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Effect from "effect-app/Effect";
|
|
2
|
+
import * as Layer from "effect-app/Layer";
|
|
2
3
|
import { NonEmptyString255 } from "effect-app/Schema";
|
|
3
4
|
import * as Tracer from "effect/Tracer";
|
|
4
5
|
import { RequestContext } from "../RequestContext.js";
|
|
@@ -10,7 +11,9 @@ export declare const getRC: Effect.Effect<{
|
|
|
10
11
|
export interface SetupRequestOptions {
|
|
11
12
|
readonly withTransaction?: boolean;
|
|
12
13
|
}
|
|
14
|
+
export declare const provideOnRequestScope: <ROut, E2, RIn>(layer: Layer.Layer<ROut, E2, RIn>) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E | E2, RIn | import("effect/Scope").Scope | Exclude<R, ROut>>;
|
|
13
15
|
export declare const setupRequestContextFromCurrent: (name?: string, options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>, never>>;
|
|
16
|
+
export declare const setupStreamingRequestContextFromCurrent: (name?: string, options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, import("effect/Scope").Scope | Exclude<Exclude<R, Tracer.ParentSpan>, never>>;
|
|
14
17
|
export declare function setupRequestContext<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext, options?: SetupRequestOptions): Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>, never>>;
|
|
15
18
|
export declare function setupRequestContextWithCustomSpan<R, E, A>(self: Effect.Effect<A, E, R>, requestContext: RequestContext, name: string, options?: Tracer.SpanOptions & SetupRequestOptions): Effect.Effect<A, E, Exclude<Exclude<R, Tracer.ParentSpan>, never>>;
|
|
16
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dXBSZXF1ZXN0LmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvYXBpL3NldHVwUmVxdWVzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssTUFBTSxNQUFNLG1CQUFtQixDQUFBO0FBQzNDLE9BQU8sS0FBSyxLQUFLLE1BQU0sa0JBQWtCLENBQUE7QUFFekMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDckQsT0FBTyxLQUFLLE1BQU0sTUFBTSxlQUFlLENBQUE7QUFFdkMsT0FBTyxFQUFhLGNBQWMsRUFBa0IsTUFBTSxzQkFBc0IsQ0FBQTtBQVloRixlQUFPLE1BQU0saUJBQWlCLDZDQWUzQixDQUFBO0FBRUgsZUFBTyxNQUFNLEtBQUs7OztnQkFHaEIsQ0FBQTtBQW1CRixNQUFNLFdBQVcsbUJBQW1CO0lBQ2xDLFFBQVEsQ0FBQyxlQUFlLENBQUMsRUFBRSxPQUFPLENBQUE7Q0FDbkM7QUFNRCxlQUFPLE1BQU0scUJBQXFCLEdBQy9CLElBQUksRUFBRSxFQUFFLEVBQUUsR0FBRyxTQUFTLEtBQUssQ0FBQyxLQUFLLENBQUMsSUFBSSxFQUFFLEVBQUUsRUFBRSxHQUFHLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsUUFBUSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLG9GQUt4RixDQUFBO0FBRU4sZUFBTyxNQUFNLDhCQUE4Qiw0QkFDWixNQUFNLENBQUMsV0FBVyxHQUFHLG1CQUFtQixNQUFNLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxRQUFRLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsdUVBTTNHLENBQUE7QUFNUCxlQUFPLE1BQU0sdUNBQXVDLDRCQUNyQixNQUFNLENBQUMsV0FBVyxHQUFHLG1CQUFtQixNQUFNLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxRQUFRLE1BQU0sQ0FBQyxNQUFNLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsc0dBSzdHLENBQUE7QUFHTCx3QkFBZ0IsbUJBQW1CLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQ3pDLElBQUksRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQzVCLGNBQWMsRUFBRSxjQUFjLEVBQzlCLE9BQU8sQ0FBQyxFQUFFLG1CQUFtQixzRUFhOUI7QUFFRCx3QkFBZ0IsaUNBQWlDLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLEVBQ3ZELElBQUksRUFBRSxNQUFNLENBQUMsTUFBTSxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQzVCLGNBQWMsRUFBRSxjQUFjLEVBQzlCLElBQUksRUFBRSxNQUFNLEVBQ1osT0FBTyxDQUFDLEVBQUUsTUFBTSxDQUFDLFdBQVcsR0FBRyxtQkFBbUIsc0VBYW5EIn0=
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setupRequest.d.ts","sourceRoot":"","sources":["../../src/api/setupRequest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"setupRequest.d.ts","sourceRoot":"","sources":["../../src/api/setupRequest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,mBAAmB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,kBAAkB,CAAA;AAEzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAA;AACrD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,EAAa,cAAc,EAAkB,MAAM,sBAAsB,CAAA;AAYhF,eAAO,MAAM,iBAAiB,6CAe3B,CAAA;AAEH,eAAO,MAAM,KAAK;;;gBAGhB,CAAA;AAmBF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CACnC;AAMD,eAAO,MAAM,qBAAqB,GAC/B,IAAI,EAAE,EAAE,EAAE,GAAG,SAAS,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,oFAKxF,CAAA;AAEN,eAAO,MAAM,8BAA8B,4BACZ,MAAM,CAAC,WAAW,GAAG,mBAAmB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,uEAM3G,CAAA;AAMP,eAAO,MAAM,uCAAuC,4BACrB,MAAM,CAAC,WAAW,GAAG,mBAAmB,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,sGAK7G,CAAA;AAGL,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACzC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC5B,cAAc,EAAE,cAAc,EAC9B,OAAO,CAAC,EAAE,mBAAmB,sEAa9B;AAED,wBAAgB,iCAAiC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EACvD,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC5B,cAAc,EAAE,cAAc,EAC9B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,CAAC,WAAW,GAAG,mBAAmB,sEAanD"}
|
package/dist/api/setupRequest.js
CHANGED
|
@@ -36,8 +36,22 @@ const withRequestSpan = (name = "request", options) => (f) => Effect.andThen(get
|
|
|
36
36
|
// TODO: false
|
|
37
37
|
// request context info is picked up directly in the logger for annotations.
|
|
38
38
|
Effect.withLogSpan(name)));
|
|
39
|
+
// Build `layer` against the ambient (request) scope rather than a sub-scope of the
|
|
40
|
+
// returned Effect. Required when the returned value is a streaming HttpServerResponse:
|
|
41
|
+
// the response body keeps producing chunks (and using layer-provided state) after the
|
|
42
|
+
// Effect returns, so a sub-scope would close too early and run finalizers mid-stream.
|
|
43
|
+
export const provideOnRequestScope = (layer) => (self) => Effect.gen(function* () {
|
|
44
|
+
const requestScope = yield* Effect.scope;
|
|
45
|
+
const ctx = yield* Layer.buildWithScope(layer, requestScope);
|
|
46
|
+
return yield* Effect.provide(self, ctx);
|
|
47
|
+
});
|
|
39
48
|
export const setupRequestContextFromCurrent = (name = "request", options) => (self) => self
|
|
40
49
|
.pipe(options?.withTransaction === true ? withSqlTransaction : (_) => _, withRequestSpan(name, options), Effect.provide(ContextMapContainer.layer, { local: true }));
|
|
50
|
+
// Streaming variant: binds ContextMapContainer to the ambient (request) scope so its
|
|
51
|
+
// finalizer (clear()) runs only after the response body is fully drained, not when the
|
|
52
|
+
// outer Effect returns its HttpServerResponse value. Use for handlers that return a
|
|
53
|
+
// streaming HttpServerResponse (e.g. SSE) — see RequestContextMiddleware for context.
|
|
54
|
+
export const setupStreamingRequestContextFromCurrent = (name = "request", options) => (self) => self.pipe(options?.withTransaction === true ? withSqlTransaction : (_) => _, withRequestSpan(name, options), provideOnRequestScope(ContextMapContainer.layer));
|
|
41
55
|
// TODO: consider integrating Effect.withParentSpan
|
|
42
56
|
export function setupRequestContext(self, requestContext, options) {
|
|
43
57
|
const layer = Layer.mergeAll(ContextMapContainer.layer, Layer.succeed(LocaleRef, requestContext.locale), Layer.succeed(storeId, requestContext.namespace));
|
|
@@ -49,4 +63,4 @@ export function setupRequestContextWithCustomSpan(self, requestContext, name, op
|
|
|
49
63
|
return self
|
|
50
64
|
.pipe(options?.withTransaction === true ? withSqlTransaction : (_) => _, withRequestSpan(name, options), Effect.provide(layer, { local: true }));
|
|
51
65
|
}
|
|
52
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
66
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2V0dXBSZXF1ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwaS9zZXR1cFJlcXVlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE1BQU0sTUFBTSxtQkFBbUIsQ0FBQTtBQUMzQyxPQUFPLEtBQUssS0FBSyxNQUFNLGtCQUFrQixDQUFBO0FBQ3pDLE9BQU8sS0FBSyxNQUFNLE1BQU0sbUJBQW1CLENBQUE7QUFDM0MsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sbUJBQW1CLENBQUE7QUFDckQsT0FBTyxLQUFLLE1BQU0sTUFBTSxlQUFlLENBQUE7QUFDdkMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLHFCQUFxQixDQUFBO0FBQy9DLE9BQU8sRUFBRSxTQUFTLEVBQUUsY0FBYyxFQUFFLGNBQWMsRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQ2hGLE9BQU8sRUFBRSxtQkFBbUIsRUFBRSxNQUFNLGlDQUFpQyxDQUFBO0FBQ3JFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQTtBQUU1QyxNQUFNLGtCQUFrQixHQUFHLENBQVUsSUFBNEIsRUFBMEIsRUFBRSxDQUMzRixNQUFNLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxJQUFJLENBQzVDLE1BQU0sQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztJQUMxQixNQUFNLEVBQUUsR0FBRyxFQUFFLENBQUMsSUFBSTtJQUNsQixNQUFNLEVBQUUsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLEdBQUcsQ0FBQyxlQUFlLENBQUMsSUFBSSxDQUFDLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7Q0FDOUQsQ0FBQyxDQUFDLENBQ0osQ0FBQTtBQUVILE1BQU0sQ0FBQyxNQUFNLGlCQUFpQixHQUFHLE1BQU07S0FDcEMsR0FBRyxDQUFDO0lBQ0gsSUFBSSxFQUFFLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7SUFDM0MsTUFBTSxFQUFFLFNBQVM7SUFDakIsU0FBUyxFQUFFLE9BQU87Q0FDbkIsQ0FBQztLQUNELElBQUksQ0FDSCxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsQ0FDekMsY0FBYyxDQUFDLElBQUksQ0FBQztJQUNsQixJQUFJLEVBQUUsTUFBTSxDQUFDLFlBQVksQ0FBQyxJQUFJLENBQUM7SUFDL0IsTUFBTTtJQUNOLFNBQVM7SUFDVCxJQUFJLEVBQUUsaUJBQWlCLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQztDQUNuQyxDQUFDLENBQ0gsQ0FDRixDQUFBO0FBRUgsTUFBTSxDQUFDLE1BQU0sS0FBSyxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUM7SUFDOUIsTUFBTSxFQUFFLFNBQVM7SUFDakIsU0FBUyxFQUFFLE9BQU87Q0FDbkIsQ0FBQyxDQUFBO0FBRUYsTUFBTSxlQUFlLEdBQUcsQ0FBQyxJQUFJLEdBQUcsU0FBUyxFQUFFLE9BQTRCLEVBQUUsRUFBRSxDQUFDLENBQVUsQ0FBeUIsRUFBRSxFQUFFLENBQ2pILE1BQU0sQ0FBQyxPQUFPLENBQ1osS0FBSyxFQUNMLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FDTixDQUFDLENBQUMsSUFBSSxDQUNKLE1BQU0sQ0FBQyxRQUFRLENBQUMsSUFBSSxFQUFFO0lBQ3BCLEdBQUcsT0FBTztJQUNWLFVBQVUsRUFBRSxFQUFFLEdBQUcsY0FBYyxDQUFDLEVBQUUsR0FBRyxHQUFHLEVBQUUsSUFBSSxFQUFFLGlCQUFpQixDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsRUFBRSxHQUFHLE9BQU8sRUFBRSxVQUFVLEVBQUU7Q0FDckcsRUFBRTtJQUNELGlCQUFpQixFQUFFLE9BQU8sRUFBRSxpQkFBaUIsSUFBSSxLQUFLO0NBQ3ZELENBQUM7QUFDRixjQUFjO0FBQ2QsNEVBQTRFO0FBQzVFLE1BQU0sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLENBQ3pCLENBQ0osQ0FBQTtBQU1ILG1GQUFtRjtBQUNuRix1RkFBdUY7QUFDdkYsc0ZBQXNGO0FBQ3RGLHNGQUFzRjtBQUN0RixNQUFNLENBQUMsTUFBTSxxQkFBcUIsR0FDaEMsQ0FBZ0IsS0FBaUMsRUFBRSxFQUFFLENBQUMsQ0FBVSxJQUE0QixFQUFFLEVBQUUsQ0FDOUYsTUFBTSxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUM7SUFDbEIsTUFBTSxZQUFZLEdBQUcsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQTtJQUN4QyxNQUFNLEdBQUcsR0FBRyxLQUFLLENBQUMsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLEtBQUssRUFBRSxZQUFZLENBQUMsQ0FBQTtJQUM1RCxPQUFPLEtBQUssQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsSUFBSSxFQUFFLEdBQUcsQ0FBQyxDQUFBO0FBQ3pDLENBQUMsQ0FBQyxDQUFBO0FBRU4sTUFBTSxDQUFDLE1BQU0sOEJBQThCLEdBQ3pDLENBQUMsSUFBSSxHQUFHLFNBQVMsRUFBRSxPQUFrRCxFQUFFLEVBQUUsQ0FBQyxDQUFVLElBQTRCLEVBQUUsRUFBRSxDQUNsSCxJQUFJO0tBQ0QsSUFBSSxDQUNILE9BQU8sRUFBRSxlQUFlLEtBQUssSUFBSSxDQUFDLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsRUFDakUsZUFBZSxDQUFDLElBQUksRUFBRSxPQUFPLENBQUMsRUFDOUIsTUFBTSxDQUFDLE9BQU8sQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLEVBQUUsRUFBRSxLQUFLLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FDM0QsQ0FBQTtBQUVQLHFGQUFxRjtBQUNyRix1RkFBdUY7QUFDdkYsb0ZBQW9GO0FBQ3BGLHNGQUFzRjtBQUN0RixNQUFNLENBQUMsTUFBTSx1Q0FBdUMsR0FDbEQsQ0FBQyxJQUFJLEdBQUcsU0FBUyxFQUFFLE9BQWtELEVBQUUsRUFBRSxDQUFDLENBQVUsSUFBNEIsRUFBRSxFQUFFLENBQ2xILElBQUksQ0FBQyxJQUFJLENBQ1AsT0FBTyxFQUFFLGVBQWUsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUNqRSxlQUFlLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxFQUM5QixxQkFBcUIsQ0FBQyxtQkFBbUIsQ0FBQyxLQUFLLENBQUMsQ0FDakQsQ0FBQTtBQUVMLG1EQUFtRDtBQUNuRCxNQUFNLFVBQVUsbUJBQW1CLENBQ2pDLElBQTRCLEVBQzVCLGNBQThCLEVBQzlCLE9BQTZCO0lBRTdCLE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxRQUFRLENBQzFCLG1CQUFtQixDQUFDLEtBQUssRUFDekIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsY0FBYyxDQUFDLE1BQU0sQ0FBQyxFQUMvQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxjQUFjLENBQUMsU0FBUyxDQUFDLENBQ2pELENBQUE7SUFDRCxPQUFPLElBQUk7U0FDUixJQUFJLENBQ0gsT0FBTyxFQUFFLGVBQWUsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUNqRSxlQUFlLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxFQUNwQyxNQUFNLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUN2QyxDQUFBO0FBQ0wsQ0FBQztBQUVELE1BQU0sVUFBVSxpQ0FBaUMsQ0FDL0MsSUFBNEIsRUFDNUIsY0FBOEIsRUFDOUIsSUFBWSxFQUNaLE9BQWtEO0lBRWxELE1BQU0sS0FBSyxHQUFHLEtBQUssQ0FBQyxRQUFRLENBQzFCLG1CQUFtQixDQUFDLEtBQUssRUFDekIsS0FBSyxDQUFDLE9BQU8sQ0FBQyxTQUFTLEVBQUUsY0FBYyxDQUFDLE1BQU0sQ0FBQyxFQUMvQyxLQUFLLENBQUMsT0FBTyxDQUFDLE9BQU8sRUFBRSxjQUFjLENBQUMsU0FBUyxDQUFDLENBQ2pELENBQUE7SUFDRCxPQUFPLElBQUk7U0FDUixJQUFJLENBQ0gsT0FBTyxFQUFFLGVBQWUsS0FBSyxJQUFJLENBQUMsQ0FBQyxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUNqRSxlQUFlLENBQUMsSUFBSSxFQUFFLE9BQU8sQ0FBQyxFQUM5QixNQUFNLENBQUMsT0FBTyxDQUFDLEtBQUssRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUN2QyxDQUFBO0FBQ0wsQ0FBQyJ9
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect-app/infra",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.239",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"proper-lockfile": "^4.1.2",
|
|
14
14
|
"pure-rand": "8.4.0",
|
|
15
15
|
"query-string": "^9.3.1",
|
|
16
|
-
"effect-app": "4.0.0-beta.
|
|
16
|
+
"effect-app": "4.0.0-beta.239"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
19
|
"@azure/cosmos": "^4.9.3",
|
|
@@ -5,6 +5,7 @@ import { NonEmptyString255 } from "effect-app/Schema"
|
|
|
5
5
|
import { Locale, LocaleRef, RequestContext, spanAttributes } from "../../RequestContext.js"
|
|
6
6
|
import { ContextMapContainer } from "../../Store/ContextMapContainer.js"
|
|
7
7
|
import { storeId } from "../../Store/Memory.js"
|
|
8
|
+
import { provideOnRequestScope } from "../setupRequest.js"
|
|
8
9
|
|
|
9
10
|
export const RequestContextMiddleware = (defaultLocale: Locale = "en") =>
|
|
10
11
|
HttpMiddleware.make((app) =>
|
|
@@ -40,9 +41,15 @@ export const RequestContextMiddleware = (defaultLocale: Locale = "en") =>
|
|
|
40
41
|
Layer.succeed(LocaleRef, requestContext.locale),
|
|
41
42
|
Layer.succeed(storeId, requestContext.namespace)
|
|
42
43
|
)
|
|
44
|
+
// Bind layer to the request scope so ContextMap's finalizer (clear()) runs only
|
|
45
|
+
// after the response body is fully drained — not when `app` returns its
|
|
46
|
+
// HttpServerResponse value. Streaming RPC responses keep producing chunks (and
|
|
47
|
+
// using ContextMap-cached etags) after `app` returns; a sub-scope from
|
|
48
|
+
// `Effect.provide(layer)` would close too early and wipe etags mid-stream,
|
|
49
|
+
// causing spurious OptimisticConcurrencyException on later writes.
|
|
43
50
|
const res = yield* app.pipe(
|
|
44
51
|
Effect.withLogSpan(requestContext.name),
|
|
45
|
-
|
|
52
|
+
provideOnRequestScope(layer)
|
|
46
53
|
)
|
|
47
54
|
|
|
48
55
|
// TODO: how to set also on errors?
|
|
@@ -7,7 +7,7 @@ import * as Schedule from "effect/Schedule"
|
|
|
7
7
|
import * as Stream from "effect/Stream"
|
|
8
8
|
import { reportError } from "../../errorReporter.js"
|
|
9
9
|
import { storeId } from "../../Store/Memory.js"
|
|
10
|
-
import {
|
|
10
|
+
import { setupStreamingRequestContextFromCurrent } from "../setupRequest.js"
|
|
11
11
|
|
|
12
12
|
// Tell the client to retry every 10 seconds if connectivity is lost
|
|
13
13
|
const setRetry = Stream.succeed("retry: 10000")
|
|
@@ -74,4 +74,4 @@ export const makeSSE = <A extends { id: any }, SI, SRD, SRE>(
|
|
|
74
74
|
)
|
|
75
75
|
return res
|
|
76
76
|
})
|
|
77
|
-
.pipe(Effect.tapCause(reportError("Request")),
|
|
77
|
+
.pipe(Effect.tapCause(reportError("Request")), setupStreamingRequestContextFromCurrent("events"))
|
package/src/api/setupRequest.ts
CHANGED
|
@@ -59,6 +59,18 @@ export interface SetupRequestOptions {
|
|
|
59
59
|
readonly withTransaction?: boolean
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
// Build `layer` against the ambient (request) scope rather than a sub-scope of the
|
|
63
|
+
// returned Effect. Required when the returned value is a streaming HttpServerResponse:
|
|
64
|
+
// the response body keeps producing chunks (and using layer-provided state) after the
|
|
65
|
+
// Effect returns, so a sub-scope would close too early and run finalizers mid-stream.
|
|
66
|
+
export const provideOnRequestScope =
|
|
67
|
+
<ROut, E2, RIn>(layer: Layer.Layer<ROut, E2, RIn>) => <A, E, R>(self: Effect.Effect<A, E, R>) =>
|
|
68
|
+
Effect.gen(function*() {
|
|
69
|
+
const requestScope = yield* Effect.scope
|
|
70
|
+
const ctx = yield* Layer.buildWithScope(layer, requestScope)
|
|
71
|
+
return yield* Effect.provide(self, ctx)
|
|
72
|
+
})
|
|
73
|
+
|
|
62
74
|
export const setupRequestContextFromCurrent =
|
|
63
75
|
(name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
64
76
|
self
|
|
@@ -68,6 +80,18 @@ export const setupRequestContextFromCurrent =
|
|
|
68
80
|
Effect.provide(ContextMapContainer.layer, { local: true })
|
|
69
81
|
)
|
|
70
82
|
|
|
83
|
+
// Streaming variant: binds ContextMapContainer to the ambient (request) scope so its
|
|
84
|
+
// finalizer (clear()) runs only after the response body is fully drained, not when the
|
|
85
|
+
// outer Effect returns its HttpServerResponse value. Use for handlers that return a
|
|
86
|
+
// streaming HttpServerResponse (e.g. SSE) — see RequestContextMiddleware for context.
|
|
87
|
+
export const setupStreamingRequestContextFromCurrent =
|
|
88
|
+
(name = "request", options?: Tracer.SpanOptions & SetupRequestOptions) => <R, E, A>(self: Effect.Effect<A, E, R>) =>
|
|
89
|
+
self.pipe(
|
|
90
|
+
options?.withTransaction === true ? withSqlTransaction : (_) => _,
|
|
91
|
+
withRequestSpan(name, options),
|
|
92
|
+
provideOnRequestScope(ContextMapContainer.layer)
|
|
93
|
+
)
|
|
94
|
+
|
|
71
95
|
// TODO: consider integrating Effect.withParentSpan
|
|
72
96
|
export function setupRequestContext<R, E, A>(
|
|
73
97
|
self: Effect.Effect<A, E, R>,
|