workflow 5.0.0-beta.40 → 5.0.0-beta.41

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.
@@ -81,7 +81,8 @@ const event = await world.events.get(runId, eventId); // [!code highlight]
81
81
 
82
82
  ### events.list()
83
83
 
84
- List events for a run with cursor pagination.
84
+ List events for a run. Omit `pagination.limit` to return every remaining event,
85
+ or set it to return one bounded page.
85
86
 
86
87
  ```typescript lineNumbers
87
88
  const result = await world.events.list({ runId, pagination: { cursor } }); // [!code highlight]
@@ -91,8 +92,11 @@ const result = await world.events.list({ runId, pagination: { cursor } }); // [!
91
92
  |-----------|------|-------------|
92
93
  | `params.runId` | `string` | Filter events by run ID |
93
94
  | `params.pagination.cursor` | `string` | Cursor for the next page |
95
+ | `params.pagination.limit` | `number` | Maximum events to return. When omitted, returns every remaining event up to the World's event ceiling. |
96
+ | `params.pagination.sortOrder` | `"asc" \| "desc"` | Event order |
97
+ | `params.resolveData` | `"all" \| "none"` | Include or omit event payload data |
94
98
 
95
- **Returns:** `{ data: Event[], cursor?: string }`
99
+ **Returns:** `{ data: Event[], cursor: string | null, hasMore: boolean }`
96
100
 
97
101
  ### events.listByCorrelationId()
98
102
 
@@ -43,6 +43,14 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
43
43
  - Delivery attempts before a run or step is failed gracefully.
44
44
  - Can only be lowered. The default is calibrated so Workflow can record failure before the queue expires the message.
45
45
 
46
+ ### `WORKFLOW_MAX_EVENTS`
47
+
48
+ - Default: `25000`
49
+ - Positive-integer event limit reported by the Local World and enforced by the runtime as `MAX_EVENTS_EXCEEDED`.
50
+ - The Local and Postgres Worlds also use it as the maximum number of events returned when `events.list()` is called without a limit. If more events exist, the response includes `hasMore: true` and a continuation cursor.
51
+ - The Vercel World receives its event limit from the service; this environment variable does not override that service-owned value.
52
+ - Invalid or non-positive values fall back to the default.
53
+
46
54
  ### `WORKFLOW_REPLAY_DIVERGENCE_MAX_RETRIES`
47
55
 
48
56
  - Default: `3`
@@ -75,6 +83,14 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
75
83
  - As a result, 412 volume describes a workload rather than the health of a deployment, and a run that never sees one is not evidence the guard is inactive.
76
84
  - Set `0` to disable.
77
85
 
86
+ ### `WORKFLOW_SLOT_GAP_CHECK`
87
+
88
+ - Default: enabled
89
+ - A replay checks that the [event log](/docs/how-it-works/event-sourcing#event-ids) it loaded is dense before it runs, and fails the run with [`CORRUPTED_EVENT_LOG`](/docs/errors/corrupted-event-log) when a position below the log's highest holds no event. A log missing only its first position, meaning a run whose `run_created` is still being written, is left alone.
90
+ - A position can be briefly empty while the write that occupies it is still committing, so the check re-reads the log a few times before it decides, and the replay continues from whichever log it settled on.
91
+ - The check trades one failure for another. Most holes stand for an event that never happened, and replaying past those is correct. A hole standing for an event that did happen looks identical, and replaying past that one produces a run whose result is silently wrong. Failing is the recoverable side of that trade.
92
+ - Set `0` to replay across holes instead.
93
+
78
94
  ### `WORKFLOW_PRECONDITION_MAX_INPROCESS_RESTARTS`
79
95
 
80
96
  - Default: `3`
@@ -93,17 +109,6 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
93
109
  - Delay before a re-invocation caused by a rejected event creation.
94
110
  - Unlike an in-process restart, which re-reads immediately, a re-invocation only happens once the in-process budget failed to catch up — so the delay gives the other writers a moment to quiesce.
95
111
 
96
- ### `WORKFLOW_PER_KIND_CORRELATION_IDS`
97
-
98
- - Default: disabled
99
- - Experimental. Gives each kind of entity a workflow creates — steps, waits, hooks, attribute writes, abort controllers, stream IDs — its own sequence of correlation IDs.
100
- - With one sequence shared by every kind, an ID is an ordinal over the whole run, so a single extra draw of any kind shifts every ID after it. Two concurrent replays of the same run that disagree about one `sleep()` then assign different IDs to every step that follows, and each writes events the other can neither match nor consume, which fails the run with `CORRUPTED_EVENT_LOG`. Per-kind sequences confine that to the kind that actually differs.
101
- - IDs remain ordered within a kind, so hooks created by your workflow are still listed in creation order. A hook the runtime creates for you, such as the one backing an abort controller, draws from its own kind and so is listed at an arbitrary position relative to your hooks rather than at its creation position.
102
- - A run must replay under the scheme that minted its IDs. A replay that switches schemes mid-run assigns IDs its own earlier events do not carry, so it can consume none of them and the run fails.
103
- - On Vercel, a run keeps replaying on the deployment it started on, so it only ever sees the value baked into that deployment. Changing the setting affects new runs only.
104
- - Elsewhere — `@workflow/world-postgres`, `@workflow/world-local`, any self-hosted process — nothing pins a run to the code that started it. Turn the setting on during a quiet window with no runs in flight, and roll the new value out to your whole fleet at once: a rolling deploy that leaves both values live replays one run under two schemes concurrently, which is the failure the setting exists to reduce.
105
- - Set `1` to enable.
106
-
107
112
  ## Inline execution
108
113
 
109
114
  ### `WORKFLOW_V2_TIMEOUT_MS`
@@ -202,7 +207,7 @@ For example, a workflow can run a 10-minute inline step even with `WORKFLOW_REPL
202
207
 
203
208
  - Default: enabled
204
209
  - On the Vercel World, lets concurrent event-log requests share one HTTP/2 connection instead of one connection per in-flight request.
205
- - Set `0` to send one event request per connection.
210
+ - Set `0` to take the event-log requests off HTTP/2 entirely, back to one request per HTTP/1.1 connection. Use this as the kill switch if HTTP/2 turns out to be at fault for event delivery problems.
206
211
 
207
212
  ## Queue namespace
208
213
 
@@ -273,3 +273,11 @@ Platform-provided values such as `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, an
273
273
  - CLI flag: none
274
274
  - Default: `1000`
275
275
  - Maximum stream chunks written in one Vercel World request. Larger batches are split.
276
+
277
+ ### `WORKFLOW_EVENTS_TRANSPORT`
278
+
279
+ - Factory option: none
280
+ - CLI flag: none
281
+ - Default: `http`
282
+ - Experimental. Set to `ws` to ship workflow run events to the Vercel World over a WebSocket instead of one HTTP request each.
283
+ - Ignored when the World is configured with `projectConfig` and routes through the `api-workflow` proxy — that endpoint is an HTTP-only REST gateway and does not forward a WebSocket upgrade, so events stay on HTTP.
@@ -21,15 +21,18 @@ Workflow replay diverged <divergenceCount> times after <maxRecoveryReplays> reco
21
21
 
22
22
  ## Why This Happens
23
23
 
24
- Workflows persist their progress as an ordered event log. During replay, the runtime processes each event in sequence — every event must be consumed by a matching callback (e.g., a step or sleep waiting for its result). When an event has no matching consumer, the runtime cannot advance past it, which would block all subsequent events and hang the workflow indefinitely.
24
+ Workflows persist their progress as an ordered event log. During replay, the runtime processes each event in sequence — every event must be consumed by a matching callback (e.g., a step or sleep waiting for its result). An event no callback ever claims is one the runtime would have to drop to finish the run, so it fails the run instead of returning a result that silently ignored it.
25
25
 
26
- Instead of silently hanging, the runtime retries a divergent replay before failing the workflow and surfacing this terminal error.
26
+ A delivery written from outside the replay, such as a hook firing or a step completing on another invocation, can land ahead of the events the replay is writing itself. That is ordinary concurrency rather than corruption, so the runtime holds such an event and offers it to each consumer the replay registers afterwards. The failure comes only when the workflow function returns while an event is still held, at which point no consumer can ever appear. A replay that suspends still holding one reports it on the span (`workflow.events.parked.count`, `.event_id`, `.event_type`) and leaves the decision to the replay that follows.
27
+
28
+ Before failing, the runtime retries a divergent replay and surfaces this terminal error only if replay still cannot recover.
27
29
 
28
30
  Common scenarios that produce this error:
29
31
 
30
- 1. **Duplicate completion events** — Two `wait_completed` events for a single `wait_created`, or two `step_completed` events for the same step. The first is consumed normally, but the second has no consumer.
31
- 2. **Orphaned events** — A `step_completed` or `wait_completed` event whose `correlationId` doesn't match any step or sleep in the workflow code.
32
+ 1. **Duplicate completion events** — Two `wait_completed` events for a single `wait_created`, or two `step_completed` events for the same step. The first is consumed normally, and the second resolves something already resolved, so no later consumer can claim it.
33
+ 2. **Orphaned events** — A `step_completed` or `wait_completed` event whose `correlationId` doesn't match any step or sleep in the workflow code, so the replay reaches its end still holding it.
32
34
  3. **Events after terminal state** — An event that arrives after its corresponding step or wait has already reached a terminal state (e.g., `step_retrying` after `step_completed`).
35
+ 4. **A hole in the log** — Events are numbered by their position in the run's log, and those positions are dense, so a position below the log's highest that holds no event means the log the replay loaded is incomplete. The runtime cannot tell a position no write ever occupied from one whose event it failed to read, so it refuses to replay rather than produce a result that may be silently wrong. See [`WORKFLOW_SLOT_GAP_CHECK`](/docs/configuration/runtime-tuning#workflow_slot_gap_check).
33
36
 
34
37
  ## What To Do
35
38
 
@@ -260,7 +260,7 @@ On Vercel, `requestId` is the platform request ID when available. Other worlds a
260
260
 
261
261
  ## Entity IDs
262
262
 
263
- All entities in the Workflow SDK use a consistent ID format: a 4-character prefix followed by an underscore and a [ULID](https://github.com/ulid/spec) (Universally Unique Lexicographically Sortable Identifier).
263
+ All entities in the Workflow SDK use a consistent ID format: a 4-character prefix followed by an underscore and a fixed-width body. For every entity except events, that body is a [ULID](https://github.com/ulid/spec) (Universally Unique Lexicographically Sortable Identifier). An event's body is its slot number, described below.
264
264
 
265
265
  | Entity | Prefix | Example |
266
266
  |--------|--------|---------|
@@ -268,11 +268,19 @@ All entities in the Workflow SDK use a consistent ID format: a 4-character prefi
268
268
  | Step | `step_` | `step_01HXYZ123ABC456DEF789GHJ` |
269
269
  | Hook | `hook_` | `hook_01HXYZ123ABC456DEF789GHJ` |
270
270
  | Wait | `wait_` | `wait_01HXYZ123ABC456DEF789GHJ` |
271
- | Event | `evnt_` | `evnt_01HXYZ123ABC456DEF789GHJ` |
271
+ | Event | `evnt_` | `evnt_00000000000000000000000042` (slot 42) |
272
272
  | Stream | `strm_` | `strm_01HXYZ123ABC456DEF789GHJ` |
273
273
 
274
274
  **Why this format?**
275
275
 
276
276
  - **Prefixes enable introspection**: Given any ID, you can immediately identify what type of entity it refers to. This makes debugging, logging, and cross-referencing entities across the system straightforward.
277
277
 
278
- - **ULIDs enable chronological ordering**: Unlike UUIDs, ULIDs encode a timestamp in their first 48 bits, making them lexicographically sortable by creation time. This property is essential for the event log—events are always stored and retrieved in the correct chronological order simply by sorting their IDs.
278
+ - **Fixed-width bodies enable ordering**: Unlike UUIDs, these bodies sort lexicographically in creation order, so the event log is stored and retrieved in the correct order by sorting IDs alone. Slot numbers get that from counting at a fixed width, which makes string order the same as numeric order. ULIDs get it from the timestamp in their first 48 bits, which also makes a ULID's creation time recoverable from the ID itself.
279
+
280
+ ### Event IDs
281
+
282
+ An event ID is a **slot number**: the event's 1-based position in the run's event log, zero-padded to the same width as a ULID. The world assigns it when the event is published, so two writers racing to append never claim the same position and a rejected write leaves no gap behind. Slots are dense, and unique only within a run, so an event ID identifies an event only when paired with its `runId`.
283
+
284
+ Density is what lets a reader tell a complete log from an incomplete one by its length alone. A replay that loads a log with a position missing below the highest one it can see cannot tell an event that was never written from one it failed to read, so it fails the run with [`CORRUPTED_EVENT_LOG`](/docs/errors/corrupted-event-log) rather than replay across the hole. See [`WORKFLOW_SLOT_GAP_CHECK`](/docs/configuration/runtime-tuning#workflow_slot_gap_check).
285
+
286
+ A slot ID carries no timestamp. Zero-padded decimal digits are a subset of the ULID alphabet, so a slot ID passes ULID validation and sorts correctly, but decoding its first 48 bits yields the Unix epoch instead of a creation time. Read `createdAt` on the event when you need to know when it was written, and don't decode the time from an `evnt_` ID you get back from an API, a log line, or a cursor.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "workflow",
3
- "version": "5.0.0-beta.40",
3
+ "version": "5.0.0-beta.41",
4
4
  "description": "Workflow SDK - Build durable, resilient, and observable workflows",
5
5
  "main": "dist/typescript-plugin.cjs",
6
6
  "type": "module",
@@ -59,18 +59,18 @@
59
59
  },
60
60
  "dependencies": {
61
61
  "ms": "2.1.3",
62
- "@workflow/astro": "5.0.0-beta.40",
63
- "@workflow/cli": "5.0.0-beta.40",
64
- "@workflow/core": "5.0.0-beta.40",
62
+ "@workflow/astro": "5.0.0-beta.41",
63
+ "@workflow/cli": "5.0.0-beta.41",
64
+ "@workflow/core": "5.0.0-beta.41",
65
65
  "@workflow/errors": "5.0.0-beta.16",
66
66
  "@workflow/typescript-plugin": "5.0.0-beta.5",
67
67
  "@workflow/utils": "5.0.0-beta.8",
68
- "@workflow/next": "5.0.0-beta.40",
69
- "@workflow/nest": "5.0.0-beta.40",
70
- "@workflow/nitro": "5.0.0-beta.40",
71
- "@workflow/nuxt": "5.0.0-beta.40",
72
- "@workflow/sveltekit": "5.0.0-beta.40",
73
- "@workflow/rollup": "5.0.0-beta.40"
68
+ "@workflow/next": "5.0.0-beta.41",
69
+ "@workflow/nest": "5.0.0-beta.41",
70
+ "@workflow/nitro": "5.0.0-beta.41",
71
+ "@workflow/nuxt": "5.0.0-beta.41",
72
+ "@workflow/sveltekit": "5.0.0-beta.41",
73
+ "@workflow/rollup": "5.0.0-beta.41"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@types/ms": "2.1.0",