effect 4.0.3 → 4.0.4

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 (207) hide show
  1. package/AGENTS.md +24 -0
  2. package/CLAUDE.md +24 -0
  3. package/ai-docs/package.json +2 -2
  4. package/ai-docs/src/52_graphql/10_basics.ts +111 -0
  5. package/ai-docs/src/52_graphql/20_testing.ts +45 -0
  6. package/ai-docs/src/52_graphql/30_batching.ts +116 -0
  7. package/ai-docs/src/52_graphql/40_aliased-batching.ts +122 -0
  8. package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
  9. package/ai-docs/src/52_graphql/index.md +6 -0
  10. package/dist/Context.d.ts.map +1 -1
  11. package/dist/Context.js +9 -0
  12. package/dist/Context.js.map +1 -1
  13. package/dist/RequestResolver.d.ts.map +1 -1
  14. package/dist/RequestResolver.js +4 -10
  15. package/dist/RequestResolver.js.map +1 -1
  16. package/dist/Scheduler.d.ts +9 -0
  17. package/dist/Scheduler.d.ts.map +1 -1
  18. package/dist/Scheduler.js +9 -0
  19. package/dist/Scheduler.js.map +1 -1
  20. package/dist/Schema.d.ts +8 -0
  21. package/dist/Schema.d.ts.map +1 -1
  22. package/dist/Schema.js +7 -0
  23. package/dist/Schema.js.map +1 -1
  24. package/dist/SchemaGetter.d.ts +4 -0
  25. package/dist/SchemaGetter.d.ts.map +1 -1
  26. package/dist/SchemaGetter.js +52 -33
  27. package/dist/SchemaGetter.js.map +1 -1
  28. package/dist/Sink.d.ts.map +1 -1
  29. package/dist/Sink.js +38 -17
  30. package/dist/Sink.js.map +1 -1
  31. package/dist/Stream.d.ts +11 -0
  32. package/dist/Stream.d.ts.map +1 -1
  33. package/dist/Stream.js +16 -1
  34. package/dist/Stream.js.map +1 -1
  35. package/dist/Utils.js +8 -3
  36. package/dist/Utils.js.map +1 -1
  37. package/dist/ai/AnthropicStructuredOutput.js +0 -2
  38. package/dist/ai/AnthropicStructuredOutput.js.map +1 -1
  39. package/dist/ai/Chat.d.ts +2 -2
  40. package/dist/ai/Chat.d.ts.map +1 -1
  41. package/dist/ai/LanguageModel.d.ts +38 -7
  42. package/dist/ai/LanguageModel.d.ts.map +1 -1
  43. package/dist/ai/LanguageModel.js +25 -7
  44. package/dist/ai/LanguageModel.js.map +1 -1
  45. package/dist/ai/McpServer.d.ts +55 -0
  46. package/dist/ai/McpServer.d.ts.map +1 -1
  47. package/dist/ai/McpServer.js +132 -22
  48. package/dist/ai/McpServer.js.map +1 -1
  49. package/dist/ai/Prompt.d.ts.map +1 -1
  50. package/dist/ai/Prompt.js +28 -5
  51. package/dist/ai/Prompt.js.map +1 -1
  52. package/dist/ai/Response.d.ts +4 -3
  53. package/dist/ai/Response.d.ts.map +1 -1
  54. package/dist/ai/Response.js.map +1 -1
  55. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  56. package/dist/cluster/ClusterWorkflowEngine.js +46 -8
  57. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  58. package/dist/cluster/Sharding.d.ts.map +1 -1
  59. package/dist/cluster/Sharding.js +6 -4
  60. package/dist/cluster/Sharding.js.map +1 -1
  61. package/dist/encoding/Sse.d.ts +6 -0
  62. package/dist/encoding/Sse.d.ts.map +1 -1
  63. package/dist/encoding/Sse.js +62 -70
  64. package/dist/encoding/Sse.js.map +1 -1
  65. package/dist/graphql/GraphQL.d.ts +349 -0
  66. package/dist/graphql/GraphQL.d.ts.map +1 -0
  67. package/dist/graphql/GraphQL.js +207 -0
  68. package/dist/graphql/GraphQL.js.map +1 -0
  69. package/dist/graphql/GraphQLClient.d.ts +562 -0
  70. package/dist/graphql/GraphQLClient.d.ts.map +1 -0
  71. package/dist/graphql/GraphQLClient.js +498 -0
  72. package/dist/graphql/GraphQLClient.js.map +1 -0
  73. package/dist/graphql/GraphQLClientError.d.ts +445 -0
  74. package/dist/graphql/GraphQLClientError.d.ts.map +1 -0
  75. package/dist/graphql/GraphQLClientError.js +479 -0
  76. package/dist/graphql/GraphQLClientError.js.map +1 -0
  77. package/dist/graphql/GraphQLGroup.d.ts +118 -0
  78. package/dist/graphql/GraphQLGroup.d.ts.map +1 -0
  79. package/dist/graphql/GraphQLGroup.js +109 -0
  80. package/dist/graphql/GraphQLGroup.js.map +1 -0
  81. package/dist/graphql/GraphQLMiddleware.d.ts +249 -0
  82. package/dist/graphql/GraphQLMiddleware.d.ts.map +1 -0
  83. package/dist/graphql/GraphQLMiddleware.js +130 -0
  84. package/dist/graphql/GraphQLMiddleware.js.map +1 -0
  85. package/dist/graphql/GraphQLProtocol.d.ts +731 -0
  86. package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
  87. package/dist/graphql/GraphQLProtocol.js +719 -0
  88. package/dist/graphql/GraphQLProtocol.js.map +1 -0
  89. package/dist/graphql/GraphQLResolver.d.ts +453 -0
  90. package/dist/graphql/GraphQLResolver.d.ts.map +1 -0
  91. package/dist/graphql/GraphQLResolver.js +552 -0
  92. package/dist/graphql/GraphQLResolver.js.map +1 -0
  93. package/dist/graphql/index.d.ts +40 -0
  94. package/dist/graphql/index.d.ts.map +1 -0
  95. package/dist/graphql/index.js +41 -0
  96. package/dist/graphql/index.js.map +1 -0
  97. package/dist/http/HttpClient.d.ts +15 -0
  98. package/dist/http/HttpClient.d.ts.map +1 -1
  99. package/dist/http/HttpClient.js +24 -10
  100. package/dist/http/HttpClient.js.map +1 -1
  101. package/dist/http/HttpEffect.d.ts.map +1 -1
  102. package/dist/http/HttpEffect.js +14 -7
  103. package/dist/http/HttpEffect.js.map +1 -1
  104. package/dist/http/HttpMiddleware.d.ts.map +1 -1
  105. package/dist/http/HttpMiddleware.js +31 -32
  106. package/dist/http/HttpMiddleware.js.map +1 -1
  107. package/dist/http/HttpRouter.d.ts +35 -0
  108. package/dist/http/HttpRouter.d.ts.map +1 -1
  109. package/dist/http/HttpRouter.js +35 -0
  110. package/dist/http/HttpRouter.js.map +1 -1
  111. package/dist/http/HttpServerResponse.d.ts +8 -6
  112. package/dist/http/HttpServerResponse.d.ts.map +1 -1
  113. package/dist/http/HttpServerResponse.js +27 -16
  114. package/dist/http/HttpServerResponse.js.map +1 -1
  115. package/dist/http/Multipart.d.ts.map +1 -1
  116. package/dist/http/Multipart.js +15 -1
  117. package/dist/http/Multipart.js.map +1 -1
  118. package/dist/http-api/HttpApiScalar.d.ts +5 -0
  119. package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
  120. package/dist/http-api/HttpApiScalar.js.map +1 -1
  121. package/dist/internal/dateTime.js +40 -35
  122. package/dist/internal/dateTime.js.map +1 -1
  123. package/dist/internal/effect.js +8 -3
  124. package/dist/internal/effect.js.map +1 -1
  125. package/dist/internal/graphql.d.ts +2 -0
  126. package/dist/internal/graphql.d.ts.map +1 -0
  127. package/dist/internal/graphql.js +195 -0
  128. package/dist/internal/graphql.js.map +1 -0
  129. package/dist/internal/graphqlHeaders.d.ts +4 -0
  130. package/dist/internal/graphqlHeaders.d.ts.map +1 -0
  131. package/dist/internal/graphqlHeaders.js +11 -0
  132. package/dist/internal/graphqlHeaders.js.map +1 -0
  133. package/dist/internal/request.js +20 -1
  134. package/dist/internal/request.js.map +1 -1
  135. package/dist/internal/schema/toCodeDocument.js +4 -1
  136. package/dist/internal/schema/toCodeDocument.js.map +1 -1
  137. package/dist/internal/schema/toJsonSchemaDocument.js +29 -5
  138. package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
  139. package/dist/internal/version.js +1 -1
  140. package/dist/net/Host.d.ts +49 -0
  141. package/dist/net/Host.d.ts.map +1 -1
  142. package/dist/net/Host.js +55 -0
  143. package/dist/net/Host.js.map +1 -1
  144. package/dist/net/NetAddress.d.ts +8 -0
  145. package/dist/net/NetAddress.d.ts.map +1 -1
  146. package/dist/net/NetAddress.js +8 -0
  147. package/dist/net/NetAddress.js.map +1 -1
  148. package/dist/rpc/RpcServer.d.ts.map +1 -1
  149. package/dist/rpc/RpcServer.js +36 -9
  150. package/dist/rpc/RpcServer.js.map +1 -1
  151. package/dist/socket/Socket.d.ts +6 -0
  152. package/dist/socket/Socket.d.ts.map +1 -1
  153. package/dist/socket/Socket.js.map +1 -1
  154. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  155. package/dist/workflow/DurableDeferred.js +32 -3
  156. package/dist/workflow/DurableDeferred.js.map +1 -1
  157. package/dist/workflow/WorkflowEngine.d.ts +24 -2
  158. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  159. package/dist/workflow/WorkflowEngine.js +11 -2
  160. package/dist/workflow/WorkflowEngine.js.map +1 -1
  161. package/package.json +6 -3
  162. package/src/Context.ts +9 -0
  163. package/src/RequestResolver.ts +4 -10
  164. package/src/Scheduler.ts +9 -0
  165. package/src/Schema.ts +8 -0
  166. package/src/SchemaGetter.ts +47 -35
  167. package/src/Sink.ts +45 -18
  168. package/src/Stream.ts +33 -4
  169. package/src/Utils.ts +8 -5
  170. package/src/ai/AnthropicStructuredOutput.ts +2 -2
  171. package/src/ai/Chat.ts +2 -2
  172. package/src/ai/LanguageModel.ts +96 -24
  173. package/src/ai/McpServer.ts +244 -90
  174. package/src/ai/Prompt.ts +24 -10
  175. package/src/ai/Response.ts +6 -3
  176. package/src/cluster/ClusterWorkflowEngine.ts +51 -12
  177. package/src/cluster/Sharding.ts +12 -9
  178. package/src/encoding/Sse.ts +69 -79
  179. package/src/graphql/GraphQL.ts +494 -0
  180. package/src/graphql/GraphQLClient.ts +912 -0
  181. package/src/graphql/GraphQLClientError.ts +542 -0
  182. package/src/graphql/GraphQLGroup.ts +167 -0
  183. package/src/graphql/GraphQLMiddleware.ts +253 -0
  184. package/src/graphql/GraphQLProtocol.ts +1286 -0
  185. package/src/graphql/GraphQLResolver.ts +1010 -0
  186. package/src/graphql/index.ts +48 -0
  187. package/src/http/HttpClient.ts +54 -10
  188. package/src/http/HttpEffect.ts +11 -5
  189. package/src/http/HttpMiddleware.ts +33 -34
  190. package/src/http/HttpRouter.ts +35 -0
  191. package/src/http/HttpServerResponse.ts +28 -16
  192. package/src/http/Multipart.ts +17 -1
  193. package/src/http-api/HttpApiScalar.ts +5 -0
  194. package/src/internal/dateTime.ts +13 -10
  195. package/src/internal/effect.ts +8 -3
  196. package/src/internal/graphql.ts +263 -0
  197. package/src/internal/graphqlHeaders.ts +11 -0
  198. package/src/internal/request.ts +31 -3
  199. package/src/internal/schema/toCodeDocument.ts +7 -1
  200. package/src/internal/schema/toJsonSchemaDocument.ts +34 -2
  201. package/src/internal/version.ts +1 -1
  202. package/src/net/Host.ts +72 -0
  203. package/src/net/NetAddress.ts +9 -0
  204. package/src/rpc/RpcServer.ts +55 -15
  205. package/src/socket/Socket.ts +6 -0
  206. package/src/workflow/DurableDeferred.ts +43 -5
  207. package/src/workflow/WorkflowEngine.ts +48 -4
@@ -134,6 +134,25 @@ const CurrentAttempt = Context.Reference<number>(
134
134
  { defaultValue: () => 1 }
135
135
  )
136
136
 
137
+ /** Awaits registered under the instance copy of a `recordHeld` body. */
138
+ const awaitOwners = new WeakMap<WorkflowInstance["Service"], Array<string>>()
139
+
140
+ const registerAwait = (instance: WorkflowInstance["Service"], name: string) => {
141
+ instance.awaitedDeferreds.set(name, (instance.awaitedDeferreds.get(name) ?? 0) + 1)
142
+ awaitOwners.get(instance)?.push(name)
143
+ }
144
+
145
+ const releaseAwaits = (instance: WorkflowInstance["Service"], names: Array<string>) => {
146
+ for (const name of names) {
147
+ const count = instance.awaitedDeferreds.get(name)! - 1
148
+ if (count === 0) {
149
+ instance.awaitedDeferreds.delete(name)
150
+ } else {
151
+ instance.awaitedDeferreds.set(name, count)
152
+ }
153
+ }
154
+ }
155
+
137
156
  const await_: <Success extends Schema.Constraint, Error extends Schema.Constraint>(
138
157
  self: DurableDeferred<Success, Error>
139
158
  ) => Effect.Effect<
@@ -150,7 +169,7 @@ const await_: <Success extends Schema.Constraint, Error extends Schema.Constrain
150
169
  const engine = yield* EngineTag
151
170
  const instance = yield* InstanceTag
152
171
  // Register before the read so any later completion can preempt the run.
153
- instance.awaitedDeferreds.add(self.name)
172
+ registerAwait(instance, self.name)
154
173
  const exit = yield* Workflow.wrapActivityResult(
155
174
  engine.deferredResult(self),
156
175
  Option.isNone
@@ -343,6 +362,10 @@ const recordHeld = Effect.fnUntraced(function*<A, E, R>(
343
362
  suspended: false,
344
363
  activityState: { count: 0, latch: Latch.makeUnsafe() }
345
364
  }
365
+ // A losing branch parked on a durable wait has already exited when the
366
+ // race settles, so this body owns the waits registered beneath it.
367
+ const owned: Array<string> = []
368
+ awaitOwners.set(local, owned)
346
369
  // A parked `effect` interrupts the fiber it runs on. The child fiber
347
370
  // keeps that from skipping the suspension handling below.
348
371
  const fiber = yield* effect.pipe(
@@ -350,10 +373,25 @@ const recordHeld = Effect.fnUntraced(function*<A, E, R>(
350
373
  Effect.onExit((exit) => recordExit(engine, instance, self, exit)),
351
374
  Effect.forkChild({ startImmediately: true })
352
375
  )
353
- const exit = yield* Effect.onInterrupt(Fiber.await(fiber), () => Fiber.interrupt(fiber))
354
- return local.suspended && Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)
355
- ? Option.none()
356
- : Option.some(exit)
376
+ return yield* Effect.onInterrupt(Fiber.await(fiber), () => Fiber.interrupt(fiber)).pipe(
377
+ Effect.map((exit) =>
378
+ local.suspended && Exit.isFailure(exit) && Cause.hasInterruptsOnly(exit.cause)
379
+ ? Option.none()
380
+ : Option.some(exit)
381
+ ),
382
+ // The waits are over once the body has a result. Otherwise every branch
383
+ // parked, or an enclosing race interrupted the body, and the waits move
384
+ // to the enclosing owner, if any, to be released when that race settles.
385
+ Effect.onExit((result) =>
386
+ Effect.sync(() => {
387
+ if (Exit.isSuccess(result) && Option.isSome(result.value)) {
388
+ releaseAwaits(instance, owned)
389
+ } else {
390
+ awaitOwners.get(instance)?.push(...owned)
391
+ }
392
+ })
393
+ )
394
+ )
357
395
  }),
358
396
  // Every branch of `effect` parked: release the hold and let the enclosing
359
397
  // activities finish before suspending. External preemption does not wait.
@@ -12,6 +12,7 @@
12
12
  */
13
13
  import * as Cause from "../Cause.ts"
14
14
  import * as Context from "../Context.ts"
15
+ import * as Deferred from "../Deferred.ts"
15
16
  import * as Effect from "../Effect.ts"
16
17
  import * as Exit from "../Exit.ts"
17
18
  import * as Fiber from "../Fiber.ts"
@@ -75,6 +76,14 @@ export class WorkflowEngine extends Context.Service<
75
76
 
76
77
  /**
77
78
  * Execute a registered workflow.
79
+ *
80
+ * **Details**
81
+ *
82
+ * Unless `discard` is set, a caller outside any workflow waits for the
83
+ * result. While the execution is suspended, the caller retries on
84
+ * `suspendedRetrySchedule`; engines that observe the final result wake it
85
+ * sooner. `ClusterWorkflowEngine` does so only for callers on the runner
86
+ * that owns the execution.
78
87
  */
79
88
  readonly execute: <
80
89
  Name extends string,
@@ -271,8 +280,11 @@ export class WorkflowInstance extends Context.Service<
271
280
  */
272
281
  cause: Cause.Cause<never> | undefined
273
282
 
274
- /** Deferred names this run parked on; their completions preempt the run. */
275
- readonly awaitedDeferreds: Set<string>
283
+ /**
284
+ * Deferred names this run waits on, counted per live wait; their
285
+ * completions preempt the run.
286
+ */
287
+ readonly awaitedDeferreds: Map<string, number>
276
288
 
277
289
  /** Deferred names this run completed itself; their completions need not wake it. */
278
290
  readonly completedDeferreds: Set<string>
@@ -296,7 +308,7 @@ export class WorkflowInstance extends Context.Service<
296
308
  interrupted: false,
297
309
  abandoned: false,
298
310
  cause: undefined,
299
- awaitedDeferreds: new Set(),
311
+ awaitedDeferreds: new Map(),
300
312
  completedDeferreds: new Set(),
301
313
  activityState: {
302
314
  count: 0,
@@ -414,6 +426,22 @@ export interface Encoded {
414
426
  workflow: Workflow.Any,
415
427
  executionId: string
416
428
  ) => Effect.Effect<void>
429
+
430
+ /**
431
+ * Waits until a suspended execution has a result other than `Suspended`.
432
+ *
433
+ * **Gotchas**
434
+ *
435
+ * External callers race this with their `suspendedRetrySchedule`, so it may
436
+ * never complete when the engine cannot observe the result. Completing while
437
+ * the execution is still suspended skips the sleep and spends a step of that
438
+ * schedule. A hook that keeps completing early makes the caller spin.
439
+ */
440
+ readonly awaitComplete?: (
441
+ workflow: Workflow.Any,
442
+ executionId: string
443
+ ) => Effect.Effect<void>
444
+
417
445
  readonly activityExecute: (
418
446
  activity: Activity.Any,
419
447
  attempt: number
@@ -548,7 +576,10 @@ export const makeUnsafe = (options: Encoded): WorkflowEngine["Service"] =>
548
576
  )
549
577
  )
550
578
  )
551
- yield* sleep
579
+ // Completion wakes the caller early; the schedule is the fallback.
580
+ yield* options.awaitComplete
581
+ ? Effect.raceFirst(sleep, options.awaitComplete(self, executionId))
582
+ : sleep
552
583
  }
553
584
  }),
554
585
  poll: options.poll,
@@ -685,6 +716,8 @@ export const layerMemory: Layer.Layer<WorkflowEngine> = Layer.effect(WorkflowEng
685
716
  interrupted: boolean
686
717
  resumeRequested: boolean
687
718
  fiber: Fiber.Fiber<Workflow.Result<unknown, unknown>> | undefined
719
+ // Completed once a run succeeds with `Complete`, after which it cannot resume.
720
+ readonly done: Deferred.Deferred<void>
688
721
  }
689
722
  const executions = new Map<string, ExecutionState>()
690
723
 
@@ -742,6 +775,11 @@ export const layerMemory: Layer.Layer<WorkflowEngine> = Layer.effect(WorkflowEng
742
775
  }
743
776
  return Effect.forkIn(resume(state.parent), scope)
744
777
  }),
778
+ Effect.onExit((exit) => {
779
+ return Exit.isSuccess(exit) && exit.value._tag === "Complete"
780
+ ? Deferred.succeed(state.done, void 0)
781
+ : Effect.void
782
+ }),
745
783
  Effect.forkIn(entry.scope)
746
784
  )
747
785
  })
@@ -773,6 +811,7 @@ export const layerMemory: Layer.Layer<WorkflowEngine> = Layer.effect(WorkflowEng
773
811
  interrupted: false,
774
812
  resumeRequested: false,
775
813
  fiber: undefined,
814
+ done: Deferred.makeUnsafe(),
776
815
  parent: options.parent?.executionId
777
816
  }
778
817
  executions.set(options.executionId, state)
@@ -788,6 +827,11 @@ export const layerMemory: Layer.Layer<WorkflowEngine> = Layer.effect(WorkflowEng
788
827
  }
789
828
  return (yield* exit) as any
790
829
  }),
830
+ awaitComplete: (_workflow, executionId) =>
831
+ Effect.suspend(() => {
832
+ const state = executions.get(executionId)
833
+ return state ? Deferred.await(state.done) : Effect.never
834
+ }),
791
835
  interrupt: Effect.fnUntraced(function*(_workflow, executionId) {
792
836
  const state = executions.get(executionId)
793
837
  if (!state) return