@yolk-sdk/vercel-workflows 0.1.0-canary.77 → 0.1.0-canary.78
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/README.md +19 -3
- package/dist/effect.d.mts.map +1 -1
- package/dist/effect.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -2
- package/dist/testing/index.d.mts +4 -3
- package/dist/testing/index.d.mts.map +1 -1
- package/dist/testing/index.mjs.map +1 -1
- package/dist/workflow-children.d.mts.map +1 -1
- package/dist/workflow-children.mjs +4 -3
- package/dist/workflow-children.mjs.map +1 -1
- package/dist/workflow-events.d.mts +159 -16
- package/dist/workflow-events.d.mts.map +1 -1
- package/dist/workflow-events.mjs +23 -33
- package/dist/workflow-events.mjs.map +1 -1
- package/dist/workflow-loop.d.mts +329 -36
- package/dist/workflow-loop.d.mts.map +1 -1
- package/dist/workflow-loop.mjs +28 -39
- package/dist/workflow-loop.mjs.map +1 -1
- package/dist/workflow.d.mts +1 -1
- package/dist/workflow.mjs +2 -2
- package/package.json +4 -4
- package/src/effect.ts +4 -1
- package/src/index.ts +4 -2
- package/src/testing/index.ts +29 -8
- package/src/workflow-children.ts +20 -3
- package/src/workflow-events.ts +45 -51
- package/src/workflow-loop.ts +81 -79
- package/src/workflow.ts +8 -4
package/README.md
CHANGED
|
@@ -5,11 +5,11 @@ Vercel Workflow agent-loop contracts, durable stream helpers, and an Effect-nati
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pnpm add @yolk-sdk/vercel-workflows@canary effect@4.0.0-
|
|
8
|
+
pnpm add @yolk-sdk/vercel-workflows@canary effect@4.0.0-rc.115 workflow@^5.0.0-beta.42
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
Canary APIs are unstable. Keep all `@yolk-sdk/*` packages on the same version.
|
|
12
|
-
Use the SDK's matching Effect version (`4.0.0-
|
|
12
|
+
Use the SDK's matching Effect version (`4.0.0-rc.115`) in host code.
|
|
13
13
|
Requires Node.js 22+ and a server runtime supported by Vercel Workflow.
|
|
14
14
|
|
|
15
15
|
Requires `workflow@^5.0.0-beta.42`. The 5.x line region-pins Vercel runs so durable streams are
|
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
makeDurableAgentEventSequencerState,
|
|
37
37
|
noWorkflowStepRetry,
|
|
38
38
|
runVercelAgentWorkflow,
|
|
39
|
+
VercelAgentWorkflowRunResult,
|
|
40
|
+
WorkflowStepResult,
|
|
39
41
|
writeDurableAgentEvent
|
|
40
42
|
} from '@yolk-sdk/vercel-workflows'
|
|
41
43
|
```
|
|
@@ -103,6 +105,11 @@ and `'use step'` directive files with `@workflow/vitest` in addition to using th
|
|
|
103
105
|
- close step: flush/close output stream after a successful final model step
|
|
104
106
|
- terminal status: completed, step failure, await-input failure, close failure, or max turns exceeded
|
|
105
107
|
|
|
108
|
+
`WorkflowStepResult` and `VercelAgentWorkflowRunResult` are `Data.taggedEnum` **values** (plain objects,
|
|
109
|
+
`_tag` last), re-exported from the package root and `./workflow`. `settleWorkflowStep` returns
|
|
110
|
+
`WorkflowStepResult.Success({ value })` / `.Failure({ error })`. Inspect run status with constructors
|
|
111
|
+
or `VercelAgentWorkflowRunResult.$is('Completed')` rather than handwritten `{ _tag }` objects.
|
|
112
|
+
|
|
106
113
|
Continuation state stays plain serializable data for Workflow persistence.
|
|
107
114
|
|
|
108
115
|
## Minimal workflow wrapper
|
|
@@ -173,7 +180,10 @@ loop will:
|
|
|
173
180
|
|
|
174
181
|
1. pass that payload to `awaitInput`
|
|
175
182
|
2. wait for the hook/webhook response
|
|
176
|
-
3. rerun the same tool batch with `hitlResponses
|
|
183
|
+
3. rerun the same tool batch with the accumulated responses in a fresh `hitlResponses` array
|
|
184
|
+
|
|
185
|
+
The loop owns its accumulator: host mutation of a supplied array cannot affect a later batch.
|
|
186
|
+
Response order and element identity are preserved; response objects are not deep-cloned.
|
|
177
187
|
|
|
178
188
|
The host owns `hookToken` construction, auth, resume routes, and response decoding. Use stable
|
|
179
189
|
tokens that the resume side can reconstruct or store.
|
|
@@ -265,6 +275,12 @@ the host-provided terminal error event instead. It can protect either a final ev
|
|
|
265
275
|
`AgentAwaitingInput` persistence barrier; it never decides closure from `_tag` and never closes
|
|
266
276
|
writers.
|
|
267
277
|
|
|
278
|
+
`writeCommitError` receives the existing `CommitError` generic from `commit`
|
|
279
|
+
(`Effect.Effect<void, CommitError>`), via `Effect.matchEffect` `onFailure`. Typed failures—not
|
|
280
|
+
defects or interruptions—are passed to that callback. Result `commitError` and `error` fields stay
|
|
281
|
+
`unknown`.
|
|
282
|
+
Existing `(error: unknown) => …` callbacks remain assignable.
|
|
283
|
+
|
|
268
284
|
`runVercelAgentWorkflow` calls `closeStream` only after `runModelStep` returns `done: true`. On model,
|
|
269
285
|
tool, await-input, or max-turn failure it calls `writeError` best-effort and returns without calling
|
|
270
286
|
`closeStream`; `writeError` must write a safe run-final error and attempt to close that failure path.
|
package/dist/effect.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect.d.mts","names":[],"sources":["../src/effect.ts"],"mappings":";;;;KAUY,wBAAA;AAAA,cAQK,yBAAA;;;cAEJ,oBAAA,SAA6B,yBAAA;EAAA,SAC/B,SAAA,EAAW,wBAAA;EAAA,SACX,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGC,sBAAA,yCACP,IAAA,EAAM,KAAA,KACN,OAAA,CAAQ,OAAA;AAAA,KAED,6BAAA,GAAgC,6BAA6B;AAAA,
|
|
1
|
+
{"version":3,"file":"effect.d.mts","names":[],"sources":["../src/effect.ts"],"mappings":";;;;KAUY,wBAAA;AAAA,cAQK,yBAAA;;;cAEJ,oBAAA,SAA6B,yBAAA;EAAA,SAC/B,SAAA,EAAW,wBAAA;EAAA,SACX,OAAA;EAAA,SACA,KAAA;AAAA;AAAA,KAGC,sBAAA,yCACP,IAAA,EAAM,KAAA,KACN,OAAA,CAAQ,OAAA;AAAA,KAED,6BAAA,GAAgC,6BAA6B;AAAA,KAE7D,4BAAA,UAAsC,sBAAsB,CAAC,KAAA;AAAA,KAE7D,uBAAA,GAA0B,WAAW;AAAA,KAErC,qBAAA;EAAA,SACD,KAAA;EAAA,SACA,WAAA,UACP,OAAA,GAAU,6BAAA,KACP,4BAAA,CAA6B,KAAA;EAAA,SACzB,MAAA,QAAc,OAAA;EAAA,SACd,MAAA,EAAQ,OAAA,CAAQ,uBAAA;EAAA,SAChB,WAAA,EAAa,OAAA,CAAQ,OAAA;AAAA;AAAA,KAGpB,wBAAA;EAAA,SACD,KAAA,qCACP,QAAA,EAAU,sBAAA,CAAuB,KAAA,EAAO,OAAA,GACxC,IAAA,EAAM,KAAA,KACH,OAAA,CAAQ,qBAAA,CAAsB,OAAA;EAAA,SAC1B,MAAA,YAAkB,KAAA,aAAkB,qBAAA,CAAsB,OAAA;EAAA,SAC1D,UAAA,GAAa,KAAA,UAAe,OAAA,cAAqB,OAAA;AAAA;AAAA,KAGhD,iBAAA;EAAA,SACD,KAAA;EAAA,SACA,WAAA,UACP,OAAA,GAAU,6BAAA,KACP,MAAA,CAAO,MAAA,CAAO,4BAAA,CAA6B,KAAA,GAAQ,oBAAA;EAAA,SAC/C,MAAA,EAAQ,MAAA,CAAO,MAAA,OAAa,oBAAA;EAAA,SAC5B,MAAA,EAAQ,MAAA,CAAO,MAAA,CAAO,uBAAA,EAAyB,oBAAA;EAAA,SAC/C,WAAA,EAAa,MAAA,CAAO,MAAA,CAAO,OAAA,EAAS,oBAAA;AAAA;AAAA,KAGnC,qBAAA;EAAA,SACD,KAAA,qCACP,QAAA,EAAU,sBAAA,CAAuB,KAAA,EAAO,OAAA,GACxC,IAAA,EAAM,KAAA,KACH,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,OAAA,GAAU,oBAAA;EAAA,SACtC,MAAA,YACP,KAAA,aACG,MAAA,CAAO,MAAA,CAAO,iBAAA,CAAkB,OAAA,GAAU,oBAAA;EAAA,SACtC,WAAA,UACP,KAAA,UACA,OAAA,GAAU,6BAAA,KACP,MAAA,CAAO,MAAA,CAAO,4BAAA,CAA6B,KAAA,GAAQ,oBAAA;EAAA,SAC/C,SAAA,GAAY,KAAA,aAAkB,MAAA,CAAO,MAAA,SAAe,oBAAA;EAAA,SACpD,UAAA,GACP,KAAA,UACA,OAAA,cACG,MAAA,CAAO,MAAA,OAAa,oBAAA;EAAA,SAChB,MAAA,GAAS,KAAA,aAAkB,MAAA,CAAO,MAAA,OAAa,oBAAA;AAAA;AAAA,cACzD,uBAAA;cAEY,kBAAA,SAA2B,uBAGY;AAAA,cAAG,oBAAA;;;cAkE1C,eAAA,SAAwB,oBAAA;EAAA,OA2E5B,KAAA,EAAK,KAAA,CAAA,KAAA,CAAA,eAAA;EAAA,OACL,YAAA,EAAY,KAAA,CAAA,KAAA,CAAA,eAAA,SAAA,kBAAA;AAAA"}
|
package/dist/effect.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"effect.mjs","names":["workflowStart","workflowGetRun","workflowResumeHook"],"sources":["../src/effect.ts"],"sourcesContent":["import { Context, Data, Effect, Layer } from 'effect'\nimport {\n getRun as workflowGetRun,\n resumeHook as workflowResumeHook,\n start as workflowStart,\n type WorkflowReadableStream,\n type WorkflowReadableStreamOptions,\n type WorkflowRun\n} from 'workflow/api'\n\nexport type VercelWorkflowsOperation =\n | 'start'\n | 'getRun'\n | 'getReadable'\n | 'tailIndex'\n | 'resumeHook'\n | 'cancel'\n | 'status'\n | 'returnValue'\n\nexport class VercelWorkflowsError extends Data.TaggedError('VercelWorkflowsError')<{\n readonly operation: VercelWorkflowsOperation\n readonly message: string\n readonly cause: unknown\n}> {}\n\nexport type VercelWorkflowFunction<TArgs extends unknown[], TResult> = (\n ...args: TArgs\n) => Promise<TResult>\n\nexport type VercelWorkflowReadableOptions = WorkflowReadableStreamOptions\nexport type VercelWorkflowReadableStream<Chunk> = WorkflowReadableStream<Chunk>\nexport type VercelWorkflowRunStatus = WorkflowRun['status']\n\nexport type VercelWorkflowsSdkRun<TResult> = {\n readonly runId: string\n readonly getReadable: <Chunk>(\n options?: VercelWorkflowReadableOptions\n ) => VercelWorkflowReadableStream<Chunk>\n readonly cancel: () => Promise<void>\n readonly status: Promise<VercelWorkflowRunStatus>\n readonly returnValue: Promise<TResult>\n}\n\nexport type VercelWorkflowsSdkClient = {\n readonly start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => Promise<VercelWorkflowsSdkRun<TResult>>\n readonly getRun: <TResult>(runId: string) => VercelWorkflowsSdkRun<TResult>\n readonly resumeHook: (token: string, payload: unknown) => Promise<void>\n}\n\nexport type VercelWorkflowRun<TResult> = {\n readonly runId: string\n readonly getReadable: <Chunk>(\n options?: VercelWorkflowReadableOptions\n ) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>\n readonly cancel: Effect.Effect<void, VercelWorkflowsError>\n readonly status: Effect.Effect<VercelWorkflowRunStatus, VercelWorkflowsError>\n readonly returnValue: Effect.Effect<TResult, VercelWorkflowsError>\n}\n\nexport type VercelWorkflowsClient = {\n readonly start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>\n readonly getRun: <TResult>(\n runId: string\n ) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>\n readonly getReadable: <Chunk>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>\n readonly tailIndex: (runId: string) => Effect.Effect<number, VercelWorkflowsError>\n readonly resumeHook: (\n token: string,\n payload: unknown\n ) => Effect.Effect<void, VercelWorkflowsError>\n readonly cancel: (runId: string) => Effect.Effect<void, VercelWorkflowsError>\n}\n\nexport class VercelWorkflowsSdk extends Context.Service<\n VercelWorkflowsSdk,\n VercelWorkflowsSdkClient\n>()('@yolk-sdk/vercel-workflows/VercelWorkflowsSdk') {}\n\nconst workflowsError = (operation: VercelWorkflowsOperation, cause: unknown) =>\n new VercelWorkflowsError({\n operation,\n message: `Vercel Workflow ${operation} failed`,\n cause\n })\n\nconst spanAttributes = (runId: string) => ({ 'workflow.run.id': runId })\n\nconst trySdkPromise = <A>(body: () => Promise<A>) =>\n Effect.tryPromise({\n try: body,\n catch: error => error\n })\n\nconst mapSdkError = (operation: VercelWorkflowsOperation) =>\n <A>(effect: Effect.Effect<A, unknown>) =>\n effect.pipe(Effect.mapError(cause => workflowsError(operation, cause)))\n\nconst makeWorkflowRun = <TResult>(\n run: VercelWorkflowsSdkRun<TResult>\n): VercelWorkflowRun<TResult> => ({\n runId: run.runId,\n getReadable: <Chunk>(options?: VercelWorkflowReadableOptions) =>\n Effect.try({\n try: () => run.getReadable<Chunk>(options),\n catch: cause => workflowsError('getReadable', cause)\n }).pipe(\n Effect.withSpan('VercelWorkflows.run.getReadable', {\n attributes: spanAttributes(run.runId)\n })\n ),\n cancel: trySdkPromise(() => run.cancel()).pipe(\n mapSdkError('cancel'),\n Effect.withSpan('VercelWorkflows.run.cancel', {\n attributes: spanAttributes(run.runId)\n })\n ),\n status: trySdkPromise(() => run.status).pipe(\n mapSdkError('status'),\n Effect.withSpan('VercelWorkflows.run.status', {\n attributes: spanAttributes(run.runId)\n })\n ),\n returnValue: trySdkPromise(() => run.returnValue).pipe(\n mapSdkError('returnValue'),\n Effect.withSpan('VercelWorkflows.run.returnValue', {\n attributes: spanAttributes(run.runId)\n })\n )\n})\n\nconst VercelWorkflowsSdkLive = Layer.succeed(VercelWorkflowsSdk, {\n start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => workflowStart(workflow, args),\n getRun: <TResult>(runId: string) => workflowGetRun<TResult>(runId),\n resumeHook: async (token: string, payload: unknown) => {\n await workflowResumeHook<unknown>(token, payload)\n }\n})\n\nexport class VercelWorkflows extends Context.Service<VercelWorkflows, VercelWorkflowsClient>()(\n '@yolk-sdk/vercel-workflows/VercelWorkflows',\n {\n make: Effect.gen(function* () {\n const sdk = yield* VercelWorkflowsSdk\n\n const getRun = <TResult>(\n runId: string\n ): Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError> =>\n Effect.try({\n try: () => sdk.getRun<TResult>(runId),\n catch: error => error\n }).pipe(\n mapSdkError('getRun'),\n Effect.map(makeWorkflowRun),\n Effect.withSpan('VercelWorkflows.getRun', {\n attributes: spanAttributes(runId)\n })\n )\n\n return {\n start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ): Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError> =>\n trySdkPromise(() => sdk.start(workflow, args)).pipe(\n mapSdkError('start'),\n Effect.map(makeWorkflowRun),\n Effect.withSpan('VercelWorkflows.start')\n ),\n\n getRun,\n\n getReadable: <Chunk>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ): Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.getReadable<Chunk>(options)),\n Effect.withSpan('VercelWorkflows.getReadable', {\n attributes: spanAttributes(runId)\n })\n ),\n\n tailIndex: (runId: string): Effect.Effect<number, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.getReadable<unknown>({ startIndex: -1 })),\n Effect.flatMap(readable =>\n Effect.tryPromise({\n try: () => readable.getTailIndex(),\n catch: cause => workflowsError('tailIndex', cause)\n })\n ),\n Effect.withSpan('VercelWorkflows.tailIndex', {\n attributes: spanAttributes(runId)\n })\n ),\n\n resumeHook: (token: string, payload: unknown): Effect.Effect<void, VercelWorkflowsError> =>\n trySdkPromise(() => sdk.resumeHook(token, payload)).pipe(\n mapSdkError('resumeHook'),\n Effect.withSpan('VercelWorkflows.resumeHook')\n ),\n\n cancel: (runId: string): Effect.Effect<void, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.cancel),\n Effect.withSpan('VercelWorkflows.cancel', {\n attributes: spanAttributes(runId)\n })\n )\n }\n })\n }\n) {\n static layer = Layer.effect(this, this.make).pipe(Layer.provide(VercelWorkflowsSdkLive))\n static layerFromSdk = Layer.effect(this, this.make)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"effect.mjs","names":["workflowStart","workflowGetRun","workflowResumeHook"],"sources":["../src/effect.ts"],"sourcesContent":["import { Context, Data, Effect, Layer } from 'effect'\nimport {\n getRun as workflowGetRun,\n resumeHook as workflowResumeHook,\n start as workflowStart,\n type WorkflowReadableStream,\n type WorkflowReadableStreamOptions,\n type WorkflowRun\n} from 'workflow/api'\n\nexport type VercelWorkflowsOperation =\n | 'start'\n | 'getRun'\n | 'getReadable'\n | 'tailIndex'\n | 'resumeHook'\n | 'cancel'\n | 'status'\n | 'returnValue'\n\nexport class VercelWorkflowsError extends Data.TaggedError('VercelWorkflowsError')<{\n readonly operation: VercelWorkflowsOperation\n readonly message: string\n readonly cause: unknown\n}> {}\n\nexport type VercelWorkflowFunction<TArgs extends unknown[], TResult> = (\n ...args: TArgs\n) => Promise<TResult>\n\nexport type VercelWorkflowReadableOptions = WorkflowReadableStreamOptions\n\nexport type VercelWorkflowReadableStream<Chunk> = WorkflowReadableStream<Chunk>\n\nexport type VercelWorkflowRunStatus = WorkflowRun['status']\n\nexport type VercelWorkflowsSdkRun<TResult> = {\n readonly runId: string\n readonly getReadable: <Chunk>(\n options?: VercelWorkflowReadableOptions\n ) => VercelWorkflowReadableStream<Chunk>\n readonly cancel: () => Promise<void>\n readonly status: Promise<VercelWorkflowRunStatus>\n readonly returnValue: Promise<TResult>\n}\n\nexport type VercelWorkflowsSdkClient = {\n readonly start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => Promise<VercelWorkflowsSdkRun<TResult>>\n readonly getRun: <TResult>(runId: string) => VercelWorkflowsSdkRun<TResult>\n readonly resumeHook: (token: string, payload: unknown) => Promise<void>\n}\n\nexport type VercelWorkflowRun<TResult> = {\n readonly runId: string\n readonly getReadable: <Chunk>(\n options?: VercelWorkflowReadableOptions\n ) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>\n readonly cancel: Effect.Effect<void, VercelWorkflowsError>\n readonly status: Effect.Effect<VercelWorkflowRunStatus, VercelWorkflowsError>\n readonly returnValue: Effect.Effect<TResult, VercelWorkflowsError>\n}\n\nexport type VercelWorkflowsClient = {\n readonly start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>\n readonly getRun: <TResult>(\n runId: string\n ) => Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError>\n readonly getReadable: <Chunk>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ) => Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError>\n readonly tailIndex: (runId: string) => Effect.Effect<number, VercelWorkflowsError>\n readonly resumeHook: (\n token: string,\n payload: unknown\n ) => Effect.Effect<void, VercelWorkflowsError>\n readonly cancel: (runId: string) => Effect.Effect<void, VercelWorkflowsError>\n}\n\nexport class VercelWorkflowsSdk extends Context.Service<\n VercelWorkflowsSdk,\n VercelWorkflowsSdkClient\n>()('@yolk-sdk/vercel-workflows/VercelWorkflowsSdk') {}\n\nconst workflowsError = (operation: VercelWorkflowsOperation, cause: unknown) =>\n new VercelWorkflowsError({\n operation,\n message: `Vercel Workflow ${operation} failed`,\n cause\n })\n\nconst spanAttributes = (runId: string) => ({ 'workflow.run.id': runId })\n\nconst trySdkPromise = <A>(body: () => Promise<A>) =>\n Effect.tryPromise({\n try: body,\n catch: error => error\n })\n\nconst mapSdkError =\n (operation: VercelWorkflowsOperation) =>\n <A>(effect: Effect.Effect<A, unknown>) =>\n effect.pipe(Effect.mapError(cause => workflowsError(operation, cause)))\n\nconst makeWorkflowRun = <TResult>(\n run: VercelWorkflowsSdkRun<TResult>\n): VercelWorkflowRun<TResult> => ({\n runId: run.runId,\n getReadable: <Chunk>(options?: VercelWorkflowReadableOptions) =>\n Effect.try({\n try: () => run.getReadable<Chunk>(options),\n catch: cause => workflowsError('getReadable', cause)\n }).pipe(\n Effect.withSpan('VercelWorkflows.run.getReadable', {\n attributes: spanAttributes(run.runId)\n })\n ),\n cancel: trySdkPromise(() => run.cancel()).pipe(\n mapSdkError('cancel'),\n Effect.withSpan('VercelWorkflows.run.cancel', {\n attributes: spanAttributes(run.runId)\n })\n ),\n status: trySdkPromise(() => run.status).pipe(\n mapSdkError('status'),\n Effect.withSpan('VercelWorkflows.run.status', {\n attributes: spanAttributes(run.runId)\n })\n ),\n returnValue: trySdkPromise(() => run.returnValue).pipe(\n mapSdkError('returnValue'),\n Effect.withSpan('VercelWorkflows.run.returnValue', {\n attributes: spanAttributes(run.runId)\n })\n )\n})\n\nconst VercelWorkflowsSdkLive = Layer.succeed(VercelWorkflowsSdk, {\n start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ) => workflowStart(workflow, args),\n getRun: <TResult>(runId: string) => workflowGetRun<TResult>(runId),\n resumeHook: async (token: string, payload: unknown) => {\n await workflowResumeHook<unknown>(token, payload)\n }\n})\n\nexport class VercelWorkflows extends Context.Service<VercelWorkflows, VercelWorkflowsClient>()(\n '@yolk-sdk/vercel-workflows/VercelWorkflows',\n {\n make: Effect.gen(function* () {\n const sdk = yield* VercelWorkflowsSdk\n\n const getRun = <TResult>(\n runId: string\n ): Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError> =>\n Effect.try({\n try: () => sdk.getRun<TResult>(runId),\n catch: error => error\n }).pipe(\n mapSdkError('getRun'),\n Effect.map(makeWorkflowRun),\n Effect.withSpan('VercelWorkflows.getRun', {\n attributes: spanAttributes(runId)\n })\n )\n\n return {\n start: <TArgs extends unknown[], TResult>(\n workflow: VercelWorkflowFunction<TArgs, TResult>,\n args: TArgs\n ): Effect.Effect<VercelWorkflowRun<TResult>, VercelWorkflowsError> =>\n trySdkPromise(() => sdk.start(workflow, args)).pipe(\n mapSdkError('start'),\n Effect.map(makeWorkflowRun),\n Effect.withSpan('VercelWorkflows.start')\n ),\n\n getRun,\n\n getReadable: <Chunk>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ): Effect.Effect<VercelWorkflowReadableStream<Chunk>, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.getReadable<Chunk>(options)),\n Effect.withSpan('VercelWorkflows.getReadable', {\n attributes: spanAttributes(runId)\n })\n ),\n\n tailIndex: (runId: string): Effect.Effect<number, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.getReadable<unknown>({ startIndex: -1 })),\n Effect.flatMap(readable =>\n Effect.tryPromise({\n try: () => readable.getTailIndex(),\n catch: cause => workflowsError('tailIndex', cause)\n })\n ),\n Effect.withSpan('VercelWorkflows.tailIndex', {\n attributes: spanAttributes(runId)\n })\n ),\n\n resumeHook: (token: string, payload: unknown): Effect.Effect<void, VercelWorkflowsError> =>\n trySdkPromise(() => sdk.resumeHook(token, payload)).pipe(\n mapSdkError('resumeHook'),\n Effect.withSpan('VercelWorkflows.resumeHook')\n ),\n\n cancel: (runId: string): Effect.Effect<void, VercelWorkflowsError> =>\n getRun<unknown>(runId).pipe(\n Effect.flatMap(run => run.cancel),\n Effect.withSpan('VercelWorkflows.cancel', {\n attributes: spanAttributes(runId)\n })\n )\n }\n })\n }\n) {\n static layer = Layer.effect(this, this.make).pipe(Layer.provide(VercelWorkflowsSdkLive))\n static layerFromSdk = Layer.effect(this, this.make)\n}\n"],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import { SerializableWorkflowState, VercelAgentWorkflowAwaitingInput, VercelAgentWorkflowInput, VercelAgentWorkflowLoopConfig, VercelAgentWorkflowModelStepInput, VercelAgentWorkflowModelStepResult, VercelAgentWorkflowRunResult, VercelAgentWorkflowStepRetryPolicy, VercelAgentWorkflowToolBatchStepInput, VercelAgentWorkflowToolBatchStepResult, WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep } from "./workflow-loop.mjs";
|
|
2
2
|
import { CommitThenWriteTerminalEventInput, CommitThenWriteTerminalEventResult, DurableAgentEvent, DurableAgentEventIdInput, DurableAgentEventSequencerState, SequenceDurableAgentEventInput, SequencedDurableAgentEvent, WriteDurableAgentEventInput, commitThenWriteTerminalEvent, durableAgentEventId, makeDurableAgentEventSequencerState, sequenceDurableAgentEvent, writeDurableAgentEvent } from "./workflow-events.mjs";
|
|
3
3
|
import { awaitWorkflowChild, orchestrateWorkflowToolBatch } from "./workflow-children.mjs";
|
|
4
|
-
export { type CommitThenWriteTerminalEventInput, type CommitThenWriteTerminalEventResult, type DurableAgentEvent, type DurableAgentEventIdInput, type DurableAgentEventSequencerState, type SequenceDurableAgentEventInput, type SequencedDurableAgentEvent, type SerializableWorkflowState, type VercelAgentWorkflowAwaitingInput, type VercelAgentWorkflowInput, type VercelAgentWorkflowLoopConfig, type VercelAgentWorkflowModelStepInput, type VercelAgentWorkflowModelStepResult,
|
|
4
|
+
export { type CommitThenWriteTerminalEventInput, type CommitThenWriteTerminalEventResult, type DurableAgentEvent, type DurableAgentEventIdInput, type DurableAgentEventSequencerState, type SequenceDurableAgentEventInput, type SequencedDurableAgentEvent, type SerializableWorkflowState, type VercelAgentWorkflowAwaitingInput, type VercelAgentWorkflowInput, type VercelAgentWorkflowLoopConfig, type VercelAgentWorkflowModelStepInput, type VercelAgentWorkflowModelStepResult, VercelAgentWorkflowRunResult, type VercelAgentWorkflowStepRetryPolicy, type VercelAgentWorkflowToolBatchStepInput, type VercelAgentWorkflowToolBatchStepResult, WorkflowStepResult, type WriteDurableAgentEventInput, awaitWorkflowChild, commitThenWriteTerminalEvent, defaultMaxWorkflowTurns, durableAgentEventId, makeDurableAgentEventSequencerState, noWorkflowStepRetry, orchestrateWorkflowToolBatch, retryWorkflowStep, runVercelAgentWorkflow, sequenceDurableAgentEvent, settleWorkflowStep, writeDurableAgentEvent };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep } from "./workflow-loop.mjs";
|
|
1
|
+
import { VercelAgentWorkflowRunResult, WorkflowStepResult, defaultMaxWorkflowTurns, noWorkflowStepRetry, retryWorkflowStep, runVercelAgentWorkflow, settleWorkflowStep } from "./workflow-loop.mjs";
|
|
2
2
|
import { commitThenWriteTerminalEvent, durableAgentEventId, makeDurableAgentEventSequencerState, sequenceDurableAgentEvent, writeDurableAgentEvent } from "./workflow-events.mjs";
|
|
3
3
|
import { awaitWorkflowChild, orchestrateWorkflowToolBatch } from "./workflow-children.mjs";
|
|
4
|
-
export { awaitWorkflowChild, commitThenWriteTerminalEvent, defaultMaxWorkflowTurns, durableAgentEventId, makeDurableAgentEventSequencerState, noWorkflowStepRetry, orchestrateWorkflowToolBatch, retryWorkflowStep, runVercelAgentWorkflow, sequenceDurableAgentEvent, settleWorkflowStep, writeDurableAgentEvent };
|
|
4
|
+
export { VercelAgentWorkflowRunResult, WorkflowStepResult, awaitWorkflowChild, commitThenWriteTerminalEvent, defaultMaxWorkflowTurns, durableAgentEventId, makeDurableAgentEventSequencerState, noWorkflowStepRetry, orchestrateWorkflowToolBatch, retryWorkflowStep, runVercelAgentWorkflow, sequenceDurableAgentEvent, settleWorkflowStep, writeDurableAgentEvent };
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -53,12 +53,13 @@ type TestWorkflowRunInspection = {
|
|
|
53
53
|
type TestWorkflowHook<T> = Promise<T> & {
|
|
54
54
|
readonly [Symbol.dispose]: () => void;
|
|
55
55
|
};
|
|
56
|
+
type TestWorkflowWorldStartResult = {
|
|
57
|
+
readonly runId: string;
|
|
58
|
+
};
|
|
56
59
|
declare class TestWorkflowWorld {
|
|
57
60
|
private readonly runs;
|
|
58
61
|
private runSequence;
|
|
59
|
-
start<TArgs extends ReadonlyArray<unknown>, TResult>(workflowFn: (...args: TArgs) => Promise<TResult>, args: TArgs):
|
|
60
|
-
readonly runId: string;
|
|
61
|
-
};
|
|
62
|
+
start<TArgs extends ReadonlyArray<unknown>, TResult>(workflowFn: (...args: TArgs) => Promise<TResult>, args: TArgs): TestWorkflowWorldStartResult;
|
|
62
63
|
settled(runId: string): Promise<void>;
|
|
63
64
|
inspect(runId: string): TestWorkflowRunInspection;
|
|
64
65
|
status(runId: string): TestWorkflowRunStatus;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/testing/index.ts"],"mappings":";;;KAsBY,qBAAA,GAAwB,uBAAuB;AAAA,cAK9C,+BAAA,SAAwC,KAAK;EAAA,SACtC,IAAA;EAAA,SACT,MAAA;cAEG,KAAA;AAAA;AAAA,cAKD,4BAAA,SAAqC,KAAK;EAAA,SACnC,IAAA;EAAA,SACT,MAAA;cAEG,OAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,4BAAA,SAAqC,KAAK;EAAA,SACnC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,KAKF,oBAAA;EAAA,SAAkC,aAAa;AAAA;AAAA,KAE/C,wBAAA;EAAA,SACD,OAAA;EAAA,SACA,MAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,wBAAA,eAAuC,aAAA,2BAC9C,IAAA,EAAM,KAAA,KACN,OAAA,CAAQ,OAAA;EAAA,SAAuB,UAAA;AAAA;AAAA,cAGvB,iCAAA;AAAA,KA0CD,yBAAA;EAAA,SACD,KAAA;EAAA,SACA,MAAA,EAAQ,qBAAA;EAAA,SACR,MAAA,EAAQ,aAAA;EAAA,SACR,YAAA;EAAA,SACA,gBAAA;EAAA,SACA,uBAAA;EAAA,SACA,YAAA,EAAc,WAAA;EAAA,SACd,QAAA;AAAA;AAAA,KAGC,gBAAA,MAAsB,OAAA,CAAQ,CAAA;EAAA,UAAiB,MAAA,CAAO,OAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/testing/index.ts"],"mappings":";;;KAsBY,qBAAA,GAAwB,uBAAuB;AAAA,cAK9C,+BAAA,SAAwC,KAAK;EAAA,SACtC,IAAA;EAAA,SACT,MAAA;cAEG,KAAA;AAAA;AAAA,cAKD,4BAAA,SAAqC,KAAK;EAAA,SACnC,IAAA;EAAA,SACT,MAAA;cAEG,OAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,4BAAA,SAAqC,KAAK;EAAA,SACnC,IAAA;cAEN,KAAA;AAAA;AAAA,cAKD,6BAAA,SAAsC,KAAK;EAAA,SACpC,IAAA;cAEN,KAAA;AAAA;AAAA,KAKF,oBAAA;EAAA,SAAkC,aAAa;AAAA;AAAA,KAE/C,wBAAA;EAAA,SACD,OAAA;EAAA,SACA,MAAA;EAAA,SACA,QAAA;AAAA;AAAA,KAGC,wBAAA,eAAuC,aAAA,2BAC9C,IAAA,EAAM,KAAA,KACN,OAAA,CAAQ,OAAA;EAAA,SAAuB,UAAA;AAAA;AAAA,cAGvB,iCAAA;AAAA,KA0CD,yBAAA;EAAA,SACD,KAAA;EAAA,SACA,MAAA,EAAQ,qBAAA;EAAA,SACR,MAAA,EAAQ,aAAA;EAAA,SACR,YAAA;EAAA,SACA,gBAAA;EAAA,SACA,uBAAA;EAAA,SACA,YAAA,EAAc,WAAA;EAAA,SACd,QAAA;AAAA;AAAA,KAGC,gBAAA,MAAsB,OAAA,CAAQ,CAAA;EAAA,UAAiB,MAAA,CAAO,OAAA;AAAA;AAAA,KAE7D,4BAAA;EAAA,SAA0C,KAAK;AAAA;AAAA,cAEvC,iBAAA;EAAA,iBACM,IAAA;EAAA,QACT,WAAA;EAER,KAAA,eAAoB,aAAA,mBAAA,CAClB,UAAA,MAAgB,IAAA,EAAM,KAAA,KAAU,OAAA,CAAQ,OAAA,GACxC,IAAA,EAAM,KAAA,GACL,4BAAA;EAmDG,OAAA,CAAQ,KAAA,WAAgB,OAAA;EAI9B,OAAA,CAAQ,KAAA,WAAgB,yBAAA;EAexB,MAAA,CAAO,KAAA,WAAgB,qBAAA;EAIjB,MAAA,CAAO,KAAA,WAAgB,OAAA;EAmBvB,UAAA,CAAW,KAAA,UAAe,OAAA,YAAmB,OAAA;EAoBnD,cAAA,CAAe,KAAA;EAaT,OAAA,eAAsB,aAAA,mBAAA,CAC1B,MAAA,EAAQ,wBAAA,CAAyB,KAAA,EAAO,OAAA,GACxC,IAAA,EAAM,KAAA,EACN,QAAA,YACC,OAAA,CAAQ,OAAA;EA2BX,IAAA,eAAmB,aAAA,mBAAA,CACjB,MAAA,EAAQ,wBAAA,CAAyB,KAAA,EAAO,OAAA,GACxC,QAAA,gBACK,IAAA,EAAM,KAAA,KAAU,OAAA,CAAQ,OAAA;EAAA,OAMxB,mBAAA,CAAA,GAAuB,oBAAA;EAAA,OAIvB,eAAA,CAAA,GAAmB,wBAAA;EAAA,OAUnB,WAAA,GAAA,CAAA,GAAkB,cAAA,CAAe,CAAA;EAAA,OAMjC,UAAA,GAAA,CAAc,KAAA;IAAA,SAAkB,KAAA;EAAA,IAAkB,gBAAA,CAAiB,CAAA;EAAA,QAQlE,YAAA;EAAA,QAwBA,QAAA;EAqBR,WAAA,GAAA,CACE,KAAA,UACA,OAAA,GAAU,6BAAA,GACT,4BAAA,CAA6B,CAAA;EAAA,QAiCxB,WAAA;EAAA,QAIA,YAAA;EAAA,QAOA,UAAA;EAAA,QAMA,UAAA;EAAA,QAUA,MAAA;EAAA,SA2BC,GAAA,EAAK,wBAAA;AAAA;AAAA,cAcH,kBAAA;;;wBAGI,cAAA,CAAA,CAAA;kBACD,KAAA;IAAA,SAAoB,KAAA;EAAA,MAAe,gBAAA,CAAA,CAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/testing/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport type {\n VercelWorkflowsSdkClient,\n VercelWorkflowsSdkRun,\n VercelWorkflowReadableOptions,\n VercelWorkflowReadableStream,\n VercelWorkflowRunStatus\n} from '../effect.ts'\n\n// Behavioral emulator of the Vercel Workflow platform for tests: run lifecycle,\n// append-only durable streams with close-once -> 409 write conflicts, a step\n// executor honoring `fn.maxRetries` + `getStepMetadata().attempt`, hooks/resume,\n// and cancellation. Host code under test runs unmodified against it; tests mock\n// only the `workflow` module's ambient APIs, delegating to the active world\n// (see `testWorkflowModule`).\n//\n// The single most important semantic: writing to (or re-closing) a closed\n// durable stream fails with the platform's HTTP 409 \"already completed\"\n// conflict shape. Any code path that performs a terminal stream effect from a\n// retryable step attempt poisons every subsequent platform retry — exactly the\n// #252 production bug. The emulator enforces that invariant permanently.\n\nexport type TestWorkflowRunStatus = VercelWorkflowRunStatus\n\n// Matches the platform's conflict contract: `EntityConflictError.is(value)`\n// checks `value.name === 'EntityConflictError'`, and the Vercel world maps\n// HTTP 409 responses to that class.\nexport class TestWorkflowStreamConflictError extends Error {\n override readonly name = 'EntityConflictError'\n readonly status = 409\n\n constructor(runId: string) {\n super(`Workflow run \"${runId}\" stream already completed`)\n }\n}\n\nexport class TestWorkflowRunConflictError extends Error {\n override readonly name = 'EntityConflictError'\n readonly status = 409\n\n constructor(message: string) {\n super(message)\n }\n}\n\nexport class TestWorkflowHookNotFoundError extends Error {\n override readonly name = 'HookNotFoundError'\n\n constructor(token: string) {\n super(`Workflow hook \"${token}\" was not found`)\n }\n}\n\nexport class TestWorkflowHookConflictError extends Error {\n override readonly name = 'HookConflictError'\n\n constructor(token: string) {\n super(`Workflow hook \"${token}\" was already resumed`)\n }\n}\n\nexport class TestWorkflowRunNotFoundError extends Error {\n override readonly name = 'WorkflowRunNotFoundError'\n\n constructor(runId: string) {\n super(`Workflow run \"${runId}\" was not found`)\n }\n}\n\nexport class TestWorkflowRunCancelledError extends Error {\n override readonly name = 'WorkflowRunCancelledError'\n\n constructor(runId: string) {\n super(`Workflow run \"${runId}\" was cancelled`)\n }\n}\n\nexport type TestWorkflowMetadata = { readonly workflowRunId: string }\n\nexport type TestWorkflowStepMetadata = {\n readonly attempt: number\n readonly stepId: string\n readonly stepName: string\n}\n\nexport type TestWorkflowStepFunction<TArgs extends ReadonlyArray<unknown>, TResult> = ((\n ...args: TArgs\n) => Promise<TResult>) & { readonly maxRetries?: number }\n\n// The platform default: retries after the first attempt; total attempts = maxRetries + 1.\nexport const defaultTestWorkflowStepMaxRetries = 3\n\ntype HookRecord = {\n readonly resolve: (payload: unknown) => void\n readonly reject: (error: unknown) => void\n}\n\ntype TestWorkflowRunRecord = {\n readonly runId: string\n status: TestWorkflowRunStatus\n returnValue: unknown\n runError: unknown\n settled: Promise<void>\n readonly chunks: Array<unknown>\n streamClosed: boolean\n streamCloseCount: number\n writeAfterCloseAttempts: number\n readonly changeListeners: Set<() => void>\n readonly hooks: Map<string, HookRecord>\n readonly resumedHookTokens: Set<string>\n stepSequence: number\n readonly stepAttempts: Map<string, number>\n}\n\ntype AmbientContext = {\n readonly world: TestWorkflowWorld\n readonly run: TestWorkflowRunRecord\n readonly stepMetadata: TestWorkflowStepMetadata | undefined\n}\n\nconst ambientStorage = new AsyncLocalStorage<AmbientContext>()\n\nconst requireAmbient = (): AmbientContext => {\n const ambient = ambientStorage.getStore()\n\n if (ambient === undefined) {\n throw new Error('Workflow ambient API called outside a TestWorkflowWorld run context')\n }\n\n return ambient\n}\n\nexport type TestWorkflowRunInspection = {\n readonly runId: string\n readonly status: TestWorkflowRunStatus\n readonly chunks: ReadonlyArray<unknown>\n readonly streamClosed: boolean\n readonly streamCloseCount: number\n readonly writeAfterCloseAttempts: number\n readonly stepAttempts: ReadonlyMap<string, number>\n readonly runError: unknown\n}\n\nexport type TestWorkflowHook<T> = Promise<T> & { readonly [Symbol.dispose]: () => void }\n\nexport class TestWorkflowWorld {\n private readonly runs = new Map<string, TestWorkflowRunRecord>()\n private runSequence = 0\n\n start<TArgs extends ReadonlyArray<unknown>, TResult>(\n workflowFn: (...args: TArgs) => Promise<TResult>,\n args: TArgs\n ): { readonly runId: string } {\n this.runSequence += 1\n const runId = `twr_${this.runSequence}`\n const run: TestWorkflowRunRecord = {\n runId,\n status: 'pending',\n returnValue: undefined,\n runError: undefined,\n settled: Promise.resolve(),\n chunks: [],\n streamClosed: false,\n streamCloseCount: 0,\n writeAfterCloseAttempts: 0,\n changeListeners: new Set(),\n hooks: new Map(),\n resumedHookTokens: new Set(),\n stepSequence: 0,\n stepAttempts: new Map()\n }\n this.runs.set(runId, run)\n\n run.settled = ambientStorage.run(\n { world: this, run, stepMetadata: undefined },\n async (): Promise<void> => {\n // Yield once so callers observe `pending` before execution begins,\n // mirroring the platform's enqueue-then-run lifecycle.\n await Promise.resolve()\n if (run.status === 'cancelled') return\n\n run.status = 'running'\n try {\n run.returnValue = await workflowFn(...args)\n if (run.status === 'running') run.status = 'completed'\n } catch (error) {\n run.runError = error\n if (run.status === 'running') run.status = 'failed'\n } finally {\n this.notifyChange(run)\n }\n }\n )\n\n return { runId }\n }\n\n async settled(runId: string): Promise<void> {\n await this.requireRun(runId).settled\n }\n\n inspect(runId: string): TestWorkflowRunInspection {\n const run = this.requireRun(runId)\n\n return {\n runId: run.runId,\n status: run.status,\n chunks: [...run.chunks],\n streamClosed: run.streamClosed,\n streamCloseCount: run.streamCloseCount,\n writeAfterCloseAttempts: run.writeAfterCloseAttempts,\n stepAttempts: new Map(run.stepAttempts),\n runError: run.runError\n }\n }\n\n status(runId: string): TestWorkflowRunStatus {\n return this.requireRun(runId).status\n }\n\n async cancel(runId: string): Promise<void> {\n const run = this.requireRun(runId)\n\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'cancelled') {\n throw new TestWorkflowRunConflictError(\n `Workflow run \"${runId}\" is already ${run.status} and cannot be cancelled`\n )\n }\n\n run.status = 'cancelled'\n for (const [token, hook] of run.hooks) {\n run.hooks.delete(token)\n hook.reject(new TestWorkflowRunCancelledError(runId))\n }\n this.notifyChange(run)\n }\n\n async resumeHook(token: string, payload: unknown): Promise<void> {\n for (const run of this.runs.values()) {\n const hook = run.hooks.get(token)\n if (hook !== undefined) {\n run.hooks.delete(token)\n run.resumedHookTokens.add(token)\n hook.resolve(payload)\n return\n }\n\n if (run.resumedHookTokens.has(token)) {\n throw new TestWorkflowHookConflictError(token)\n }\n }\n\n throw new TestWorkflowHookNotFoundError(token)\n }\n\n hasResumedHook(token: string): boolean {\n for (const run of this.runs.values()) {\n if (run.resumedHookTokens.has(token)) return true\n }\n\n return false\n }\n\n // --- Step executor (platform retry semantics) ---\n\n // Runs a step body with the platform's retry contract: `fn.maxRetries`\n // retries after the first attempt (total attempts = maxRetries + 1) and\n // 1-based `getStepMetadata().attempt` visible to the body.\n async runStep<TArgs extends ReadonlyArray<unknown>, TResult>(\n stepFn: TestWorkflowStepFunction<TArgs, TResult>,\n args: TArgs,\n stepName = stepFn.name === '' ? 'step' : stepFn.name\n ): Promise<TResult> {\n const ambient = requireAmbient()\n const run = ambient.run\n run.stepSequence += 1\n const stepId = `${run.runId}:step:${run.stepSequence}:${stepName}`\n const maxRetries = stepFn.maxRetries ?? defaultTestWorkflowStepMaxRetries\n const maxAttempts = maxRetries + 1\n\n let attempt = 1\n\n for (;;) {\n run.stepAttempts.set(stepName, (run.stepAttempts.get(stepName) ?? 0) + 1)\n try {\n return await ambientStorage.run(\n { world: this, run, stepMetadata: { attempt, stepId, stepName } },\n () => stepFn(...args)\n )\n } catch (error) {\n if (attempt >= maxAttempts) throw error\n attempt += 1\n }\n }\n }\n\n // Wraps a step function so orchestration code can invoke it exactly the way\n // the platform invokes `use step` functions: through the retry executor.\n step<TArgs extends ReadonlyArray<unknown>, TResult>(\n stepFn: TestWorkflowStepFunction<TArgs, TResult>,\n stepName?: string\n ): (...args: TArgs) => Promise<TResult> {\n return (...args: TArgs) => this.runStep(stepFn, args, stepName)\n }\n\n // --- Ambient platform APIs (exposed through `testWorkflowModule`) ---\n\n static getWorkflowMetadata(): TestWorkflowMetadata {\n return { workflowRunId: requireAmbient().run.runId }\n }\n\n static getStepMetadata(): TestWorkflowStepMetadata {\n const ambient = requireAmbient()\n\n if (ambient.stepMetadata === undefined) {\n throw new Error('getStepMetadata called outside a step context')\n }\n\n return ambient.stepMetadata\n }\n\n static getWritable<T>(): WritableStream<T> {\n const ambient = requireAmbient()\n\n return ambient.world.makeWritable(ambient.run)\n }\n\n static createHook<T>(input: { readonly token: string }): TestWorkflowHook<T> {\n const ambient = requireAmbient()\n\n return ambient.world.makeHook<T>(ambient.run, input.token)\n }\n\n // --- Durable streams ---\n\n private makeWritable<T>(run: TestWorkflowRunRecord): WritableStream<T> {\n return new WritableStream<T>({\n write: chunk => {\n if (run.streamClosed || run.status === 'cancelled') {\n run.writeAfterCloseAttempts += 1\n throw new TestWorkflowStreamConflictError(run.runId)\n }\n\n run.chunks.push(chunk)\n this.notifyChange(run)\n },\n close: () => {\n if (run.streamClosed) {\n run.streamCloseCount += 1\n throw new TestWorkflowStreamConflictError(run.runId)\n }\n\n run.streamClosed = true\n run.streamCloseCount += 1\n this.notifyChange(run)\n }\n })\n }\n\n private makeHook<T>(run: TestWorkflowRunRecord, token: string): TestWorkflowHook<T> {\n let resolveHook: (payload: unknown) => void = () => {}\n let rejectHook: (error: unknown) => void = () => {}\n const promise = new Promise<T>((resolve, reject) => {\n // Hook payloads are caller-typed on the platform (`createHook<T>`).\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n resolveHook = payload => resolve(payload as T)\n rejectHook = reject\n })\n\n run.hooks.set(token, { resolve: resolveHook, reject: rejectHook })\n\n return Object.assign(promise, {\n [Symbol.dispose]: () => {\n run.hooks.delete(token)\n }\n })\n }\n\n getReadable<T>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ): VercelWorkflowReadableStream<T> {\n const run = this.requireRun(runId)\n const requestedStart = options?.startIndex ?? 0\n let index =\n requestedStart < 0 ? Math.max(0, run.chunks.length + requestedStart) : requestedStart\n\n const stream = new ReadableStream<T>({\n pull: async controller => {\n while (index >= run.chunks.length && !this.streamEnded(run)) {\n await this.nextChange(run)\n }\n\n if (index < run.chunks.length) {\n // The platform readable is caller-typed (`WorkflowReadableStream<R = any>`);\n // the durable log stores opaque chunks, so this coercion mirrors the\n // platform contract exactly.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n controller.enqueue(run.chunks[index] as T)\n index += 1\n return\n }\n\n controller.close()\n }\n })\n\n return Object.assign(stream, {\n getTailIndex: () => Promise.resolve(run.chunks.length - 1)\n })\n }\n\n private streamEnded(run: TestWorkflowRunRecord): boolean {\n return run.streamClosed || run.status === 'cancelled'\n }\n\n private notifyChange(run: TestWorkflowRunRecord): void {\n const listeners = [...run.changeListeners]\n run.changeListeners.clear()\n for (const listener of listeners) listener()\n }\n\n private nextChange(run: TestWorkflowRunRecord): Promise<void> {\n return new Promise(resolve => {\n run.changeListeners.add(resolve)\n })\n }\n\n private requireRun(runId: string): TestWorkflowRunRecord {\n const run = this.runs.get(runId)\n\n if (run === undefined) throw new TestWorkflowRunNotFoundError(runId)\n\n return run\n }\n\n // --- SDK adapter for `VercelWorkflows.layerFromSdk` ---\n\n private sdkRun<TResult>(run: TestWorkflowRunRecord): VercelWorkflowsSdkRun<TResult> {\n const getReadable = <Chunk>(options?: VercelWorkflowReadableOptions) =>\n this.getReadable<Chunk>(run.runId, options)\n const cancel = () => this.cancel(run.runId)\n\n return {\n runId: run.runId,\n getReadable,\n cancel,\n get status() {\n return Promise.resolve(run.status)\n },\n get returnValue() {\n return run.settled.then(() => {\n // Return values are caller-typed on the platform SDK as well.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n if (run.status === 'completed') return run.returnValue as TResult\n\n throw run.runError ?? new TestWorkflowRunCancelledError(run.runId)\n })\n }\n }\n }\n\n readonly sdk: VercelWorkflowsSdkClient = {\n start: async (workflowFn, args) => {\n const { runId } = this.start(workflowFn, args)\n\n return this.sdkRun(this.requireRun(runId))\n },\n getRun: <TResult>(runId: string) => this.sdkRun<TResult>(this.requireRun(runId)),\n resumeHook: (token, payload) => this.resumeHook(token, payload)\n }\n}\n\n// Factory for `vi.mock('workflow', () => testWorkflowModule)`: the mocked\n// module surface delegates every ambient platform API to the active world's\n// AsyncLocalStorage context, so production code under test runs unmodified.\nexport const testWorkflowModule = {\n getWorkflowMetadata: () => TestWorkflowWorld.getWorkflowMetadata(),\n getStepMetadata: () => TestWorkflowWorld.getStepMetadata(),\n getWritable: <T>() => TestWorkflowWorld.getWritable<T>(),\n createHook: <T>(input: { readonly token: string }) => TestWorkflowWorld.createHook<T>(input)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/testing/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport type {\n VercelWorkflowsSdkClient,\n VercelWorkflowsSdkRun,\n VercelWorkflowReadableOptions,\n VercelWorkflowReadableStream,\n VercelWorkflowRunStatus\n} from '../effect.ts'\n\n// Behavioral emulator of the Vercel Workflow platform for tests: run lifecycle,\n// append-only durable streams with close-once -> 409 write conflicts, a step\n// executor honoring `fn.maxRetries` + `getStepMetadata().attempt`, hooks/resume,\n// and cancellation. Host code under test runs unmodified against it; tests mock\n// only the `workflow` module's ambient APIs, delegating to the active world\n// (see `testWorkflowModule`).\n//\n// The single most important semantic: writing to (or re-closing) a closed\n// durable stream fails with the platform's HTTP 409 \"already completed\"\n// conflict shape. Any code path that performs a terminal stream effect from a\n// retryable step attempt poisons every subsequent platform retry — exactly the\n// #252 production bug. The emulator enforces that invariant permanently.\n\nexport type TestWorkflowRunStatus = VercelWorkflowRunStatus\n\n// Matches the platform's conflict contract: `EntityConflictError.is(value)`\n// checks `value.name === 'EntityConflictError'`, and the Vercel world maps\n// HTTP 409 responses to that class.\nexport class TestWorkflowStreamConflictError extends Error {\n override readonly name = 'EntityConflictError'\n readonly status = 409\n\n constructor(runId: string) {\n super(`Workflow run \"${runId}\" stream already completed`)\n }\n}\n\nexport class TestWorkflowRunConflictError extends Error {\n override readonly name = 'EntityConflictError'\n readonly status = 409\n\n constructor(message: string) {\n super(message)\n }\n}\n\nexport class TestWorkflowHookNotFoundError extends Error {\n override readonly name = 'HookNotFoundError'\n\n constructor(token: string) {\n super(`Workflow hook \"${token}\" was not found`)\n }\n}\n\nexport class TestWorkflowHookConflictError extends Error {\n override readonly name = 'HookConflictError'\n\n constructor(token: string) {\n super(`Workflow hook \"${token}\" was already resumed`)\n }\n}\n\nexport class TestWorkflowRunNotFoundError extends Error {\n override readonly name = 'WorkflowRunNotFoundError'\n\n constructor(runId: string) {\n super(`Workflow run \"${runId}\" was not found`)\n }\n}\n\nexport class TestWorkflowRunCancelledError extends Error {\n override readonly name = 'WorkflowRunCancelledError'\n\n constructor(runId: string) {\n super(`Workflow run \"${runId}\" was cancelled`)\n }\n}\n\nexport type TestWorkflowMetadata = { readonly workflowRunId: string }\n\nexport type TestWorkflowStepMetadata = {\n readonly attempt: number\n readonly stepId: string\n readonly stepName: string\n}\n\nexport type TestWorkflowStepFunction<TArgs extends ReadonlyArray<unknown>, TResult> = ((\n ...args: TArgs\n) => Promise<TResult>) & { readonly maxRetries?: number }\n\n// The platform default: retries after the first attempt; total attempts = maxRetries + 1.\nexport const defaultTestWorkflowStepMaxRetries = 3\n\ntype HookRecord = {\n readonly resolve: (payload: unknown) => void\n readonly reject: (error: TestWorkflowRunCancelledError) => void\n}\n\ntype TestWorkflowRunRecord = {\n readonly runId: string\n status: TestWorkflowRunStatus\n returnValue: unknown\n runError: unknown\n settled: Promise<void>\n readonly chunks: Array<unknown>\n streamClosed: boolean\n streamCloseCount: number\n writeAfterCloseAttempts: number\n readonly changeListeners: Set<() => void>\n readonly hooks: Map<string, HookRecord>\n readonly resumedHookTokens: Set<string>\n stepSequence: number\n readonly stepAttempts: Map<string, number>\n}\n\ntype AmbientContext = {\n readonly world: TestWorkflowWorld\n readonly run: TestWorkflowRunRecord\n readonly stepMetadata: TestWorkflowStepMetadata | undefined\n}\n\nconst ambientStorage = new AsyncLocalStorage<AmbientContext>()\n\nconst requireAmbient = (): AmbientContext => {\n const ambient = ambientStorage.getStore()\n\n if (ambient === undefined) {\n throw new Error('Workflow ambient API called outside a TestWorkflowWorld run context')\n }\n\n return ambient\n}\n\nexport type TestWorkflowRunInspection = {\n readonly runId: string\n readonly status: TestWorkflowRunStatus\n readonly chunks: ReadonlyArray<unknown>\n readonly streamClosed: boolean\n readonly streamCloseCount: number\n readonly writeAfterCloseAttempts: number\n readonly stepAttempts: ReadonlyMap<string, number>\n readonly runError: unknown\n}\n\nexport type TestWorkflowHook<T> = Promise<T> & { readonly [Symbol.dispose]: () => void }\n\ntype TestWorkflowWorldStartResult = { readonly runId: string }\n\nexport class TestWorkflowWorld {\n private readonly runs = new Map<string, TestWorkflowRunRecord>()\n private runSequence = 0\n\n start<TArgs extends ReadonlyArray<unknown>, TResult>(\n workflowFn: (...args: TArgs) => Promise<TResult>,\n args: TArgs\n ): TestWorkflowWorldStartResult {\n this.runSequence += 1\n const runId = `twr_${this.runSequence}`\n\n const run: TestWorkflowRunRecord = {\n runId,\n status: 'pending',\n returnValue: undefined,\n runError: undefined,\n settled: Promise.resolve(),\n chunks: [],\n streamClosed: false,\n streamCloseCount: 0,\n writeAfterCloseAttempts: 0,\n changeListeners: new Set(),\n hooks: new Map(),\n resumedHookTokens: new Set(),\n stepSequence: 0,\n stepAttempts: new Map()\n }\n\n this.runs.set(runId, run)\n\n run.settled = ambientStorage.run(\n { world: this, run, stepMetadata: undefined },\n async (): Promise<void> => {\n // Yield once so callers observe `pending` before execution begins,\n // mirroring the platform's enqueue-then-run lifecycle.\n await Promise.resolve()\n\n if (run.status === 'cancelled') return\n\n run.status = 'running'\n\n try {\n run.returnValue = await workflowFn(...args)\n\n if (run.status === 'running') run.status = 'completed'\n } catch (error) {\n run.runError = error\n\n if (run.status === 'running') run.status = 'failed'\n } finally {\n this.notifyChange(run)\n }\n }\n )\n\n return { runId }\n }\n\n async settled(runId: string): Promise<void> {\n await this.requireRun(runId).settled\n }\n\n inspect(runId: string): TestWorkflowRunInspection {\n const run = this.requireRun(runId)\n\n return {\n runId: run.runId,\n status: run.status,\n chunks: [...run.chunks],\n streamClosed: run.streamClosed,\n streamCloseCount: run.streamCloseCount,\n writeAfterCloseAttempts: run.writeAfterCloseAttempts,\n stepAttempts: new Map(run.stepAttempts),\n runError: run.runError\n }\n }\n\n status(runId: string): TestWorkflowRunStatus {\n return this.requireRun(runId).status\n }\n\n async cancel(runId: string): Promise<void> {\n const run = this.requireRun(runId)\n\n if (run.status === 'completed' || run.status === 'failed' || run.status === 'cancelled') {\n throw new TestWorkflowRunConflictError(\n `Workflow run \"${runId}\" is already ${run.status} and cannot be cancelled`\n )\n }\n\n run.status = 'cancelled'\n\n for (const [token, hook] of run.hooks) {\n run.hooks.delete(token)\n hook.reject(new TestWorkflowRunCancelledError(runId))\n }\n\n this.notifyChange(run)\n }\n\n async resumeHook(token: string, payload: unknown): Promise<void> {\n for (const run of this.runs.values()) {\n const hook = run.hooks.get(token)\n\n if (hook !== undefined) {\n run.hooks.delete(token)\n run.resumedHookTokens.add(token)\n hook.resolve(payload)\n\n return\n }\n\n if (run.resumedHookTokens.has(token)) {\n throw new TestWorkflowHookConflictError(token)\n }\n }\n\n throw new TestWorkflowHookNotFoundError(token)\n }\n\n hasResumedHook(token: string): boolean {\n for (const run of this.runs.values()) {\n if (run.resumedHookTokens.has(token)) return true\n }\n\n return false\n }\n\n // --- Step executor (platform retry semantics) ---\n\n // Runs a step body with the platform's retry contract: `fn.maxRetries`\n // retries after the first attempt (total attempts = maxRetries + 1) and\n // 1-based `getStepMetadata().attempt` visible to the body.\n async runStep<TArgs extends ReadonlyArray<unknown>, TResult>(\n stepFn: TestWorkflowStepFunction<TArgs, TResult>,\n args: TArgs,\n stepName = stepFn.name === '' ? 'step' : stepFn.name\n ): Promise<TResult> {\n const ambient = requireAmbient()\n const run = ambient.run\n run.stepSequence += 1\n const stepId = `${run.runId}:step:${run.stepSequence}:${stepName}`\n const maxRetries = stepFn.maxRetries ?? defaultTestWorkflowStepMaxRetries\n const maxAttempts = maxRetries + 1\n\n let attempt = 1\n\n for (;;) {\n run.stepAttempts.set(stepName, (run.stepAttempts.get(stepName) ?? 0) + 1)\n\n try {\n return await ambientStorage.run(\n { world: this, run, stepMetadata: { attempt, stepId, stepName } },\n () => stepFn(...args)\n )\n } catch (error) {\n if (attempt >= maxAttempts) throw error\n attempt += 1\n }\n }\n }\n\n // Wraps a step function so orchestration code can invoke it exactly the way\n // the platform invokes `use step` functions: through the retry executor.\n step<TArgs extends ReadonlyArray<unknown>, TResult>(\n stepFn: TestWorkflowStepFunction<TArgs, TResult>,\n stepName?: string\n ): (...args: TArgs) => Promise<TResult> {\n return (...args: TArgs) => this.runStep(stepFn, args, stepName)\n }\n\n // --- Ambient platform APIs (exposed through `testWorkflowModule`) ---\n\n static getWorkflowMetadata(): TestWorkflowMetadata {\n return { workflowRunId: requireAmbient().run.runId }\n }\n\n static getStepMetadata(): TestWorkflowStepMetadata {\n const ambient = requireAmbient()\n\n if (ambient.stepMetadata === undefined) {\n throw new Error('getStepMetadata called outside a step context')\n }\n\n return ambient.stepMetadata\n }\n\n static getWritable<T>(): WritableStream<T> {\n const ambient = requireAmbient()\n\n return ambient.world.makeWritable(ambient.run)\n }\n\n static createHook<T>(input: { readonly token: string }): TestWorkflowHook<T> {\n const ambient = requireAmbient()\n\n return ambient.world.makeHook<T>(ambient.run, input.token)\n }\n\n // --- Durable streams ---\n\n private makeWritable<T>(run: TestWorkflowRunRecord): WritableStream<T> {\n return new WritableStream<T>({\n write: chunk => {\n if (run.streamClosed || run.status === 'cancelled') {\n run.writeAfterCloseAttempts += 1\n throw new TestWorkflowStreamConflictError(run.runId)\n }\n\n run.chunks.push(chunk)\n this.notifyChange(run)\n },\n close: () => {\n if (run.streamClosed) {\n run.streamCloseCount += 1\n throw new TestWorkflowStreamConflictError(run.runId)\n }\n\n run.streamClosed = true\n run.streamCloseCount += 1\n this.notifyChange(run)\n }\n })\n }\n\n private makeHook<T>(run: TestWorkflowRunRecord, token: string): TestWorkflowHook<T> {\n let resolveHook: (payload: unknown) => void = () => {}\n\n let rejectHook: (error: TestWorkflowRunCancelledError) => void = () => {}\n\n const promise = new Promise<T>((resolve, reject) => {\n // SAFETY: Hook payloads are caller-typed on the platform (`createHook<T>`).\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n resolveHook = payload => resolve(payload as T)\n rejectHook = reject\n })\n\n run.hooks.set(token, { resolve: resolveHook, reject: rejectHook })\n\n return Object.assign(promise, {\n [Symbol.dispose]: () => {\n run.hooks.delete(token)\n }\n })\n }\n\n getReadable<T>(\n runId: string,\n options?: VercelWorkflowReadableOptions\n ): VercelWorkflowReadableStream<T> {\n const run = this.requireRun(runId)\n const requestedStart = options?.startIndex ?? 0\n\n let index =\n requestedStart < 0 ? Math.max(0, run.chunks.length + requestedStart) : requestedStart\n\n const stream = new ReadableStream<T>({\n pull: async controller => {\n while (index >= run.chunks.length && !this.streamEnded(run)) {\n await this.nextChange(run)\n }\n\n if (index < run.chunks.length) {\n // SAFETY: The platform readable is caller-typed (`WorkflowReadableStream<R = any>`);\n // the durable log stores opaque chunks, so this coercion mirrors the\n // platform contract exactly.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n controller.enqueue(run.chunks[index] as T)\n index += 1\n\n return\n }\n\n controller.close()\n }\n })\n\n return Object.assign(stream, {\n getTailIndex: () => Promise.resolve(run.chunks.length - 1)\n })\n }\n\n private streamEnded(run: TestWorkflowRunRecord): boolean {\n return run.streamClosed || run.status === 'cancelled'\n }\n\n private notifyChange(run: TestWorkflowRunRecord): void {\n const listeners = [...run.changeListeners]\n run.changeListeners.clear()\n\n for (const listener of listeners) listener()\n }\n\n private nextChange(run: TestWorkflowRunRecord): Promise<void> {\n return new Promise(resolve => {\n run.changeListeners.add(resolve)\n })\n }\n\n private requireRun(runId: string): TestWorkflowRunRecord {\n const run = this.runs.get(runId)\n\n if (run === undefined) throw new TestWorkflowRunNotFoundError(runId)\n\n return run\n }\n\n // --- SDK adapter for `VercelWorkflows.layerFromSdk` ---\n\n private sdkRun<TResult>(run: TestWorkflowRunRecord): VercelWorkflowsSdkRun<TResult> {\n const getReadable = <Chunk>(options?: VercelWorkflowReadableOptions) =>\n this.getReadable<Chunk>(run.runId, options)\n\n const cancel = () => this.cancel(run.runId)\n\n return {\n runId: run.runId,\n getReadable,\n cancel,\n get status() {\n return Promise.resolve(run.status)\n },\n get returnValue() {\n return run.settled.then(() => {\n if (run.status === 'completed') {\n // SAFETY: Return values are caller-typed on the platform SDK as well.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return run.returnValue as TResult\n }\n\n throw run.runError ?? new TestWorkflowRunCancelledError(run.runId)\n })\n }\n }\n }\n\n readonly sdk: VercelWorkflowsSdkClient = {\n start: async (workflowFn, args) => {\n const { runId } = this.start(workflowFn, args)\n\n return this.sdkRun(this.requireRun(runId))\n },\n getRun: <TResult>(runId: string) => this.sdkRun<TResult>(this.requireRun(runId)),\n resumeHook: (token, payload) => this.resumeHook(token, payload)\n }\n}\n\n// Factory for `vi.mock('workflow', () => testWorkflowModule)`: the mocked\n// module surface delegates every ambient platform API to the active world's\n// AsyncLocalStorage context, so production code under test runs unmodified.\nexport const testWorkflowModule = {\n getWorkflowMetadata: () => TestWorkflowWorld.getWorkflowMetadata(),\n getStepMetadata: () => TestWorkflowWorld.getStepMetadata(),\n getWritable: <T>() => TestWorkflowWorld.getWritable<T>(),\n createHook: <T>(input: { readonly token: string }) => TestWorkflowWorld.createHook<T>(input)\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-children.d.mts","names":[],"sources":["../src/workflow-children.ts"],"mappings":";;AAKA;;;;iBAAsB,kBAAA,GAAA,CAAsB,KAAA;EAAA,SACjC,IAAA,GACP,OAAA,aACG,OAAA;IAAA,SAAmB,IAAA;EAAA;IAAA,SAA2B,IAAA;IAAA,SAAqB,KAAA,EAAO,CAAA;EAAA;EAAA,SACtE,KAAA,GAAQ,OAAA,aAAoB,OAAA;AAAA,IACnC,OAAA,CAAQ,CAAA;;;;
|
|
1
|
+
{"version":3,"file":"workflow-children.d.mts","names":[],"sources":["../src/workflow-children.ts"],"mappings":";;AAKA;;;;iBAAsB,kBAAA,GAAA,CAAsB,KAAA;EAAA,SACjC,IAAA,GACP,OAAA,aACG,OAAA;IAAA,SAAmB,IAAA;EAAA;IAAA,SAA2B,IAAA;IAAA,SAAqB,KAAA,EAAO,CAAA;EAAA;EAAA,SACtE,KAAA,GAAQ,OAAA,aAAoB,OAAA;AAAA,IACnC,OAAA,CAAQ,CAAA;;;;iBAcU,4BAAA,qBAAA,CAAkD,KAAA;EAAA,SAC7D,KAAA,EAAO,aAAA,CAAc,IAAA;EAAA,SACrB,WAAA;EAAA,SACA,SAAA,QAAiB,OAAA;IAAA,SACb,KAAA;EAAA;IAAA,SAA2B,KAAA;IAAA,SAAuB,KAAA,EAAO,KAAA;EAAA;EAAA,SAE7D,OAAA,GAAU,IAAA,EAAM,IAAA,EAAM,KAAA,aAAkB,OAAA,CAAQ,MAAA;AAAA,IACvD,OAAA;EAAA,SAEW,KAAA;EAAA,SACA,OAAA,EAAS,aAAA,CAAc,MAAA;EAAA,SACvB,QAAA,GAAW,aAAA;IAAA,SAAyB,KAAA;IAAA,SAAwB,KAAA;EAAA;AAAA;EAAA,SAE5D,KAAA;EAAA,SAAuB,KAAA,EAAO,KAAA;AAAA"}
|
|
@@ -41,11 +41,12 @@ async function orchestrateWorkflowToolBatch(input) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
}));
|
|
44
|
-
|
|
44
|
+
const ready = {
|
|
45
45
|
ready: true,
|
|
46
|
-
results: [...results.entries()].sort(([a], [b]) => a - b).map(([, result]) => result)
|
|
47
|
-
...failures.length === 0 ? {} : { failures: failures.sort((a, b) => a.index - b.index) }
|
|
46
|
+
results: [...results.entries()].sort(([a], [b]) => a - b).map(([, result]) => result)
|
|
48
47
|
};
|
|
48
|
+
if (failures.length !== 0) ready.failures = failures.sort((a, b) => a.index - b.index);
|
|
49
|
+
return ready;
|
|
49
50
|
}
|
|
50
51
|
//#endregion
|
|
51
52
|
export { awaitWorkflowChild, orchestrateWorkflowToolBatch };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-children.mjs","names":[],"sources":["../src/workflow-children.ts"],"sourcesContent":["/** Workflow orchestration only. Callbacks must be durable steps (sleep must be Workflow sleep).\n * A read is short-lived; never put a long returnValue poll inside a step.\n * Both callbacks receive a deterministic zero-based attempt for host budgets/backoff.\n * Defaults remain unbounded: hosts end observation at their ceiling by returning done + value.\n */\nexport async function awaitWorkflowChild<A>(input: {\n readonly read: (\n attempt: number\n ) => Promise<{ readonly done: false } | { readonly done: true; readonly value: A }>\n readonly sleep: (attempt: number) => Promise<void>\n}): Promise<A> {\n const { read, sleep } = input\n for (let attempt = 0; ; attempt++) {\n const state = await read(attempt)\n if (state.done) return state.value\n await sleep(attempt)\n }\n}\n\n/** Preflight fences the entire batch. On rejection, stop dispatching new work, settle already\n * active siblings, and return their ordered progress alongside failures. Hosts must handle failures.\n */\nexport async function orchestrateWorkflowToolBatch<Call, Result, Pause>(input: {\n readonly calls: ReadonlyArray<Call>\n readonly concurrency: number\n readonly preflight: () => Promise<\n { readonly ready: true } | { readonly ready: false; readonly value: Pause }\n >\n readonly execute: (call: Call, index: number) => Promise<Result>\n}): Promise<\n | {\n readonly ready: true\n readonly results: ReadonlyArray<Result>\n readonly failures?: ReadonlyArray<{ readonly index: number; readonly error: unknown }>\n }\n | { readonly ready: false; readonly value: Pause }\n> {\n const { calls, concurrency, preflight, execute } = input\n const prepared = await preflight()\n if (!prepared.ready) return prepared\n const width = Number.isFinite(concurrency) ? Math.max(1, Math.floor(concurrency)) : 1\n const entries = calls.map((call, index) => ({ call, index }))\n let next = 0\n const results = new Map<number, Result>()\n const failures: Array<{ readonly index: number; readonly error: unknown }> = []\n await Promise.all(\n Array.from({ length: Math.min(width, calls.length) }, async () => {\n while (failures.length === 0) {\n const entry = entries[next++]\n if (entry === undefined) return\n try {\n results.set(entry.index, await execute(entry.call, entry.index))\n } catch (error) {\n failures.push({ index: entry.index, error })\n }\n }\n })\n )\n
|
|
1
|
+
{"version":3,"file":"workflow-children.mjs","names":[],"sources":["../src/workflow-children.ts"],"sourcesContent":["/** Workflow orchestration only. Callbacks must be durable steps (sleep must be Workflow sleep).\n * A read is short-lived; never put a long returnValue poll inside a step.\n * Both callbacks receive a deterministic zero-based attempt for host budgets/backoff.\n * Defaults remain unbounded: hosts end observation at their ceiling by returning done + value.\n */\nexport async function awaitWorkflowChild<A>(input: {\n readonly read: (\n attempt: number\n ) => Promise<{ readonly done: false } | { readonly done: true; readonly value: A }>\n readonly sleep: (attempt: number) => Promise<void>\n}): Promise<A> {\n const { read, sleep } = input\n\n for (let attempt = 0; ; attempt++) {\n const state = await read(attempt)\n\n if (state.done) return state.value\n await sleep(attempt)\n }\n}\n\n/** Preflight fences the entire batch. On rejection, stop dispatching new work, settle already\n * active siblings, and return their ordered progress alongside failures. Hosts must handle failures.\n */\nexport async function orchestrateWorkflowToolBatch<Call, Result, Pause>(input: {\n readonly calls: ReadonlyArray<Call>\n readonly concurrency: number\n readonly preflight: () => Promise<\n { readonly ready: true } | { readonly ready: false; readonly value: Pause }\n >\n readonly execute: (call: Call, index: number) => Promise<Result>\n}): Promise<\n | {\n readonly ready: true\n readonly results: ReadonlyArray<Result>\n readonly failures?: ReadonlyArray<{ readonly index: number; readonly error: unknown }>\n }\n | { readonly ready: false; readonly value: Pause }\n> {\n const { calls, concurrency, preflight, execute } = input\n const prepared = await preflight()\n\n if (!prepared.ready) return prepared\n const width = Number.isFinite(concurrency) ? Math.max(1, Math.floor(concurrency)) : 1\n const entries = calls.map((call, index) => ({ call, index }))\n let next = 0\n const results = new Map<number, Result>()\n const failures: Array<{ readonly index: number; readonly error: unknown }> = []\n await Promise.all(\n Array.from({ length: Math.min(width, calls.length) }, async () => {\n while (failures.length === 0) {\n const entry = entries[next++]\n\n if (entry === undefined) return\n\n try {\n results.set(entry.index, await execute(entry.call, entry.index))\n } catch (error) {\n failures.push({ index: entry.index, error })\n }\n }\n })\n )\n\n type ReadyWorkflowToolBatch = {\n ready: true\n results: Array<Result>\n failures?: Array<{ readonly index: number; readonly error: unknown }>\n }\n\n const ready: ReadyWorkflowToolBatch = {\n ready: true,\n results: [...results.entries()].sort(([a], [b]) => a - b).map(([, result]) => result)\n }\n\n if (failures.length !== 0) {\n ready.failures = failures.sort((a, b) => a.index - b.index)\n }\n\n return ready\n}\n"],"mappings":";;;;;;AAKA,eAAsB,mBAAsB,OAK7B;CACb,MAAM,EAAE,MAAM,UAAU;CAExB,KAAK,IAAI,UAAU,IAAK,WAAW;EACjC,MAAM,QAAQ,MAAM,KAAK,OAAO;EAEhC,IAAI,MAAM,MAAM,OAAO,MAAM;EAC7B,MAAM,MAAM,OAAO;CACrB;AACF;;;;AAKA,eAAsB,6BAAkD,OActE;CACA,MAAM,EAAE,OAAO,aAAa,WAAW,YAAY;CACnD,MAAM,WAAW,MAAM,UAAU;CAEjC,IAAI,CAAC,SAAS,OAAO,OAAO;CAC5B,MAAM,QAAQ,OAAO,SAAS,WAAW,IAAI,KAAK,IAAI,GAAG,KAAK,MAAM,WAAW,CAAC,IAAI;CACpF,MAAM,UAAU,MAAM,KAAK,MAAM,WAAW;EAAE;EAAM;CAAM,EAAE;CAC5D,IAAI,OAAO;CACX,MAAM,0BAAU,IAAI,IAAoB;CACxC,MAAM,WAAuE,CAAC;CAC9E,MAAM,QAAQ,IACZ,MAAM,KAAK,EAAE,QAAQ,KAAK,IAAI,OAAO,MAAM,MAAM,EAAE,GAAG,YAAY;EAChE,OAAO,SAAS,WAAW,GAAG;GAC5B,MAAM,QAAQ,QAAQ;GAEtB,IAAI,UAAU,KAAA,GAAW;GAEzB,IAAI;IACF,QAAQ,IAAI,MAAM,OAAO,MAAM,QAAQ,MAAM,MAAM,MAAM,KAAK,CAAC;GACjE,SAAS,OAAO;IACd,SAAS,KAAK;KAAE,OAAO,MAAM;KAAO;IAAM,CAAC;GAC7C;EACF;CACF,CAAC,CACH;CAQA,MAAM,QAAgC;EACpC,OAAO;EACP,SAAS,CAAC,GAAG,QAAQ,QAAQ,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,EAAE,KAAK,GAAG,YAAY,MAAM;CACtF;CAEA,IAAI,SAAS,WAAW,GACtB,MAAM,WAAW,SAAS,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;CAG5D,OAAO;AACT"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Data, Effect } from "effect";
|
|
2
2
|
|
|
3
3
|
//#region src/workflow-events.d.ts
|
|
4
4
|
type DurableAgentEventSequencerState = {
|
|
@@ -40,22 +40,165 @@ type CommitThenWriteTerminalEventInput<TerminalEvent extends object, TerminalWri
|
|
|
40
40
|
readonly commit: Effect.Effect<void, CommitError>;
|
|
41
41
|
readonly terminal: TerminalEvent;
|
|
42
42
|
readonly write: (event: TerminalEvent) => Effect.Effect<TerminalWriteResult, TerminalWriteError>;
|
|
43
|
-
readonly writeCommitError: (error:
|
|
43
|
+
readonly writeCommitError: (error: CommitError) => Effect.Effect<CommitErrorWriteResult, CommitErrorWriteError>;
|
|
44
44
|
};
|
|
45
|
-
type CommitThenWriteTerminalEventResult<TerminalWriteResult, CommitErrorWriteResult> = {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
45
|
+
type CommitThenWriteTerminalEventResult<TerminalWriteResult, CommitErrorWriteResult> = Data.TaggedEnum<{
|
|
46
|
+
Committed: {
|
|
47
|
+
readonly writeResult: TerminalWriteResult;
|
|
48
|
+
};
|
|
49
|
+
CommitFailed: {
|
|
50
|
+
readonly commitError: unknown;
|
|
51
|
+
readonly writeResult: CommitErrorWriteResult;
|
|
52
|
+
};
|
|
53
|
+
TerminalWriteFailed: {
|
|
54
|
+
readonly error: unknown;
|
|
55
|
+
};
|
|
56
|
+
CommitErrorWriteFailed: {
|
|
57
|
+
readonly commitError: unknown;
|
|
58
|
+
readonly error: unknown;
|
|
59
|
+
};
|
|
60
|
+
}>;
|
|
61
|
+
declare const CommitThenWriteTerminalEventResult: {
|
|
62
|
+
readonly Committed: <A, B>(args: {
|
|
63
|
+
readonly writeResult: A;
|
|
64
|
+
}) => {
|
|
65
|
+
readonly _tag: "Committed";
|
|
66
|
+
readonly writeResult: A;
|
|
67
|
+
};
|
|
68
|
+
readonly CommitFailed: <A, B>(args: {
|
|
69
|
+
readonly commitError: unknown;
|
|
70
|
+
readonly writeResult: B;
|
|
71
|
+
}) => {
|
|
72
|
+
readonly _tag: "CommitFailed";
|
|
73
|
+
readonly commitError: unknown;
|
|
74
|
+
readonly writeResult: B;
|
|
75
|
+
};
|
|
76
|
+
readonly TerminalWriteFailed: <A, B>(args: {
|
|
77
|
+
readonly error: unknown;
|
|
78
|
+
}) => {
|
|
79
|
+
readonly _tag: "TerminalWriteFailed";
|
|
80
|
+
readonly error: unknown;
|
|
81
|
+
};
|
|
82
|
+
readonly CommitErrorWriteFailed: <A, B>(args: {
|
|
83
|
+
readonly commitError: unknown;
|
|
84
|
+
readonly error: unknown;
|
|
85
|
+
}) => {
|
|
86
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
87
|
+
readonly commitError: unknown;
|
|
88
|
+
readonly error: unknown;
|
|
89
|
+
};
|
|
90
|
+
readonly $is: <Tag extends "Committed" | "CommitFailed" | "TerminalWriteFailed" | "CommitErrorWriteFailed">(tag: Tag) => {
|
|
91
|
+
<T extends {
|
|
92
|
+
readonly _tag: "Committed";
|
|
93
|
+
readonly writeResult: any;
|
|
94
|
+
} | {
|
|
95
|
+
readonly _tag: "CommitFailed";
|
|
96
|
+
readonly commitError: unknown;
|
|
97
|
+
readonly writeResult: any;
|
|
98
|
+
} | {
|
|
99
|
+
readonly _tag: "TerminalWriteFailed";
|
|
100
|
+
readonly error: unknown;
|
|
101
|
+
} | {
|
|
102
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
103
|
+
readonly commitError: unknown;
|
|
104
|
+
readonly error: unknown;
|
|
105
|
+
}>(u: T): u is T & {
|
|
106
|
+
readonly _tag: Tag;
|
|
107
|
+
};
|
|
108
|
+
(u: unknown): u is Extract<{
|
|
109
|
+
readonly _tag: "Committed";
|
|
110
|
+
readonly writeResult: unknown;
|
|
111
|
+
}, {
|
|
112
|
+
readonly _tag: Tag;
|
|
113
|
+
}> | Extract<{
|
|
114
|
+
readonly _tag: "CommitFailed";
|
|
115
|
+
readonly commitError: unknown;
|
|
116
|
+
readonly writeResult: unknown;
|
|
117
|
+
}, {
|
|
118
|
+
readonly _tag: Tag;
|
|
119
|
+
}> | Extract<{
|
|
120
|
+
readonly _tag: "TerminalWriteFailed";
|
|
121
|
+
readonly error: unknown;
|
|
122
|
+
}, {
|
|
123
|
+
readonly _tag: Tag;
|
|
124
|
+
}> | Extract<{
|
|
125
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
126
|
+
readonly commitError: unknown;
|
|
127
|
+
readonly error: unknown;
|
|
128
|
+
}, {
|
|
129
|
+
readonly _tag: Tag;
|
|
130
|
+
}>;
|
|
131
|
+
};
|
|
132
|
+
readonly $match: {
|
|
133
|
+
<A, B, C, D, Cases extends {
|
|
134
|
+
readonly Committed: (args: {
|
|
135
|
+
readonly _tag: "Committed";
|
|
136
|
+
readonly writeResult: A;
|
|
137
|
+
}) => any;
|
|
138
|
+
readonly CommitFailed: (args: {
|
|
139
|
+
readonly _tag: "CommitFailed";
|
|
140
|
+
readonly commitError: unknown;
|
|
141
|
+
readonly writeResult: B;
|
|
142
|
+
}) => any;
|
|
143
|
+
readonly TerminalWriteFailed: (args: {
|
|
144
|
+
readonly _tag: "TerminalWriteFailed";
|
|
145
|
+
readonly error: unknown;
|
|
146
|
+
}) => any;
|
|
147
|
+
readonly CommitErrorWriteFailed: (args: {
|
|
148
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
149
|
+
readonly commitError: unknown;
|
|
150
|
+
readonly error: unknown;
|
|
151
|
+
}) => any;
|
|
152
|
+
}>(cases: Cases): (self: {
|
|
153
|
+
readonly _tag: "Committed";
|
|
154
|
+
readonly writeResult: A;
|
|
155
|
+
} | {
|
|
156
|
+
readonly _tag: "CommitFailed";
|
|
157
|
+
readonly commitError: unknown;
|
|
158
|
+
readonly writeResult: B;
|
|
159
|
+
} | {
|
|
160
|
+
readonly _tag: "TerminalWriteFailed";
|
|
161
|
+
readonly error: unknown;
|
|
162
|
+
} | {
|
|
163
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
164
|
+
readonly commitError: unknown;
|
|
165
|
+
readonly error: unknown;
|
|
166
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Committed" | "CommitFailed" | "TerminalWriteFailed" | "CommitErrorWriteFailed"]>>;
|
|
167
|
+
<A, B, C, D, Cases extends {
|
|
168
|
+
readonly Committed: (args: {
|
|
169
|
+
readonly _tag: "Committed";
|
|
170
|
+
readonly writeResult: A;
|
|
171
|
+
}) => any;
|
|
172
|
+
readonly CommitFailed: (args: {
|
|
173
|
+
readonly _tag: "CommitFailed";
|
|
174
|
+
readonly commitError: unknown;
|
|
175
|
+
readonly writeResult: B;
|
|
176
|
+
}) => any;
|
|
177
|
+
readonly TerminalWriteFailed: (args: {
|
|
178
|
+
readonly _tag: "TerminalWriteFailed";
|
|
179
|
+
readonly error: unknown;
|
|
180
|
+
}) => any;
|
|
181
|
+
readonly CommitErrorWriteFailed: (args: {
|
|
182
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
183
|
+
readonly commitError: unknown;
|
|
184
|
+
readonly error: unknown;
|
|
185
|
+
}) => any;
|
|
186
|
+
}>(self: {
|
|
187
|
+
readonly _tag: "Committed";
|
|
188
|
+
readonly writeResult: A;
|
|
189
|
+
} | {
|
|
190
|
+
readonly _tag: "CommitFailed";
|
|
191
|
+
readonly commitError: unknown;
|
|
192
|
+
readonly writeResult: B;
|
|
193
|
+
} | {
|
|
194
|
+
readonly _tag: "TerminalWriteFailed";
|
|
195
|
+
readonly error: unknown;
|
|
196
|
+
} | {
|
|
197
|
+
readonly _tag: "CommitErrorWriteFailed";
|
|
198
|
+
readonly commitError: unknown;
|
|
199
|
+
readonly error: unknown;
|
|
200
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Committed" | "CommitFailed" | "TerminalWriteFailed" | "CommitErrorWriteFailed"]>>;
|
|
201
|
+
};
|
|
59
202
|
};
|
|
60
203
|
declare const makeDurableAgentEventSequencerState: (eventSequence?: number) => DurableAgentEventSequencerState;
|
|
61
204
|
declare const durableAgentEventId: (input: DurableAgentEventIdInput) => string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-events.d.mts","names":[],"sources":["../src/workflow-events.ts"],"mappings":";;;KAEY,+BAAA;EAAA,SACD,aAAa;AAAA;AAAA,KAGZ,iBAAA,kCAAmD,KAAK;EAAA,SACzD,OAAA;EAAA,SACA,WAAA;AAAA;AAAA,KAGC,wBAAA;EAAA,SACD,QAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;AAAA;AAAA,KAGC,8BAAA;EAAA,SACD,KAAA,EAAO,+BAAA;EAVP;;AAAW;AAGtB;;;;EAHW,SAkBA,QAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA,EAAO,KAAK;EAAA,SACZ,WAAA;AAAA;AAAA,KAGC,0BAAA;EAAA,SACD,KAAA,EAAO,iBAAA,CAAkB,KAAA;EAAA,SACzB,aAAA;EAAA,SACA,iBAAA;EAAA,SACA,SAAA,EAAW,+BAAA;AAAA;AAAA,KAGV,2BAAA,kCACV,8BAAA,CAA+B,KAAA;EAAA,SACpB,MAAA,EAAQ,2BAAA,CAA4B,UAAA;AAAA;AAAA,KAGrC,iCAAA;EAAA,SAQD,MAAA,EAAQ,MAAA,CAAO,MAAA,OAAa,WAAA;EAAA,SAC5B,QAAA,EAAU,aAAA;EAAA,SACV,KAAA,GAAQ,KAAA,EAAO,aAAA,KAAkB,MAAA,CAAO,MAAA,CAAO,mBAAA,EAAqB,kBAAA;EAAA,SACpE,gBAAA,GACP,KAAA,
|
|
1
|
+
{"version":3,"file":"workflow-events.d.mts","names":[],"sources":["../src/workflow-events.ts"],"mappings":";;;KAEY,+BAAA;EAAA,SACD,aAAa;AAAA;AAAA,KAGZ,iBAAA,kCAAmD,KAAK;EAAA,SACzD,OAAA;EAAA,SACA,WAAA;AAAA;AAAA,KAGC,wBAAA;EAAA,SACD,QAAA;EAAA,SACA,IAAA;EAAA,SACA,aAAA;AAAA;AAAA,KAGC,8BAAA;EAAA,SACD,KAAA,EAAO,+BAAA;EAVP;;AAAW;AAGtB;;;;EAHW,SAkBA,QAAA;EAAA,SACA,IAAA;EAAA,SACA,KAAA,EAAO,KAAK;EAAA,SACZ,WAAA;AAAA;AAAA,KAGC,0BAAA;EAAA,SACD,KAAA,EAAO,iBAAA,CAAkB,KAAA;EAAA,SACzB,aAAA;EAAA,SACA,iBAAA;EAAA,SACA,SAAA,EAAW,+BAAA;AAAA;AAAA,KAGV,2BAAA,kCACV,8BAAA,CAA+B,KAAA;EAAA,SACpB,MAAA,EAAQ,2BAAA,CAA4B,UAAA;AAAA;AAAA,KAGrC,iCAAA;EAAA,SAQD,MAAA,EAAQ,MAAA,CAAO,MAAA,OAAa,WAAA;EAAA,SAC5B,QAAA,EAAU,aAAA;EAAA,SACV,KAAA,GAAQ,KAAA,EAAO,aAAA,KAAkB,MAAA,CAAO,MAAA,CAAO,mBAAA,EAAqB,kBAAA;EAAA,SACpE,gBAAA,GACP,KAAA,EAAO,WAAA,KACJ,MAAA,CAAO,MAAA,CAAO,sBAAA,EAAwB,qBAAA;AAAA;AAAA,KAGjC,kCAAA,gDACV,IAAA,CAAK,UAAA;EACH,SAAA;IAAA,SACW,WAAA,EAAa,mBAAA;EAAA;EAExB,YAAA;IAAA,SACW,WAAA;IAAA,SACA,WAAA,EAAa,sBAAA;EAAA;EAExB,mBAAA;IAAA,SACW,KAAA;EAAA;EAEX,sBAAA;IAAA,SACW,WAAA;IAAA,SACA,KAAA;EAAA;AAAA;AAAA,cAQF,kCAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAKA,mCAAA,GACX,aAAA,cACC,+BAAsD;AAAA,cAE5C,mBAAA,GAAuB,KAA+B,EAAxB,wBAAwB;AAAA,cAGtD,yBAAA,yBACX,KAAA,EAAO,8BAAA,CAA+B,KAAA,MACrC,0BAAA,CAA2B,KAAA;AAAA,cAiCjB,sBAAA,yBACX,KAAA,EAAO,2BAAA,CAA4B,KAAA,MAClC,MAAA,CAAO,MAAA,CAAO,0BAAA,CAA2B,KAAA;AAAA,cAW/B,4BAAA,sIAQX,KAAA,EAAO,iCAAA,CACL,aAAA,EACA,mBAAA,EACA,sBAAA,EACA,WAAA,EACA,kBAAA,EACA,qBAAA,MAED,MAAA,CAAO,MAAA,CACR,kCAAA,CAAmC,mBAAA,EAAqB,sBAAA"}
|
package/dist/workflow-events.mjs
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
1
|
+
import { Data, Effect } from "effect";
|
|
2
2
|
//#region src/workflow-events.ts
|
|
3
|
+
const CommitThenWriteTerminalEventResult = Data.taggedEnum();
|
|
3
4
|
const textEncoder = new TextEncoder();
|
|
4
5
|
const makeDurableAgentEventSequencerState = (eventSequence = 0) => ({ eventSequence });
|
|
5
6
|
const durableAgentEventId = (input) => `${input.streamId}:${input.turn}:${input.eventSequence}`;
|
|
6
7
|
const sequenceDurableAgentEvent = (input) => {
|
|
7
8
|
const eventSequence = input.state.eventSequence;
|
|
8
9
|
const nextEventSequence = eventSequence + 1;
|
|
10
|
+
const event = {
|
|
11
|
+
...input.event,
|
|
12
|
+
eventId: durableAgentEventId({
|
|
13
|
+
streamId: input.streamId,
|
|
14
|
+
turn: input.turn,
|
|
15
|
+
eventSequence
|
|
16
|
+
})
|
|
17
|
+
};
|
|
18
|
+
if (input.createdAtMs !== void 0) event.createdAtMs = input.createdAtMs;
|
|
9
19
|
return {
|
|
10
|
-
event
|
|
11
|
-
...input.event,
|
|
12
|
-
eventId: durableAgentEventId({
|
|
13
|
-
streamId: input.streamId,
|
|
14
|
-
turn: input.turn,
|
|
15
|
-
eventSequence
|
|
16
|
-
}),
|
|
17
|
-
...input.createdAtMs === void 0 ? {} : { createdAtMs: input.createdAtMs }
|
|
18
|
-
},
|
|
20
|
+
event,
|
|
19
21
|
eventSequence,
|
|
20
22
|
nextEventSequence,
|
|
21
23
|
nextState: makeDurableAgentEventSequencerState(nextEventSequence)
|
|
@@ -30,35 +32,23 @@ const writeDurableAgentEvent = (input) => Effect.gen(function* () {
|
|
|
30
32
|
});
|
|
31
33
|
return sequenced;
|
|
32
34
|
});
|
|
33
|
-
const committedTerminalResult = (writeResult) => ({
|
|
34
|
-
_tag: "Committed",
|
|
35
|
-
writeResult
|
|
36
|
-
});
|
|
37
|
-
const commitFailedTerminalResult = (commitError, writeResult) => ({
|
|
38
|
-
_tag: "CommitFailed",
|
|
39
|
-
commitError,
|
|
40
|
-
writeResult
|
|
41
|
-
});
|
|
42
|
-
const terminalWriteFailedResult = (error) => ({
|
|
43
|
-
_tag: "TerminalWriteFailed",
|
|
44
|
-
error
|
|
45
|
-
});
|
|
46
|
-
const commitErrorWriteFailedResult = (commitError, error) => ({
|
|
47
|
-
_tag: "CommitErrorWriteFailed",
|
|
48
|
-
commitError,
|
|
49
|
-
error
|
|
50
|
-
});
|
|
51
35
|
const commitThenWriteTerminalEvent = (input) => input.commit.pipe(Effect.matchEffect({
|
|
52
36
|
onFailure: (commitError) => input.writeCommitError(commitError).pipe(Effect.match({
|
|
53
|
-
onFailure: (error) =>
|
|
54
|
-
|
|
37
|
+
onFailure: (error) => CommitThenWriteTerminalEventResult.CommitErrorWriteFailed({
|
|
38
|
+
commitError,
|
|
39
|
+
error
|
|
40
|
+
}),
|
|
41
|
+
onSuccess: (writeResult) => CommitThenWriteTerminalEventResult.CommitFailed({
|
|
42
|
+
commitError,
|
|
43
|
+
writeResult
|
|
44
|
+
})
|
|
55
45
|
})),
|
|
56
46
|
onSuccess: () => input.write(input.terminal).pipe(Effect.match({
|
|
57
|
-
onFailure: (error) =>
|
|
58
|
-
onSuccess: (writeResult) =>
|
|
47
|
+
onFailure: (error) => CommitThenWriteTerminalEventResult.TerminalWriteFailed({ error }),
|
|
48
|
+
onSuccess: (writeResult) => CommitThenWriteTerminalEventResult.Committed({ writeResult })
|
|
59
49
|
}))
|
|
60
50
|
}));
|
|
61
51
|
//#endregion
|
|
62
|
-
export { commitThenWriteTerminalEvent, durableAgentEventId, makeDurableAgentEventSequencerState, sequenceDurableAgentEvent, writeDurableAgentEvent };
|
|
52
|
+
export { CommitThenWriteTerminalEventResult, commitThenWriteTerminalEvent, durableAgentEventId, makeDurableAgentEventSequencerState, sequenceDurableAgentEvent, writeDurableAgentEvent };
|
|
63
53
|
|
|
64
54
|
//# sourceMappingURL=workflow-events.mjs.map
|