effect-cf 0.42.0 → 0.43.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.
- package/CHANGELOG.md +48 -0
- package/README.md +1 -1
- package/dist/AnalyticsEngine.mjs +3 -3
- package/dist/CloudflareTracer.mjs +1 -1
- package/dist/DurableObject.d.mts +4 -2
- package/dist/DurableObject.mjs +5 -1
- package/dist/DurableObjectSqlite.mjs +1 -1
- package/dist/Environment.mjs +5 -5
- package/dist/Worker.mjs +3 -2
- package/dist/internal/NativeCallback.mjs +5 -2
- package/package.json +5 -5
- package/src/AnalyticsEngine.ts +3 -3
- package/src/CloudflareTracer.ts +1 -1
- package/src/DurableObject.ts +16 -3
- package/src/Environment.ts +5 -5
- package/src/Worker.ts +11 -4
- package/src/internal/NativeCallback.ts +13 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# effect-cf
|
|
2
2
|
|
|
3
|
+
## 0.43.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#174](https://github.com/danieljvdm/effect-cf/pull/174) [`10cd4a9`](https://github.com/danieljvdm/effect-cf/commit/10cd4a9cd162ef38599a53c772cdb19f8e241605) Thanks [@danieljvdm](https://github.com/danieljvdm)! - ## effect-cf
|
|
8
|
+
|
|
9
|
+
Require Effect `^4.0.0-rc.115` and matching `@effect/sql-d1`, `@effect/sql-pg`, and
|
|
10
|
+
`@effect/sql-sqlite-do` peers. Upgrade the Effect family together.
|
|
11
|
+
|
|
12
|
+
Preserve Cloudflare span context with Effect's new fiber representation and close
|
|
13
|
+
request scopes when HEAD or status 204, 205, or 304 omits an Effect streaming body.
|
|
14
|
+
Omitted streams are not started, and request resources and telemetry are finalized.
|
|
15
|
+
|
|
16
|
+
HyperdrivePg now uses Effect's native PostgreSQL driver. Consumers must account for
|
|
17
|
+
its new result codecs (`int8` becomes `bigint`, dates become strings, and timestamps
|
|
18
|
+
become epoch milliseconds), wrap JSON parameters with `sql.json`, and submit one
|
|
19
|
+
statement per query. The driver enables named prepared statements by default;
|
|
20
|
+
pass `prepare: false` for poolers that cannot preserve them.
|
|
21
|
+
|
|
22
|
+
## effect-webtransport
|
|
23
|
+
|
|
24
|
+
Require Effect `^4.0.0-rc.115` and migrate the Socket adapter to scoped readers and
|
|
25
|
+
writers. Replace `socket.run(handler)` with a scoped acquisition of `socket.reader`
|
|
26
|
+
and a loop over `reader.pull`. Use `writer.write` and `writer.writeAll` after
|
|
27
|
+
acquiring `socket.writer`.
|
|
28
|
+
|
|
29
|
+
Reads now follow consumer demand without dispatching a fiber per chunk. Reader
|
|
30
|
+
scope cleanup still releases stream locks, sends FIN on success, and aborts writes
|
|
31
|
+
on failure. Releasing a writer scope half-closes the write side. Interrupting a
|
|
32
|
+
batch stops unsent frames while leaving the socket available for later writes.
|
|
33
|
+
|
|
34
|
+
Every close, including peer FIN, fails with a `SocketError` containing
|
|
35
|
+
`SocketCloseError`. Remove `closeCodeIsError`, `FromBidirectionalStreamOptions`,
|
|
36
|
+
and `defaultCloseCodeIsError`; handle close reasons in the read loop and use
|
|
37
|
+
`Effect.retry` around the scoped loop to reconnect. WebSocket fallback options now
|
|
38
|
+
accept `highWaterMark`.
|
|
39
|
+
|
|
40
|
+
MessagePack serialization was removed upstream. Use `RpcSerialization.layerNdjson`
|
|
41
|
+
or `RpcSerialization.layerSchemaBinary()` for the byte-stream transport.
|
|
42
|
+
|
|
43
|
+
## 0.42.1
|
|
44
|
+
|
|
45
|
+
### Patch Changes
|
|
46
|
+
|
|
47
|
+
- [#171](https://github.com/danieljvdm/effect-cf/pull/171) [`06a0bfc`](https://github.com/danieljvdm/effect-cf/commit/06a0bfc1f750f5042e13f97b212c63ced5caa5e5) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Prevent the first Durable Object event from overtaking an initialization hook's concurrency gate while its service layer is still building. Hooks that perform background setup without a concurrency gate continue to allow incoming events.
|
|
48
|
+
|
|
49
|
+
- [#170](https://github.com/danieljvdm/effect-cf/pull/170) [`4c41e8d`](https://github.com/danieljvdm/effect-cf/commit/4c41e8d648f340dbf43c5e2c17a850d967b7fa09) Thanks [@danieljvdm](https://github.com/danieljvdm)! - Prevent Durable Object storage transactions and concurrency-blocking callbacks from stalling when their Effect work yields while an earlier timer is blocked by the native input gate.
|
|
50
|
+
|
|
3
51
|
## 0.42.0
|
|
4
52
|
|
|
5
53
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
Cloudflare entrypoints and bindings as Effect services.
|
|
4
4
|
|
|
5
5
|
```sh
|
|
6
|
-
npm install effect-cf "effect@^4.0.0-rc.
|
|
6
|
+
npm install effect-cf "effect@^4.0.0-rc.115"
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
The repository tests against workerd `1.20260825.1` and `@cloudflare/workers-types@5.20260825.1`. Use `compatibility_date: "2026-08-25"` in Wrangler.
|
package/dist/AnalyticsEngine.mjs
CHANGED
|
@@ -235,9 +235,9 @@ const makeClient = (definition, defaults) => (dataset) => {
|
|
|
235
235
|
};
|
|
236
236
|
};
|
|
237
237
|
const queryConfig = (options) => Config.all({
|
|
238
|
-
accountId: options?.accountId ?? Config.
|
|
239
|
-
apiToken: options?.apiToken ?? Config.
|
|
240
|
-
apiBaseUrl: options?.apiBaseUrl ?? Config.
|
|
238
|
+
accountId: options?.accountId ?? Config.String("CLOUDFLARE_ACCOUNT_ID"),
|
|
239
|
+
apiToken: options?.apiToken ?? Config.Redacted("CLOUDFLARE_API_TOKEN"),
|
|
240
|
+
apiBaseUrl: options?.apiBaseUrl ?? Config.String("CLOUDFLARE_API_BASE_URL").pipe(Config.withDefault(defaultQueryApiBaseUrl))
|
|
241
241
|
});
|
|
242
242
|
const queryLayer = (tag, definition) => Layer.effect(tag, makeQueryClient(definition));
|
|
243
243
|
const layerFetch = (tag, definition) => queryLayer(tag, definition).pipe(Layer.provide(FetchHttpClient.layer));
|
|
@@ -61,7 +61,7 @@ const layer = Layer.effect(Tracer.Tracer, Effect.sync(() => {
|
|
|
61
61
|
return parentContext(() => tracing.startActiveSpan(options.name, (span) => new Span(options, AsyncLocalStorage.snapshot(), span)));
|
|
62
62
|
},
|
|
63
63
|
context(primitive, fiber) {
|
|
64
|
-
return contextFor(fiber.
|
|
64
|
+
return contextFor(fiber.cache.span)(() => primitive["~effect/Effect/evaluate"](fiber));
|
|
65
65
|
}
|
|
66
66
|
});
|
|
67
67
|
}));
|
package/dist/DurableObject.d.mts
CHANGED
|
@@ -47,8 +47,10 @@ interface DurableObjectOptionsBase<RRuntime, REvent = never, EventLayerError = n
|
|
|
47
47
|
/**
|
|
48
48
|
* Effect run when Cloudflare loads this Durable Object instance into memory.
|
|
49
49
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* The base layer is acquired before incoming events are admitted. The hook
|
|
51
|
+
* itself runs in the background; start it with
|
|
52
|
+
* `DurableObjectState.blockConcurrencyWhile` when incoming events should
|
|
53
|
+
* wait for setup to finish. Cloudflare may construct
|
|
52
54
|
* the same Durable Object id again after eviction or restart; use Durable
|
|
53
55
|
* Object storage if work must happen only once per id.
|
|
54
56
|
*/
|
package/dist/DurableObject.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { DurableObjectAlarm } from "./DurableObjectAlarm.mjs";
|
|
|
10
10
|
import { Tag, implement, method, native } from "./DurableObjectDefinition.mjs";
|
|
11
11
|
import * as Effect from "effect/Effect";
|
|
12
12
|
import * as Layer from "effect/Layer";
|
|
13
|
+
import * as Scheduler from "effect/Scheduler";
|
|
13
14
|
import { DurableObject } from "cloudflare:workers";
|
|
14
15
|
import * as Tracer from "effect/Tracer";
|
|
15
16
|
//#region src/DurableObject.ts
|
|
@@ -36,7 +37,10 @@ function make(layer, options = {}) {
|
|
|
36
37
|
const services = (registration?.layer ?? Layer.empty).pipe(Layer.provideMerge(baseServices));
|
|
37
38
|
this.runtime = makeEntrypointRuntime(layer, env, services);
|
|
38
39
|
const initialize = options.initialize;
|
|
39
|
-
if (initialize !== void 0) state.waitUntil(
|
|
40
|
+
if (initialize !== void 0) state.waitUntil(state.blockConcurrencyWhile(async () => {
|
|
41
|
+
await Effect.runPromise(this.runtime.contextEffect, { scheduler: new Scheduler.MixedScheduler("sync") });
|
|
42
|
+
state.waitUntil(this[RunSymbol](initialize, { eventLayer: false }));
|
|
43
|
+
}));
|
|
40
44
|
}
|
|
41
45
|
[RunSymbol](effect, runOptions = {}) {
|
|
42
46
|
const eventLayer = runOptions.eventLayer === false ? void 0 : options.eventLayer;
|
|
@@ -3,8 +3,8 @@ import { DurableObjectState } from "./DurableObjectState.mjs";
|
|
|
3
3
|
import * as Context from "effect/Context";
|
|
4
4
|
import * as Effect from "effect/Effect";
|
|
5
5
|
import * as Layer from "effect/Layer";
|
|
6
|
-
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
7
6
|
import * as Scheduler from "effect/Scheduler";
|
|
7
|
+
import { SqliteClient } from "@effect/sql-sqlite-do";
|
|
8
8
|
import * as Reactivity from "effect/unstable/reactivity/Reactivity";
|
|
9
9
|
import { SqlClient } from "effect/unstable/sql";
|
|
10
10
|
//#region src/DurableObjectSqlite.ts
|
package/dist/Environment.mjs
CHANGED
|
@@ -15,11 +15,11 @@ var Environment_exports = /* @__PURE__ */ __exportAll({
|
|
|
15
15
|
var WorkerEnvironment = class extends Context.Service()("effect-cf/WorkerEnvironment") {};
|
|
16
16
|
let WorkerConfig;
|
|
17
17
|
(function(_WorkerConfig) {
|
|
18
|
-
_WorkerConfig.string = (name) => Config.
|
|
19
|
-
_WorkerConfig.redacted = (name) => Config.
|
|
20
|
-
_WorkerConfig.number = (name) => Config.
|
|
21
|
-
_WorkerConfig.integer = (name) => Config.
|
|
22
|
-
_WorkerConfig.boolean = (name) => Config.
|
|
18
|
+
_WorkerConfig.string = (name) => Config.String(name);
|
|
19
|
+
_WorkerConfig.redacted = (name) => Config.Redacted(name);
|
|
20
|
+
_WorkerConfig.number = (name) => Config.Number(name);
|
|
21
|
+
_WorkerConfig.integer = (name) => Config.Int(name);
|
|
22
|
+
_WorkerConfig.boolean = (name) => Config.Boolean(name);
|
|
23
23
|
const providerFromEnv = _WorkerConfig.providerFromEnv = (env, options) => {
|
|
24
24
|
const record = {};
|
|
25
25
|
for (const [key, value] of Object.entries(env)) if (Predicate.isString(value) || Predicate.isNumber(value) || Predicate.isBoolean(value)) record[key] = String(value);
|
package/dist/Worker.mjs
CHANGED
|
@@ -58,8 +58,9 @@ const renderFetchSuccess = (effect, eventLayer) => Effect.suspend(() => {
|
|
|
58
58
|
return Effect.succeed(response);
|
|
59
59
|
});
|
|
60
60
|
const handleResponse = (request, response) => nativeResponse !== void 0 ? Effect.void : Effect.map(Effect.context(), (context) => {
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
const withoutBody = request.method === "HEAD";
|
|
62
|
+
webResponse = HttpServerResponse.toWeb(HttpServerResponse.omitsBody(response, withoutBody) ? response : HttpEffect.scopeTransferToStream(response), {
|
|
63
|
+
withoutBody,
|
|
63
64
|
context
|
|
64
65
|
});
|
|
65
66
|
});
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import "effect/Exit";
|
|
3
4
|
import * as FiberSet from "effect/FiberSet";
|
|
5
|
+
import * as Scheduler from "effect/Scheduler";
|
|
4
6
|
//#region src/internal/NativeCallback.ts
|
|
7
|
+
const callbackScheduler = new Scheduler.MixedScheduler("sync");
|
|
5
8
|
/**
|
|
6
9
|
* Runs an Effect callback owned by a native Promise operation.
|
|
7
10
|
*
|
|
@@ -10,7 +13,7 @@ import * as FiberSet from "effect/FiberSet";
|
|
|
10
13
|
* the native operation's settlement.
|
|
11
14
|
*/
|
|
12
15
|
const runNativeCallback = Effect.fnUntraced(function* (operation) {
|
|
13
|
-
const
|
|
16
|
+
const callbackContext = Context.add(yield* Effect.context(), Scheduler.Scheduler, callbackScheduler);
|
|
14
17
|
return yield* Effect.scoped(Effect.gen(function* () {
|
|
15
18
|
let nativePromise;
|
|
16
19
|
yield* Effect.addFinalizer(() => {
|
|
@@ -20,7 +23,7 @@ const runNativeCallback = Effect.fnUntraced(function* (operation) {
|
|
|
20
23
|
const runPromise = yield* FiberSet.makeRuntimePromise();
|
|
21
24
|
return yield* Effect.callback((resume) => {
|
|
22
25
|
try {
|
|
23
|
-
nativePromise = operation((effect) => runPromise(Effect.provideContext(Effect.exit(effect),
|
|
26
|
+
nativePromise = operation((effect) => runPromise(Effect.provideContext(Effect.exit(effect), callbackContext)));
|
|
24
27
|
} catch (cause) {
|
|
25
28
|
resume(Effect.fail(cause));
|
|
26
29
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "effect-cf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.43.0",
|
|
4
4
|
"description": "Effect-native primitives for Cloudflare Workers and bindings.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -94,11 +94,11 @@
|
|
|
94
94
|
"@cloudflare/sandbox": "^0.13.0-next.738.2",
|
|
95
95
|
"@cloudflare/vitest-plugin": "^1.0.0",
|
|
96
96
|
"@cloudflare/workers-types": "^5.20260825.1",
|
|
97
|
-
"@effect/sql-d1": "^4.0.0-rc.
|
|
98
|
-
"@effect/sql-pg": "^4.0.0-rc.
|
|
99
|
-
"@effect/sql-sqlite-do": "^4.0.0-rc.
|
|
97
|
+
"@effect/sql-d1": "^4.0.0-rc.115",
|
|
98
|
+
"@effect/sql-pg": "^4.0.0-rc.115",
|
|
99
|
+
"@effect/sql-sqlite-do": "^4.0.0-rc.115",
|
|
100
100
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
101
|
-
"effect": "^4.0.0-rc.
|
|
101
|
+
"effect": "^4.0.0-rc.115"
|
|
102
102
|
},
|
|
103
103
|
"peerDependenciesMeta": {
|
|
104
104
|
"@cloudflare/computer": {
|
package/src/AnalyticsEngine.ts
CHANGED
|
@@ -729,11 +729,11 @@ export const makeClient =
|
|
|
729
729
|
|
|
730
730
|
export const queryConfig = (options?: QueryConfigOptions) =>
|
|
731
731
|
Config.all({
|
|
732
|
-
accountId: options?.accountId ?? Config.
|
|
733
|
-
apiToken: options?.apiToken ?? Config.
|
|
732
|
+
accountId: options?.accountId ?? Config.String("CLOUDFLARE_ACCOUNT_ID"),
|
|
733
|
+
apiToken: options?.apiToken ?? Config.Redacted("CLOUDFLARE_API_TOKEN"),
|
|
734
734
|
apiBaseUrl:
|
|
735
735
|
options?.apiBaseUrl ??
|
|
736
|
-
Config.
|
|
736
|
+
Config.String("CLOUDFLARE_API_BASE_URL").pipe(Config.withDefault(defaultQueryApiBaseUrl)),
|
|
737
737
|
});
|
|
738
738
|
|
|
739
739
|
export const queryLayer = <Self>(
|
package/src/CloudflareTracer.ts
CHANGED
|
@@ -91,7 +91,7 @@ export const layer: Layer.Layer<never> = Layer.effect(
|
|
|
91
91
|
);
|
|
92
92
|
},
|
|
93
93
|
context(primitive, fiber) {
|
|
94
|
-
return contextFor(fiber.
|
|
94
|
+
return contextFor(fiber.cache.span)(() => primitive["~effect/Effect/evaluate"](fiber));
|
|
95
95
|
},
|
|
96
96
|
});
|
|
97
97
|
}),
|
package/src/DurableObject.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DurableObject as CloudflareDurableObject } from "cloudflare:workers";
|
|
2
2
|
import * as Effect from "effect/Effect";
|
|
3
3
|
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Scheduler from "effect/Scheduler";
|
|
4
5
|
import * as Tracer from "effect/Tracer";
|
|
5
6
|
import type { ManagedRuntime, Scope } from "effect";
|
|
6
7
|
|
|
@@ -125,8 +126,10 @@ interface DurableObjectOptionsBase<
|
|
|
125
126
|
/**
|
|
126
127
|
* Effect run when Cloudflare loads this Durable Object instance into memory.
|
|
127
128
|
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
129
|
+
* The base layer is acquired before incoming events are admitted. The hook
|
|
130
|
+
* itself runs in the background; start it with
|
|
131
|
+
* `DurableObjectState.blockConcurrencyWhile` when incoming events should
|
|
132
|
+
* wait for setup to finish. Cloudflare may construct
|
|
130
133
|
* the same Durable Object id again after eviction or restart; use Durable
|
|
131
134
|
* Object storage if work must happen only once per id.
|
|
132
135
|
*/
|
|
@@ -298,7 +301,17 @@ export function make<
|
|
|
298
301
|
const initialize = options.initialize;
|
|
299
302
|
|
|
300
303
|
if (initialize !== undefined) {
|
|
301
|
-
state.waitUntil(
|
|
304
|
+
state.waitUntil(
|
|
305
|
+
state.blockConcurrencyWhile(async () => {
|
|
306
|
+
// Build services before admitting the first event, then let the
|
|
307
|
+
// hook synchronously install its own gate if it needs one. Its
|
|
308
|
+
// background work and failure reporting keep their waitUntil owner.
|
|
309
|
+
await Effect.runPromise(this.runtime.contextEffect, {
|
|
310
|
+
scheduler: new Scheduler.MixedScheduler("sync"),
|
|
311
|
+
});
|
|
312
|
+
state.waitUntil(this[RunSymbol](initialize, { eventLayer: false }));
|
|
313
|
+
}),
|
|
314
|
+
);
|
|
302
315
|
}
|
|
303
316
|
}
|
|
304
317
|
|
package/src/Environment.ts
CHANGED
|
@@ -33,19 +33,19 @@ export namespace WorkerConfig {
|
|
|
33
33
|
export type Key = ScalarConfigKey;
|
|
34
34
|
|
|
35
35
|
/** Read a scalar Cloudflare var or secret as a string. */
|
|
36
|
-
export const string = <const Name extends Key>(name: Name) => Config.
|
|
36
|
+
export const string = <const Name extends Key>(name: Name) => Config.String(name);
|
|
37
37
|
|
|
38
38
|
/** Read a scalar Cloudflare secret as a redacted string. */
|
|
39
|
-
export const redacted = <const Name extends Key>(name: Name) => Config.
|
|
39
|
+
export const redacted = <const Name extends Key>(name: Name) => Config.Redacted(name);
|
|
40
40
|
|
|
41
41
|
/** Read a scalar Cloudflare var or secret as a number. */
|
|
42
|
-
export const number = <const Name extends Key>(name: Name) => Config.
|
|
42
|
+
export const number = <const Name extends Key>(name: Name) => Config.Number(name);
|
|
43
43
|
|
|
44
44
|
/** Read a scalar Cloudflare var or secret as an integer. */
|
|
45
|
-
export const integer = <const Name extends Key>(name: Name) => Config.
|
|
45
|
+
export const integer = <const Name extends Key>(name: Name) => Config.Int(name);
|
|
46
46
|
|
|
47
47
|
/** Read a scalar Cloudflare var or secret as a boolean. */
|
|
48
|
-
export const boolean = <const Name extends Key>(name: Name) => Config.
|
|
48
|
+
export const boolean = <const Name extends Key>(name: Name) => Config.Boolean(name);
|
|
49
49
|
|
|
50
50
|
export interface ProviderOptions {
|
|
51
51
|
/**
|
package/src/Worker.ts
CHANGED
|
@@ -277,10 +277,17 @@ const renderFetchSuccess = <E, R, REvent, EventLayerError, EventLayerRequirement
|
|
|
277
277
|
nativeResponse !== undefined
|
|
278
278
|
? Effect.void
|
|
279
279
|
: Effect.map(Effect.context<never>(), (context) => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
280
|
+
const withoutBody = request.method === "HEAD";
|
|
281
|
+
|
|
282
|
+
webResponse = HttpServerResponse.toWeb(
|
|
283
|
+
HttpServerResponse.omitsBody(response, withoutBody)
|
|
284
|
+
? response
|
|
285
|
+
: HttpEffect.scopeTransferToStream(response),
|
|
286
|
+
{
|
|
287
|
+
withoutBody,
|
|
288
|
+
context,
|
|
289
|
+
},
|
|
290
|
+
);
|
|
284
291
|
});
|
|
285
292
|
const telemetryMiddleware = HttpMiddleware.make((self) =>
|
|
286
293
|
RpcTargets.withScope(
|
|
@@ -1,9 +1,13 @@
|
|
|
1
|
+
import * as Context from "effect/Context";
|
|
1
2
|
import * as Effect from "effect/Effect";
|
|
2
3
|
import * as Exit from "effect/Exit";
|
|
3
4
|
import * as FiberSet from "effect/FiberSet";
|
|
5
|
+
import * as Scheduler from "effect/Scheduler";
|
|
4
6
|
|
|
5
7
|
type RunCallback<A, E, R> = (effect: Effect.Effect<A, E, R>) => Promise<Exit.Exit<A, E>>;
|
|
6
8
|
|
|
9
|
+
const callbackScheduler = new Scheduler.MixedScheduler("sync");
|
|
10
|
+
|
|
7
11
|
/**
|
|
8
12
|
* Runs an Effect callback owned by a native Promise operation.
|
|
9
13
|
*
|
|
@@ -14,7 +18,14 @@ type RunCallback<A, E, R> = (effect: Effect.Effect<A, E, R>) => Promise<Exit.Exi
|
|
|
14
18
|
export const runNativeCallback = Effect.fnUntraced(function* <A, E, R, B>(
|
|
15
19
|
operation: (run: RunCallback<A, E, R>) => Promise<B>,
|
|
16
20
|
): Effect.fn.Return<B, unknown, R> {
|
|
17
|
-
|
|
21
|
+
// An older timer in the blocked parent input gate prevents later timers in
|
|
22
|
+
// this owner from running. Yield through microtasks while the native gate is
|
|
23
|
+
// held, including when the caller explicitly provided its own scheduler.
|
|
24
|
+
const callbackContext = Context.add(
|
|
25
|
+
yield* Effect.context<R>(),
|
|
26
|
+
Scheduler.Scheduler,
|
|
27
|
+
callbackScheduler,
|
|
28
|
+
);
|
|
18
29
|
|
|
19
30
|
return yield* Effect.scoped(
|
|
20
31
|
Effect.gen(function* () {
|
|
@@ -39,7 +50,7 @@ export const runNativeCallback = Effect.fnUntraced(function* <A, E, R, B>(
|
|
|
39
50
|
return yield* Effect.callback<B, unknown>((resume) => {
|
|
40
51
|
try {
|
|
41
52
|
nativePromise = operation((effect) =>
|
|
42
|
-
runPromise(Effect.provideContext(Effect.exit(effect),
|
|
53
|
+
runPromise(Effect.provideContext(Effect.exit(effect), callbackContext)),
|
|
43
54
|
);
|
|
44
55
|
} catch (cause) {
|
|
45
56
|
resume(Effect.fail(cause));
|