effect 4.0.0 → 4.0.1
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/dist/Cache.d.ts.map +1 -1
- package/dist/Cache.js +20 -16
- package/dist/Cache.js.map +1 -1
- package/dist/Cause.d.ts.map +1 -1
- package/dist/Cause.js +1 -1
- package/dist/Cause.js.map +1 -1
- package/dist/Effect.d.ts +150 -22
- package/dist/Effect.d.ts.map +1 -1
- package/dist/Effect.js +11 -5
- package/dist/Effect.js.map +1 -1
- package/dist/HashRing.d.ts.map +1 -1
- package/dist/HashRing.js +13 -5
- package/dist/HashRing.js.map +1 -1
- package/dist/Pool.d.ts +9 -5
- package/dist/Pool.d.ts.map +1 -1
- package/dist/Pool.js +34 -41
- package/dist/Pool.js.map +1 -1
- package/dist/RcMap.d.ts.map +1 -1
- package/dist/RcMap.js +16 -1
- package/dist/RcMap.js.map +1 -1
- package/dist/ScopedCache.d.ts.map +1 -1
- package/dist/ScopedCache.js +24 -16
- package/dist/ScopedCache.js.map +1 -1
- package/dist/Stream.d.ts +45 -6
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +55 -44
- package/dist/Stream.js.map +1 -1
- package/dist/ai/LanguageModel.d.ts +8 -0
- package/dist/ai/LanguageModel.d.ts.map +1 -1
- package/dist/ai/LanguageModel.js +13 -2
- package/dist/ai/LanguageModel.js.map +1 -1
- package/dist/ai/McpSchema.d.ts +508 -508
- package/dist/ai/McpSchema.d.ts.map +1 -1
- package/dist/ai/McpSchema.js +1 -1
- package/dist/ai/McpSchema.js.map +1 -1
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +11 -3
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Response.d.ts.map +1 -1
- package/dist/ai/Response.js +22 -12
- package/dist/ai/Response.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js +1 -0
- package/dist/ai/internal/mcpProtocol/v2024_11_05.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js +1 -0
- package/dist/ai/internal/mcpProtocol/v2025_03_26.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js +1 -0
- package/dist/ai/internal/mcpProtocol/v2025_06_18.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js +1 -0
- package/dist/ai/internal/mcpProtocol/v2025_11_25.js.map +1 -1
- package/dist/ai/internal/mcpSchema/v2024_11_05.js +3 -1
- package/dist/ai/internal/mcpSchema/v2024_11_05.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +11 -2
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +14 -7
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
- package/dist/cluster/SqlMessageStorage.js +21 -12
- package/dist/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/cluster/SqlRunnerStorage.js +30 -44
- package/dist/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +63 -62
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/cluster/internal/interruptors.js +8 -0
- package/dist/cluster/internal/interruptors.js.map +1 -1
- package/dist/cluster/internal/resourceMap.js +39 -14
- package/dist/cluster/internal/resourceMap.js.map +1 -1
- package/dist/cluster/internal/resourceRef.js +168 -87
- package/dist/cluster/internal/resourceRef.js.map +1 -1
- package/dist/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/http/HttpMiddleware.js +3 -2
- package/dist/http/HttpMiddleware.js.map +1 -1
- package/dist/http/HttpStaticServer.d.ts.map +1 -1
- package/dist/http/HttpStaticServer.js +14 -0
- package/dist/http/HttpStaticServer.js.map +1 -1
- package/dist/http/HttpTraceContext.d.ts +1 -1
- package/dist/http/HttpTraceContext.d.ts.map +1 -1
- package/dist/http/HttpTraceContext.js +4 -4
- package/dist/http/HttpTraceContext.js.map +1 -1
- package/dist/http-api/HttpApiError.d.ts +1 -0
- package/dist/http-api/HttpApiError.d.ts.map +1 -1
- package/dist/http-api/HttpApiError.js +1 -0
- package/dist/http-api/HttpApiError.js.map +1 -1
- package/dist/internal/core.js +43 -2
- package/dist/internal/core.js.map +1 -1
- package/dist/internal/dateTime.js +1 -1
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +63 -81
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/rcRef.js +59 -23
- package/dist/internal/rcRef.js.map +1 -1
- package/dist/internal/schema/fromJsonSchemaDocument.js +11 -8
- package/dist/internal/schema/fromJsonSchemaDocument.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +20 -14
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +2 -2
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/rpc/Utils.d.ts +2 -3
- package/dist/rpc/Utils.d.ts.map +1 -1
- package/dist/rpc/Utils.js +3 -4
- package/dist/rpc/Utils.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts +2 -0
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +1 -0
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +1 -1
- package/src/Cache.ts +22 -16
- package/src/Cause.ts +1 -1
- package/src/Effect.ts +153 -22
- package/src/HashRing.ts +14 -5
- package/src/Pool.ts +28 -36
- package/src/RcMap.ts +26 -2
- package/src/ScopedCache.ts +27 -16
- package/src/Stream.ts +158 -88
- package/src/ai/LanguageModel.ts +27 -2
- package/src/ai/McpSchema.ts +3 -3
- package/src/ai/McpServer.ts +12 -3
- package/src/ai/Response.ts +27 -12
- package/src/ai/internal/mcpProtocol/v2024_11_05.ts +1 -0
- package/src/ai/internal/mcpProtocol/v2025_03_26.ts +1 -0
- package/src/ai/internal/mcpProtocol/v2025_06_18.ts +1 -0
- package/src/ai/internal/mcpProtocol/v2025_11_25.ts +1 -0
- package/src/ai/internal/mcpSchema/v2024_11_05.ts +4 -2
- package/src/cluster/ClusterWorkflowEngine.ts +18 -4
- package/src/cluster/Sharding.ts +24 -24
- package/src/cluster/SqlMessageStorage.ts +26 -13
- package/src/cluster/SqlRunnerStorage.ts +85 -113
- package/src/cluster/internal/entityManager.ts +68 -80
- package/src/cluster/internal/interruptors.ts +13 -0
- package/src/cluster/internal/resourceMap.ts +55 -17
- package/src/cluster/internal/resourceRef.ts +189 -96
- package/src/http/HttpMiddleware.ts +8 -2
- package/src/http/HttpStaticServer.ts +16 -0
- package/src/http/HttpTraceContext.ts +8 -4
- package/src/http-api/HttpApiError.ts +1 -0
- package/src/internal/core.ts +56 -2
- package/src/internal/dateTime.ts +3 -1
- package/src/internal/effect.ts +109 -111
- package/src/internal/rcRef.ts +83 -33
- package/src/internal/schema/fromJsonSchemaDocument.ts +5 -3
- package/src/rpc/RpcClient.ts +26 -22
- package/src/rpc/RpcServer.ts +2 -1
- package/src/rpc/Utils.ts +4 -10
- package/src/workflow/WorkflowEngine.ts +4 -0
package/src/internal/effect.ts
CHANGED
|
@@ -50,7 +50,7 @@ import type { Primitive } from "./core.ts"
|
|
|
50
50
|
import {
|
|
51
51
|
args,
|
|
52
52
|
causeAnnotate,
|
|
53
|
-
|
|
53
|
+
causeCombine,
|
|
54
54
|
causeEmpty,
|
|
55
55
|
causeFromReasons,
|
|
56
56
|
CauseImpl,
|
|
@@ -238,48 +238,6 @@ export const causeAnnotations = <E>(
|
|
|
238
238
|
return Context.makeUnsafe(map)
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
const dedupeReasons = <E>(
|
|
242
|
-
self: ReadonlyArray<Cause.Reason<E>>,
|
|
243
|
-
that: ReadonlyArray<Cause.Reason<E>>
|
|
244
|
-
): Array<Cause.Reason<E>> => {
|
|
245
|
-
// Keep deduplication local so causeCombine does not retain Array.ts in the core bundle.
|
|
246
|
-
// Snapshot both arrays before invoking user-defined hash or equality methods.
|
|
247
|
-
const buckets = new Map<number, Array<Cause.Reason<E>>>()
|
|
248
|
-
const out: Array<Cause.Reason<E>> = []
|
|
249
|
-
for (const reason of self.concat(that)) {
|
|
250
|
-
const hash = Hash.hash(reason)
|
|
251
|
-
const bucket = buckets.get(hash)
|
|
252
|
-
if (bucket === undefined) {
|
|
253
|
-
buckets.set(hash, [reason])
|
|
254
|
-
} else if (bucket.some((previous) => Equal.equals(previous, reason))) {
|
|
255
|
-
continue
|
|
256
|
-
} else {
|
|
257
|
-
bucket.push(reason)
|
|
258
|
-
}
|
|
259
|
-
out.push(reason)
|
|
260
|
-
}
|
|
261
|
-
return out
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/** @internal */
|
|
265
|
-
export const causeCombine: {
|
|
266
|
-
<E2>(that: Cause.Cause<E2>): <E>(self: Cause.Cause<E>) => Cause.Cause<E | E2>
|
|
267
|
-
<E, E2>(self: Cause.Cause<E>, that: Cause.Cause<E2>): Cause.Cause<E | E2>
|
|
268
|
-
} = dual(
|
|
269
|
-
2,
|
|
270
|
-
<E, E2>(self: Cause.Cause<E>, that: Cause.Cause<E2>): Cause.Cause<E | E2> => {
|
|
271
|
-
if (self.reasons.length === 0) {
|
|
272
|
-
return that as Cause.Cause<E | E2>
|
|
273
|
-
} else if (that.reasons.length === 0) {
|
|
274
|
-
return self as Cause.Cause<E | E2>
|
|
275
|
-
}
|
|
276
|
-
const newCause = new CauseImpl<E | E2>(
|
|
277
|
-
dedupeReasons<E | E2>(self.reasons, that.reasons)
|
|
278
|
-
)
|
|
279
|
-
return Equal.equals(self, newCause) ? self : newCause
|
|
280
|
-
}
|
|
281
|
-
)
|
|
282
|
-
|
|
283
241
|
/** @internal */
|
|
284
242
|
export const causeMap: {
|
|
285
243
|
<E, E2>(f: (error: NoInfer<E>) => E2): (self: Cause.Cause<E>) => Cause.Cause<E2>
|
|
@@ -504,6 +462,21 @@ const fiberVariance = {
|
|
|
504
462
|
|
|
505
463
|
const fiberIdStore = { id: 0 }
|
|
506
464
|
|
|
465
|
+
interface AsyncContext {
|
|
466
|
+
runInAsyncScope<This, Arg, R>(fn: (this: This, arg: Arg) => R, thisArg: This, arg: Arg): R
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const AsyncResource: (new(type: string) => AsyncContext) | undefined = (() => {
|
|
470
|
+
try {
|
|
471
|
+
return (globalThis as any).process?.getBuiltinModule?.("node:async_hooks")?.AsyncResource
|
|
472
|
+
} catch {
|
|
473
|
+
return undefined
|
|
474
|
+
}
|
|
475
|
+
})()
|
|
476
|
+
|
|
477
|
+
const captureAsyncContext = (): AsyncContext | undefined =>
|
|
478
|
+
AsyncResource === undefined ? undefined : new AsyncResource("effect/Fiber")
|
|
479
|
+
|
|
507
480
|
/** @internal */
|
|
508
481
|
export const getCurrentFiber = (): Fiber.Fiber<any, any> | undefined => (globalThis as any)[currentFiberTypeId]
|
|
509
482
|
|
|
@@ -526,6 +499,7 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
|
|
|
526
499
|
this._running = false
|
|
527
500
|
this._deferredInterrupt = false
|
|
528
501
|
this._parent = undefined
|
|
502
|
+
this._asyncContext = undefined
|
|
529
503
|
this.cache.runtimeMetrics?.recordFiberStart(this.context)
|
|
530
504
|
}
|
|
531
505
|
|
|
@@ -545,6 +519,7 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
|
|
|
545
519
|
declare _running: boolean
|
|
546
520
|
declare _deferredInterrupt: boolean
|
|
547
521
|
declare _parent: FiberImpl<any, any> | undefined
|
|
522
|
+
declare _asyncContext: AsyncContext | undefined
|
|
548
523
|
|
|
549
524
|
// set in setContext
|
|
550
525
|
declare context: Context.Context<never>
|
|
@@ -605,6 +580,10 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
|
|
|
605
580
|
evaluate(effect: Primitive): void {
|
|
606
581
|
if (this._exit) {
|
|
607
582
|
return
|
|
583
|
+
} else if (this._asyncContext !== undefined) {
|
|
584
|
+
const asyncContext = this._asyncContext
|
|
585
|
+
this._asyncContext = undefined
|
|
586
|
+
return asyncContext.runInAsyncScope(this.evaluate, this, effect)
|
|
608
587
|
} else if (this._yielded !== undefined) {
|
|
609
588
|
const yielded = this._yielded as () => void
|
|
610
589
|
this._yielded = undefined
|
|
@@ -612,6 +591,7 @@ export class FiberImpl<A = any, E = any> implements Fiber.Fiber<A, E> {
|
|
|
612
591
|
}
|
|
613
592
|
const exit = this.runLoop(effect)
|
|
614
593
|
if (exit === Yield) {
|
|
594
|
+
this._asyncContext = captureAsyncContext()
|
|
615
595
|
return
|
|
616
596
|
}
|
|
617
597
|
// the interruptChildren middleware is added in Effect.forkChild, so it can be
|
|
@@ -1122,20 +1102,22 @@ export const tryPromise = <A, E = Cause.UnknownError>(
|
|
|
1122
1102
|
? ((cause: unknown) => new UnknownError(cause, "An error occurred in Effect.tryPromise"))
|
|
1123
1103
|
: options.catch
|
|
1124
1104
|
return callbackOptions<A, E>(function(resume, signal) {
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1105
|
+
// Defer the catcher until the fiber restores its async context.
|
|
1106
|
+
const failWithCatch = (cause: unknown) =>
|
|
1107
|
+
suspend(() => {
|
|
1108
|
+
try {
|
|
1109
|
+
return fail(internalCall(() => catcher(cause)) as E)
|
|
1110
|
+
} catch (err) {
|
|
1111
|
+
return die(err)
|
|
1112
|
+
}
|
|
1113
|
+
})
|
|
1132
1114
|
try {
|
|
1133
1115
|
f(signal!).then(
|
|
1134
1116
|
(a) => resume(succeed(a)),
|
|
1135
|
-
failWithCatch
|
|
1117
|
+
(e) => resume(failWithCatch(e))
|
|
1136
1118
|
)
|
|
1137
1119
|
} catch (err) {
|
|
1138
|
-
failWithCatch(err)
|
|
1120
|
+
resume(failWithCatch(err))
|
|
1139
1121
|
}
|
|
1140
1122
|
}, f.length !== 0)
|
|
1141
1123
|
}
|
|
@@ -4457,48 +4439,78 @@ export const cachedInvalidateWithTTL: {
|
|
|
4457
4439
|
self: Effect.Effect<A, E, R>,
|
|
4458
4440
|
ttl: Duration.Input | ((exit: Exit.Exit<A, E>) => Duration.Input)
|
|
4459
4441
|
): Effect.Effect<[Effect.Effect<A, E, R>, Effect.Effect<void>]> =>
|
|
4460
|
-
sync(() =>
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
|
|
4442
|
+
sync(() =>
|
|
4443
|
+
makeCachedUnsafe(
|
|
4444
|
+
self,
|
|
4445
|
+
typeof ttl === "function"
|
|
4446
|
+
? (exit: Exit.Exit<A, E>) => Duration.toMillis(Duration.fromInputUnsafe(ttl(exit)))
|
|
4447
|
+
: constant(Duration.toMillis(Duration.fromInputUnsafe(ttl)))
|
|
4448
|
+
)
|
|
4449
|
+
))
|
|
4450
|
+
|
|
4451
|
+
const infiniteTTL = constant(Infinity)
|
|
4452
|
+
|
|
4453
|
+
interface CachedRun<A, E> {
|
|
4454
|
+
fiber: FiberImpl<A, E> | undefined
|
|
4455
|
+
awaiters: number
|
|
4456
|
+
}
|
|
4457
|
+
|
|
4458
|
+
const makeCachedUnsafe = <A, E, R>(
|
|
4459
|
+
self: Effect.Effect<A, E, R>,
|
|
4460
|
+
ttlMillis: (exit: Exit.Exit<A, E>) => number
|
|
4461
|
+
): [Effect.Effect<A, E, R>, Effect.Effect<void>] => {
|
|
4462
|
+
let expiresAt = 0
|
|
4463
|
+
let exit: Exit.Exit<A, E> | undefined
|
|
4464
|
+
let current: CachedRun<A, E> | undefined
|
|
4465
|
+
|
|
4466
|
+
const join = (fiber: Fiber.Fiber<unknown, unknown>, run: CachedRun<A, E>): Effect.Effect<A, E> => {
|
|
4467
|
+
run.awaiters++
|
|
4468
|
+
onExitUnsafe(fiber, () => {
|
|
4469
|
+
// Abandon the run once every caller has left, unless it already finished.
|
|
4470
|
+
if (--run.awaiters > 0 || current !== run) return
|
|
4471
|
+
// Detach it first so new callers start a fresh run instead of joining
|
|
4472
|
+
// one that is being interrupted.
|
|
4473
|
+
current = undefined
|
|
4474
|
+
return fiberInterrupt(run.fiber!)
|
|
4475
|
+
})
|
|
4476
|
+
return fiberJoin(run.fiber!)
|
|
4477
|
+
}
|
|
4478
|
+
|
|
4479
|
+
return [
|
|
4480
|
+
withFiber((fiber) => {
|
|
4481
|
+
if (current !== undefined) return join(fiber, current)
|
|
4482
|
+
if (
|
|
4483
|
+
exit !== undefined &&
|
|
4484
|
+
(expiresAt === Infinity || fiber.getRef(ClockRef).currentTimeMillisUnsafe() < expiresAt)
|
|
4485
|
+
) {
|
|
4486
|
+
return exit
|
|
4487
|
+
}
|
|
4488
|
+
exit = undefined
|
|
4489
|
+
const clock = fiber.getRef(ClockRef)
|
|
4490
|
+
const run: CachedRun<A, E> = { fiber: undefined, awaiters: 0 }
|
|
4491
|
+
current = run
|
|
4492
|
+
run.fiber = forkUnsafe(
|
|
4493
|
+
fiber,
|
|
4494
|
+
onExitPrimitive(self, (exit_) => {
|
|
4495
|
+
// An abandoned run must not overwrite or clear a replacement run.
|
|
4496
|
+
if (current !== run) return
|
|
4497
|
+
current = undefined
|
|
4498
|
+
// Interruption is abandonment, so it is never cached.
|
|
4499
|
+
if (exitHasInterrupts(exit_)) return
|
|
4500
|
+
const duration = ttlMillis(exit_)
|
|
4501
|
+
expiresAt = duration === Infinity ? Infinity : clock.currentTimeMillisUnsafe() + duration
|
|
4502
|
+
exit = exit_
|
|
4503
|
+
}),
|
|
4504
|
+
true,
|
|
4505
|
+
true
|
|
4506
|
+
)
|
|
4507
|
+
return run.fiber._exit ?? join(fiber, run)
|
|
4508
|
+
}),
|
|
4509
|
+
sync(() => {
|
|
4510
|
+
exit = undefined
|
|
4511
|
+
})
|
|
4512
|
+
]
|
|
4513
|
+
}
|
|
4502
4514
|
|
|
4503
4515
|
/** @internal */
|
|
4504
4516
|
export const cachedWithTTL: {
|
|
@@ -4525,23 +4537,7 @@ export const cachedWithTTL: {
|
|
|
4525
4537
|
|
|
4526
4538
|
/** @internal */
|
|
4527
4539
|
export const cached = <A, E, R>(self: Effect.Effect<A, E, R>): Effect.Effect<Effect.Effect<A, E, R>> =>
|
|
4528
|
-
sync(() =>
|
|
4529
|
-
const latch = makeLatchUnsafe(false)
|
|
4530
|
-
let started = false
|
|
4531
|
-
let exit: Exit.Exit<A, E> | undefined
|
|
4532
|
-
const wait = flatMap(latch.await, () => exit!)
|
|
4533
|
-
return withFiber((fiber) => {
|
|
4534
|
-
if (exit !== undefined) return exit
|
|
4535
|
-
if (started) return wait
|
|
4536
|
-
started = true
|
|
4537
|
-
onExitUnsafe<A, E>(fiber, (result) =>
|
|
4538
|
-
sync(() => {
|
|
4539
|
-
exit = result
|
|
4540
|
-
latch.openUnsafe()
|
|
4541
|
-
}))
|
|
4542
|
-
return self
|
|
4543
|
-
})
|
|
4544
|
-
})
|
|
4540
|
+
sync(() => makeCachedUnsafe(self, infiniteTTL)[0])
|
|
4545
4541
|
|
|
4546
4542
|
// ----------------------------------------------------------------------------
|
|
4547
4543
|
// interruption
|
|
@@ -5531,6 +5527,8 @@ export const forkUnsafe = <FA, FE, A, E, R>(
|
|
|
5531
5527
|
if (immediate) {
|
|
5532
5528
|
child.evaluate(effect as any)
|
|
5533
5529
|
} else {
|
|
5530
|
+
// Preserve the fork context rather than the dispatcher's context.
|
|
5531
|
+
child._asyncContext = captureAsyncContext()
|
|
5534
5532
|
parentRuntime.currentDispatcher.scheduleTask(() => child.evaluate(effect as any), 0)
|
|
5535
5533
|
}
|
|
5536
5534
|
if (!daemon && !child._exit) {
|
package/src/internal/rcRef.ts
CHANGED
|
@@ -7,17 +7,26 @@ import { identity } from "../Function.ts"
|
|
|
7
7
|
import { pipeArguments } from "../Pipeable.ts"
|
|
8
8
|
import type * as RcRef from "../RcRef.ts"
|
|
9
9
|
import * as Scope from "../Scope.ts"
|
|
10
|
-
import * as Semaphore from "../Semaphore.ts"
|
|
11
10
|
|
|
12
11
|
const TypeId = "~effect/RcRef"
|
|
13
12
|
|
|
14
|
-
type State<A> = State.Empty | State.Acquired<A> | State.Closed
|
|
13
|
+
type State<A> = State.Empty | State.Acquiring<A> | State.Acquired<A> | State.Closed
|
|
15
14
|
|
|
16
15
|
declare namespace State {
|
|
17
16
|
interface Empty {
|
|
18
17
|
readonly _tag: "Empty"
|
|
19
18
|
}
|
|
20
19
|
|
|
20
|
+
interface Acquiring<A> {
|
|
21
|
+
readonly _tag: "Acquiring"
|
|
22
|
+
// Assigned as soon as the acquisition is forked.
|
|
23
|
+
fiber: Fiber.Fiber<Acquired<A>, unknown>
|
|
24
|
+
awaiters: number
|
|
25
|
+
// Set once references are allocated to the waiters, which can happen
|
|
26
|
+
// before the acquisition fiber exits.
|
|
27
|
+
acquired: Acquired<A> | undefined
|
|
28
|
+
}
|
|
29
|
+
|
|
21
30
|
interface Acquired<A> {
|
|
22
31
|
readonly _tag: "Acquired"
|
|
23
32
|
readonly value: A
|
|
@@ -48,7 +57,6 @@ class RcRefImpl<A, E> implements RcRef.RcRef<A, E> {
|
|
|
48
57
|
}
|
|
49
58
|
|
|
50
59
|
state: State<A> = stateEmpty
|
|
51
|
-
readonly semaphore = Semaphore.makeUnsafe(1)
|
|
52
60
|
readonly acquire: Effect.Effect<A, E>
|
|
53
61
|
readonly context: Context.Context<never>
|
|
54
62
|
readonly scope: Scope.Scope
|
|
@@ -85,11 +93,11 @@ export const make = <A, E, R>(options: {
|
|
|
85
93
|
)
|
|
86
94
|
return Effect.as(
|
|
87
95
|
Scope.addFinalizerExit(scope, () => {
|
|
88
|
-
const
|
|
89
|
-
? Scope.close(ref.state.scope, Exit.void)
|
|
90
|
-
: Effect.void
|
|
96
|
+
const state = ref.state
|
|
91
97
|
ref.state = stateClosed
|
|
92
|
-
return close
|
|
98
|
+
if (state._tag === "Acquired") return Scope.close(state.scope, Exit.void)
|
|
99
|
+
if (state._tag === "Acquiring") return Fiber.interrupt(state.fiber)
|
|
100
|
+
return Effect.void
|
|
93
101
|
}),
|
|
94
102
|
ref
|
|
95
103
|
)
|
|
@@ -110,40 +118,82 @@ const getState = <A, E>(
|
|
|
110
118
|
? Effect.as(Fiber.interrupt(state.fiber), state)
|
|
111
119
|
: Effect.succeed(state)
|
|
112
120
|
}
|
|
121
|
+
case "Acquiring": {
|
|
122
|
+
const state = self.state
|
|
123
|
+
state.awaiters++
|
|
124
|
+
return awaitAcquiring(self, state, restore)
|
|
125
|
+
}
|
|
113
126
|
case "Empty": {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
127
|
+
// The caller that starts the acquisition counts as its first awaiter.
|
|
128
|
+
const acquiring: State.Acquiring<A> = {
|
|
129
|
+
_tag: "Acquiring",
|
|
130
|
+
fiber: undefined as any,
|
|
131
|
+
awaiters: 1,
|
|
132
|
+
acquired: undefined
|
|
133
|
+
}
|
|
134
|
+
self.state = acquiring
|
|
135
|
+
const scope = Scope.makeUnsafe()
|
|
136
|
+
// Acquire on a detached fiber so the acquisition is shared by every
|
|
137
|
+
// caller and only interrupted once all of them have been interrupted.
|
|
138
|
+
const acquire = Effect.provideContext(
|
|
139
|
+
self.acquire as Effect.Effect<A, E>,
|
|
140
|
+
Context.add(self.context, Scope.Scope, scope)
|
|
141
|
+
).pipe(
|
|
142
|
+
Effect.flatMap((value) => {
|
|
143
|
+
// The ref was closed, or the acquisition was abandoned.
|
|
144
|
+
if (self.state !== acquiring) {
|
|
145
|
+
return Effect.interrupt
|
|
146
|
+
}
|
|
147
|
+
const state: State.Acquired<A> = {
|
|
148
|
+
_tag: "Acquired",
|
|
149
|
+
value,
|
|
150
|
+
scope,
|
|
151
|
+
fiber: undefined,
|
|
152
|
+
// Every caller still waiting holds a reference.
|
|
153
|
+
refCount: acquiring.awaiters,
|
|
154
|
+
invalidated: false
|
|
118
155
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
const state: State.Acquired<A> = {
|
|
129
|
-
_tag: "Acquired",
|
|
130
|
-
value,
|
|
131
|
-
scope,
|
|
132
|
-
fiber: undefined,
|
|
133
|
-
refCount: 1,
|
|
134
|
-
invalidated: false
|
|
135
|
-
}
|
|
136
|
-
self.state = state
|
|
137
|
-
return Effect.succeed(state)
|
|
138
|
-
}),
|
|
139
|
-
Effect.onExit((exit) => Exit.isFailure(exit) ? Scope.close(scope, exit) : Effect.void)
|
|
140
|
-
)
|
|
156
|
+
self.state = state
|
|
157
|
+
acquiring.acquired = state
|
|
158
|
+
return Effect.succeed(state)
|
|
159
|
+
}),
|
|
160
|
+
Effect.onExit((exit) => {
|
|
161
|
+
if (Exit.isSuccess(exit)) return Effect.void
|
|
162
|
+
if (self.state === acquiring) self.state = stateEmpty
|
|
163
|
+
return Scope.close(scope, exit)
|
|
141
164
|
})
|
|
142
165
|
)
|
|
166
|
+
acquiring.fiber = Effect.runForkWith(Fiber.getCurrent()!.context)(acquire)
|
|
167
|
+
return awaitAcquiring(self, acquiring, restore)
|
|
143
168
|
}
|
|
144
169
|
}
|
|
145
170
|
}
|
|
146
171
|
|
|
172
|
+
const awaitAcquiring = <A, E>(
|
|
173
|
+
self: RcRefImpl<A, E>,
|
|
174
|
+
acquiring: State.Acquiring<A>,
|
|
175
|
+
restore: <AX, EX, RX>(effect: Effect.Effect<AX, EX, RX>) => Effect.Effect<AX, EX, RX>
|
|
176
|
+
): Effect.Effect<State.Acquired<A>, E> =>
|
|
177
|
+
Effect.onExit(
|
|
178
|
+
restore(Fiber.join(acquiring.fiber)) as Effect.Effect<State.Acquired<A>, E>,
|
|
179
|
+
(exit) => {
|
|
180
|
+
const acquired = acquiring.acquired
|
|
181
|
+
if (acquired !== undefined) {
|
|
182
|
+
// The acquisition counted this caller as a reference, possibly before
|
|
183
|
+
// its fiber exited. Release it if the caller was interrupted before it
|
|
184
|
+
// could register its finalizer.
|
|
185
|
+
return Exit.isFailure(exit) ? release(self, acquired) : Effect.void
|
|
186
|
+
}
|
|
187
|
+
// Abandon the acquisition once every caller has left, unless it already
|
|
188
|
+
// failed (a failed acquisition allocated no references).
|
|
189
|
+
if (--acquiring.awaiters > 0 || self.state !== acquiring) return Effect.void
|
|
190
|
+
// Detach it first so new callers start a fresh acquisition instead of
|
|
191
|
+
// joining one that is being interrupted.
|
|
192
|
+
self.state = stateEmpty
|
|
193
|
+
return Fiber.interrupt(acquiring.fiber)
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
147
197
|
/** @internal */
|
|
148
198
|
export const get = <A, E>(self_: RcRef.RcRef<A, E>): Effect.Effect<A, E, Scope.Scope> => {
|
|
149
199
|
const self = self_ as RcRefImpl<A, E>
|
|
@@ -1067,13 +1067,15 @@ function translateJsonSchemaMultiDocument(
|
|
|
1067
1067
|
)
|
|
1068
1068
|
case "ignore":
|
|
1069
1069
|
return undefined
|
|
1070
|
-
case "apply":
|
|
1070
|
+
case "apply": {
|
|
1071
|
+
let regex: RegExp
|
|
1071
1072
|
try {
|
|
1072
|
-
globalThis.RegExp(pattern, jsonSchemaPatternFlags)
|
|
1073
|
+
regex = globalThis.RegExp(pattern, jsonSchemaPatternFlags)
|
|
1073
1074
|
} catch {
|
|
1074
1075
|
throw errorWithPath("Cannot import pattern using ECMAScript Unicode mode.", path)
|
|
1075
1076
|
}
|
|
1076
|
-
return jsonSchemaFilter("effect/schema/isPattern", { source:
|
|
1077
|
+
return jsonSchemaFilter("effect/schema/isPattern", { source: regex.source, flags: regex.flags })
|
|
1078
|
+
}
|
|
1077
1079
|
}
|
|
1078
1080
|
}
|
|
1079
1081
|
|
package/src/rpc/RpcClient.ts
CHANGED
|
@@ -454,9 +454,11 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
|
|
|
454
454
|
const id = generateRequestId()
|
|
455
455
|
|
|
456
456
|
const scope = Context.getUnsafe(fiber.context, Scope.Scope)
|
|
457
|
+
const queue = yield* Queue.bounded<any, any>(streamBufferSize)
|
|
457
458
|
yield* Scope.addFinalizerExit(
|
|
458
459
|
scope,
|
|
459
460
|
(exit) => {
|
|
461
|
+
Queue.shutdownUnsafe(queue)
|
|
460
462
|
if (!entries.has(id)) return Effect.void
|
|
461
463
|
entries.delete(id)
|
|
462
464
|
return sendInterrupt(
|
|
@@ -469,7 +471,6 @@ export const makeNoSerialization: <Rpcs extends Rpc.Any, E, const Flatten extend
|
|
|
469
471
|
}
|
|
470
472
|
)
|
|
471
473
|
|
|
472
|
-
const queue = yield* Queue.bounded<any, any>(streamBufferSize)
|
|
473
474
|
entries.set(id, {
|
|
474
475
|
_tag: "Queue",
|
|
475
476
|
rpc,
|
|
@@ -671,6 +672,12 @@ export const make: <Rpcs extends Rpc.Any, const Flatten extends boolean = false>
|
|
|
671
672
|
}
|
|
672
673
|
const entries = new Map<RequestId, ClientEntry>()
|
|
673
674
|
|
|
675
|
+
const interruptRequest = (requestId: RequestId): Effect.Effect<void, RpcClientError> => {
|
|
676
|
+
if (!entries.has(requestId)) return Effect.void
|
|
677
|
+
entries.delete(requestId)
|
|
678
|
+
return send(clientId, { _tag: "Interrupt", requestId }) as Effect.Effect<void, RpcClientError>
|
|
679
|
+
}
|
|
680
|
+
|
|
674
681
|
const { client, write } = yield* makeNoSerialization(group, {
|
|
675
682
|
...options,
|
|
676
683
|
supportsAck,
|
|
@@ -711,13 +718,7 @@ export const make: <Rpcs extends Rpc.Any, const Flatten extends boolean = false>
|
|
|
711
718
|
}) as Effect.Effect<void, RpcClientError>
|
|
712
719
|
}
|
|
713
720
|
case "Interrupt": {
|
|
714
|
-
|
|
715
|
-
if (!entry) return Effect.void
|
|
716
|
-
entries.delete(message.requestId)
|
|
717
|
-
return send(clientId, {
|
|
718
|
-
_tag: "Interrupt",
|
|
719
|
-
requestId: message.requestId
|
|
720
|
-
}) as Effect.Effect<void, RpcClientError>
|
|
721
|
+
return interruptRequest(message.requestId)
|
|
721
722
|
}
|
|
722
723
|
case "Eof": {
|
|
723
724
|
return Effect.void
|
|
@@ -732,19 +733,22 @@ export const make: <Rpcs extends Rpc.Any, const Flatten extends boolean = false>
|
|
|
732
733
|
const requestId = RequestId(message.requestId)
|
|
733
734
|
const entry = entries.get(requestId)
|
|
734
735
|
if (!entry || Option.isNone(entry.schemas.decodeChunk)) return Effect.void
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
requestId:
|
|
746
|
-
|
|
747
|
-
|
|
736
|
+
const decodeChunk = entry.schemas.decodeChunk.value
|
|
737
|
+
return Effect.uninterruptibleMask((restore) =>
|
|
738
|
+
restore(
|
|
739
|
+
decodeChunk(message.values).pipe(
|
|
740
|
+
Effect.provideContext(entry.context),
|
|
741
|
+
Effect.orDie,
|
|
742
|
+
Effect.flatMap((chunk) => write({ _tag: "Chunk", clientId: 0, requestId, values: chunk }))
|
|
743
|
+
)
|
|
744
|
+
).pipe(
|
|
745
|
+
Effect.onError((cause) =>
|
|
746
|
+
write({ _tag: "Exit", clientId: 0, requestId, exit: Exit.failCause(cause) }).pipe(
|
|
747
|
+
Effect.andThen(Cause.hasInterrupts(cause) ? Effect.void : Effect.ignore(interruptRequest(requestId)))
|
|
748
|
+
)
|
|
749
|
+
),
|
|
750
|
+
// a decode failure only ends its own request; interruption still propagates
|
|
751
|
+
Effect.catchCauseIf((cause) => !Cause.hasInterrupts(cause), () => Effect.void)
|
|
748
752
|
)
|
|
749
753
|
) as Effect.Effect<void>
|
|
750
754
|
}
|
|
@@ -768,7 +772,7 @@ export const make: <Rpcs extends Rpc.Any, const Flatten extends boolean = false>
|
|
|
768
772
|
case "ClientProtocolError": {
|
|
769
773
|
const exit = Exit.fail(message.error)
|
|
770
774
|
return Effect.forEach(
|
|
771
|
-
entries.keys(),
|
|
775
|
+
Array.from(entries.keys()),
|
|
772
776
|
(requestId) => write({ _tag: "Exit", clientId: 0, requestId, exit: exit as any })
|
|
773
777
|
)
|
|
774
778
|
}
|
package/src/rpc/RpcServer.ts
CHANGED
|
@@ -1392,13 +1392,14 @@ export const makeProtocolStdio = Effect.gen(function*() {
|
|
|
1392
1392
|
Effect.sandbox,
|
|
1393
1393
|
Effect.tapError(Effect.logError),
|
|
1394
1394
|
Effect.retry(Schedule.spaced(500)),
|
|
1395
|
-
Effect.
|
|
1395
|
+
Effect.andThen(() => writeRequest(0, constEof)),
|
|
1396
1396
|
Effect.forkScoped
|
|
1397
1397
|
)
|
|
1398
1398
|
|
|
1399
1399
|
yield* Stream.fromQueue(queue).pipe(
|
|
1400
1400
|
Stream.run(stdio.stdout()),
|
|
1401
1401
|
Effect.retry(Schedule.spaced(500)),
|
|
1402
|
+
Effect.ensuring(Effect.forkDetach(Fiber.interrupt(fiber), { startImmediately: true })),
|
|
1402
1403
|
Effect.forkScoped
|
|
1403
1404
|
)
|
|
1404
1405
|
|
package/src/rpc/Utils.ts
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* Client and server protocol constructors use these helpers to expose a stable
|
|
5
|
-
* service before the active receiver is installed. Writes made before `run`
|
|
6
|
-
* starts are buffered with their current `Context`, then replayed once the
|
|
7
|
-
* receiver is ready.
|
|
2
|
+
* Buffers RPC protocol messages until their receive loop starts.
|
|
8
3
|
*
|
|
9
4
|
* @stability unstable
|
|
10
5
|
* @since 4.0.0
|
|
@@ -16,9 +11,8 @@ import type { Protocol } from "./RpcClient.ts"
|
|
|
16
11
|
import type { FromServerEncoded } from "./RpcMessage.ts"
|
|
17
12
|
|
|
18
13
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the previous writer when the run ends.
|
|
14
|
+
* Buffers writes until `run` replays them with their original contexts and
|
|
15
|
+
* installs the writer. Restores the previous writer when `run` ends.
|
|
22
16
|
*
|
|
23
17
|
* @stability unstable
|
|
24
18
|
* @category services
|
|
@@ -43,12 +37,12 @@ export const withRun = <
|
|
|
43
37
|
run(f) {
|
|
44
38
|
return semaphore.withPermits(1)(Effect.gen(function*() {
|
|
45
39
|
const prev = write
|
|
46
|
-
write = f
|
|
47
40
|
|
|
48
41
|
for (const [args, context] of buffer) {
|
|
49
42
|
yield* Effect.provideContext(Effect.suspend(() => f(...args)), context)
|
|
50
43
|
}
|
|
51
44
|
buffer = []
|
|
45
|
+
write = f
|
|
52
46
|
|
|
53
47
|
return yield* Effect.onExit(Effect.never, () => {
|
|
54
48
|
write = prev
|
|
@@ -274,6 +274,9 @@ export class WorkflowInstance extends Context.Service<
|
|
|
274
274
|
/** Deferred names this run parked on; their completions preempt the run. */
|
|
275
275
|
readonly awaitedDeferreds: Set<string>
|
|
276
276
|
|
|
277
|
+
/** Deferred names this run completed itself; their completions need not wake it. */
|
|
278
|
+
readonly completedDeferreds: Set<string>
|
|
279
|
+
|
|
277
280
|
readonly activityState: {
|
|
278
281
|
count: number
|
|
279
282
|
readonly latch: Latch.Latch
|
|
@@ -294,6 +297,7 @@ export class WorkflowInstance extends Context.Service<
|
|
|
294
297
|
abandoned: false,
|
|
295
298
|
cause: undefined,
|
|
296
299
|
awaitedDeferreds: new Set(),
|
|
300
|
+
completedDeferreds: new Set(),
|
|
297
301
|
activityState: {
|
|
298
302
|
count: 0,
|
|
299
303
|
latch: Latch.makeUnsafe()
|