@workflow/core 5.0.0-beta.42 → 5.0.0-beta.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/capabilities.d.ts +4 -4
- package/dist/capabilities.js +5 -5
- package/dist/capture-stack.d.ts +1 -1
- package/dist/capture-stack.js +2 -2
- package/dist/class-serialization.d.ts +8 -8
- package/dist/class-serialization.js +9 -9
- package/dist/classify-error.js +5 -5
- package/dist/context-violation-error.d.ts +5 -5
- package/dist/context-violation-error.d.ts.map +1 -1
- package/dist/context-violation-error.js +5 -5
- package/dist/create-hook.d.ts +10 -6
- package/dist/create-hook.d.ts.map +1 -1
- package/dist/create-hook.js +1 -1
- package/dist/define-hook.js +2 -2
- package/dist/describe-error.d.ts +7 -7
- package/dist/describe-error.js +8 -8
- package/dist/encryption.d.ts +5 -5
- package/dist/encryption.js +7 -7
- package/dist/events-consumer.d.ts +19 -7
- package/dist/events-consumer.d.ts.map +1 -1
- package/dist/events-consumer.js +33 -12
- package/dist/flushable-stream.d.ts +4 -4
- package/dist/flushable-stream.js +12 -12
- package/dist/global.d.ts +1 -1
- package/dist/global.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/log-format.d.ts +2 -2
- package/dist/log-format.js +5 -5
- package/dist/logger.js +4 -4
- package/dist/private.d.ts +39 -64
- package/dist/private.d.ts.map +1 -1
- package/dist/private.js +294 -48
- package/dist/replay-payload-cache.d.ts +1 -1
- package/dist/replay-payload-cache.js +4 -4
- package/dist/runtime/compute-instance.d.ts +4 -3
- package/dist/runtime/compute-instance.d.ts.map +1 -1
- package/dist/runtime/compute-instance.js +5 -4
- package/dist/runtime/constants.d.ts +46 -19
- package/dist/runtime/constants.d.ts.map +1 -1
- package/dist/runtime/constants.js +76 -38
- package/dist/runtime/count-step-started-events.d.ts +7 -7
- package/dist/runtime/count-step-started-events.js +4 -4
- package/dist/runtime/deployment-guard.d.ts +7 -7
- package/dist/runtime/deployment-guard.js +8 -8
- package/dist/runtime/get-port-lazy.d.ts +2 -2
- package/dist/runtime/get-port-lazy.d.ts.map +1 -1
- package/dist/runtime/get-port-lazy.js +37 -30
- package/dist/runtime/get-world-lazy.d.ts +2 -2
- package/dist/runtime/get-world-lazy.js +3 -3
- package/dist/runtime/helpers.d.ts +18 -18
- package/dist/runtime/helpers.js +27 -27
- package/dist/runtime/quickjs-entrypoint.d.ts +23 -10
- package/dist/runtime/quickjs-entrypoint.d.ts.map +1 -1
- package/dist/runtime/quickjs-entrypoint.js +200 -81
- package/dist/runtime/quickjs-runtime.d.ts +31 -15
- package/dist/runtime/quickjs-runtime.d.ts.map +1 -1
- package/dist/runtime/quickjs-runtime.js +126 -77
- package/dist/runtime/quickjs-serde.d.ts +10 -10
- package/dist/runtime/quickjs-serde.js +20 -20
- package/dist/runtime/replay-budget.d.ts +4 -4
- package/dist/runtime/replay-budget.js +5 -5
- package/dist/runtime/replay-recovery-reporter.d.ts +1 -1
- package/dist/runtime/replay-recovery-reporter.js +2 -2
- package/dist/runtime/resume-hook.d.ts +2 -2
- package/dist/runtime/resume-hook.js +27 -27
- package/dist/runtime/resume-latency.d.ts +25 -25
- package/dist/runtime/resume-latency.d.ts.map +1 -1
- package/dist/runtime/resume-latency.js +4 -4
- package/dist/runtime/run-id-time.d.ts +2 -2
- package/dist/runtime/run-id-time.js +5 -5
- package/dist/runtime/run.d.ts +21 -4
- package/dist/runtime/run.d.ts.map +1 -1
- package/dist/runtime/run.js +154 -35
- package/dist/runtime/runs.d.ts +4 -4
- package/dist/runtime/runs.js +2 -2
- package/dist/runtime/start.d.ts +3 -3
- package/dist/runtime/start.d.ts.map +1 -1
- package/dist/runtime/start.js +27 -23
- package/dist/runtime/step-executor.d.ts +59 -11
- package/dist/runtime/step-executor.d.ts.map +1 -1
- package/dist/runtime/step-executor.js +116 -42
- package/dist/runtime/step-latency.d.ts +20 -14
- package/dist/runtime/step-latency.d.ts.map +1 -1
- package/dist/runtime/step-latency.js +13 -11
- package/dist/runtime/step-ownership.d.ts +6 -6
- package/dist/runtime/step-ownership.js +8 -8
- package/dist/runtime/step-single-flight.d.ts +1 -1
- package/dist/runtime/step-single-flight.d.ts.map +1 -1
- package/dist/runtime/step-single-flight.js +17 -11
- package/dist/runtime/suspension-handler.d.ts +99 -7
- package/dist/runtime/suspension-handler.d.ts.map +1 -1
- package/dist/runtime/suspension-handler.js +678 -57
- package/dist/runtime/unserializable-step.d.ts +34 -0
- package/dist/runtime/unserializable-step.d.ts.map +1 -0
- package/dist/runtime/unserializable-step.js +52 -0
- package/dist/runtime/vm-mode.d.ts +1 -1
- package/dist/runtime/vm-mode.js +2 -2
- package/dist/runtime/wait-continuation.d.ts +31 -9
- package/dist/runtime/wait-continuation.d.ts.map +1 -1
- package/dist/runtime/wait-continuation.js +44 -13
- package/dist/runtime/wait-until.d.ts +1 -1
- package/dist/runtime/wait-until.js +2 -2
- package/dist/runtime/world-compatibility.d.ts +20 -9
- package/dist/runtime/world-compatibility.d.ts.map +1 -1
- package/dist/runtime/world-compatibility.js +24 -13
- package/dist/runtime/world-init.d.ts +5 -5
- package/dist/runtime/world-init.js +6 -6
- package/dist/runtime/world.d.ts.map +1 -1
- package/dist/runtime/world.js +13 -3
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +417 -213
- package/dist/sealed-box.d.ts +7 -7
- package/dist/sealed-box.js +15 -15
- package/dist/serialization/client.js +2 -2
- package/dist/serialization/codec-devalue-vm.d.ts +1 -1
- package/dist/serialization/codec-devalue-vm.js +4 -4
- package/dist/serialization/codec-devalue.d.ts +1 -1
- package/dist/serialization/codec-devalue.js +2 -2
- package/dist/serialization/codec.d.ts +4 -4
- package/dist/serialization/codec.js +2 -2
- package/dist/serialization/compression.d.ts +12 -11
- package/dist/serialization/compression.d.ts.map +1 -1
- package/dist/serialization/compression.js +16 -15
- package/dist/serialization/encryption.d.ts +7 -7
- package/dist/serialization/encryption.d.ts.map +1 -1
- package/dist/serialization/encryption.js +8 -8
- package/dist/serialization/errors.js +2 -2
- package/dist/serialization/format.d.ts +6 -6
- package/dist/serialization/format.js +7 -7
- package/dist/serialization/hardened.d.ts +18 -18
- package/dist/serialization/hardened.d.ts.map +1 -1
- package/dist/serialization/hardened.js +41 -32
- package/dist/serialization/index.d.ts +1 -1
- package/dist/serialization/index.js +2 -2
- package/dist/serialization/reducers/class-vm.d.ts +1 -1
- package/dist/serialization/reducers/class-vm.js +2 -2
- package/dist/serialization/reducers/class.js +2 -2
- package/dist/serialization/reducers/common-vm.js +12 -12
- package/dist/serialization/reducers/common.js +12 -12
- package/dist/serialization/reducers/step-function-vm.d.ts +1 -1
- package/dist/serialization/reducers/step-function-vm.js +2 -2
- package/dist/serialization/reducers/step-function.js +2 -2
- package/dist/serialization/step.js +2 -2
- package/dist/serialization/types.d.ts +5 -5
- package/dist/serialization/types.js +2 -2
- package/dist/serialization/workflow-vm.d.ts +1 -1
- package/dist/serialization/workflow-vm.d.ts.map +1 -1
- package/dist/serialization/workflow-vm.js +13 -10
- package/dist/serialization-format.d.ts +11 -11
- package/dist/serialization-format.d.ts.map +1 -1
- package/dist/serialization-format.js +29 -23
- package/dist/serialization.d.ts +20 -12
- package/dist/serialization.d.ts.map +1 -1
- package/dist/serialization.js +141 -99
- package/dist/set-attributes.js +2 -2
- package/dist/source-map.d.ts +1 -1
- package/dist/source-map.d.ts.map +1 -1
- package/dist/source-map.js +18 -12
- package/dist/step/context-storage.d.ts +5 -5
- package/dist/step/context-storage.js +1 -1
- package/dist/step/writable-stream.d.ts +3 -3
- package/dist/step/writable-stream.js +7 -7
- package/dist/step.js +12 -12
- package/dist/symbols.d.ts +2 -2
- package/dist/symbols.js +3 -3
- package/dist/telemetry/semantic-conventions.d.ts +21 -13
- package/dist/telemetry/semantic-conventions.d.ts.map +1 -1
- package/dist/telemetry/semantic-conventions.js +21 -15
- package/dist/telemetry.d.ts +9 -3
- package/dist/telemetry.d.ts.map +1 -1
- package/dist/telemetry.js +40 -10
- package/dist/test-support/orchestrator-context.d.ts.map +1 -1
- package/dist/test-support/orchestrator-context.js +12 -2
- package/dist/version.d.ts +1 -1
- package/dist/version.js +2 -2
- package/dist/vm/index.js +4 -4
- package/dist/vm/script-cache.d.ts.map +1 -1
- package/dist/vm/script-cache.js +23 -17
- package/dist/vm/uint8array-base64.js +1 -1
- package/dist/workflow/abort-controller.d.ts +1 -1
- package/dist/workflow/abort-controller.js +12 -12
- package/dist/workflow/create-hook.js +3 -3
- package/dist/workflow/get-workflow-metadata.js +2 -2
- package/dist/workflow/hook.js +18 -18
- package/dist/workflow/set-attributes.d.ts +1 -1
- package/dist/workflow/set-attributes.js +2 -2
- package/dist/workflow/sleep.js +2 -2
- package/dist/workflow/world-init-stub.d.ts +1 -1
- package/dist/workflow/world-init-stub.js +2 -2
- package/dist/workflow.d.ts +2 -2
- package/dist/workflow.js +36 -14
- package/docs/api-reference/create-hook.mdx +15 -15
- package/docs/api-reference/create-webhook.mdx +15 -15
- package/docs/api-reference/define-hook.mdx +10 -10
- package/docs/api-reference/fatal-error.mdx +2 -2
- package/docs/api-reference/fetch.mdx +7 -7
- package/docs/api-reference/get-step-metadata.mdx +2 -2
- package/docs/api-reference/get-workflow-metadata.mdx +3 -3
- package/docs/api-reference/get-writable.mdx +7 -7
- package/docs/api-reference/index.mdx +1 -1
- package/docs/api-reference/retryable-error.mdx +1 -1
- package/docs/api-reference/set-attributes.mdx +2 -2
- package/docs/api-reference/sleep.mdx +3 -3
- package/docs/foundations/cancellation.mdx +31 -31
- package/docs/foundations/errors-and-retries.mdx +42 -11
- package/docs/foundations/hooks.mdx +37 -37
- package/docs/foundations/idempotency.mdx +9 -9
- package/docs/foundations/serialization.mdx +21 -22
- package/docs/foundations/starting-workflows.mdx +36 -37
- package/docs/foundations/streaming.mdx +68 -39
- package/docs/foundations/versioning.mdx +3 -3
- package/docs/foundations/workflows-and-steps.mdx +9 -9
- package/docs/how-it-works/cancellation.mdx +62 -62
- package/docs/how-it-works/code-transform.mdx +66 -54
- package/docs/how-it-works/encryption.mdx +25 -21
- package/docs/how-it-works/event-sourcing.mdx +84 -28
- package/docs/how-it-works/framework-integrations.mdx +12 -12
- package/docs/how-it-works/understanding-directives.mdx +21 -21
- package/package.json +7 -7
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: Event
|
|
2
|
+
title: Event sourcing
|
|
3
3
|
description: Learn how Workflow SDK uses event sourcing internally for debugging and observability.
|
|
4
4
|
type: conceptual
|
|
5
5
|
summary: Understand the event log that powers workflow replay and debugging.
|
|
@@ -10,14 +10,12 @@ related:
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
<Callout>
|
|
13
|
-
|
|
13
|
+
Workflow SDK uses event sourcing internally for debugging and observability tools. To start using workflows, see the [getting started](/docs/getting-started) guide for your framework.
|
|
14
14
|
</Callout>
|
|
15
15
|
|
|
16
16
|
The Workflow SDK uses event sourcing to track all state changes in workflow executions. Every mutation creates an event that is persisted to the event log, and entity state is derived by replaying these events.
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
## Event Sourcing Overview
|
|
18
|
+
## Event sourcing overview
|
|
21
19
|
|
|
22
20
|
Event sourcing is a persistence pattern where state changes are stored as a sequence of events rather than by updating records in place. The current state of any entity is reconstructed by replaying its events from the beginning.
|
|
23
21
|
|
|
@@ -35,15 +33,15 @@ In the Workflow SDK, the following entity types are managed through events:
|
|
|
35
33
|
- **Hooks**: Suspension points that can receive external data (materialized in storage)
|
|
36
34
|
- **Waits**: Sleep or delay operations (materialized in storage)
|
|
37
35
|
|
|
38
|
-
## Entity
|
|
36
|
+
## Entity lifecycles
|
|
39
37
|
|
|
40
|
-
Each entity type follows a specific lifecycle defined by the events that can affect it. Events transition entities between states
|
|
38
|
+
Each entity type follows a specific lifecycle defined by the events that can affect it. Events transition entities between states. Once an entity reaches a terminal state, no further transitions are possible.
|
|
41
39
|
|
|
42
40
|
<Callout type="info">
|
|
43
41
|
In the diagrams below, <span style={{color: '#8b5cf6', fontWeight: 'bold'}}>purple nodes</span> indicate terminal states that cannot be transitioned out of.
|
|
44
42
|
</Callout>
|
|
45
43
|
|
|
46
|
-
### Run
|
|
44
|
+
### Run lifecycle
|
|
47
45
|
|
|
48
46
|
A run represents a single execution of a workflow function. Runs begin in `pending` state when created, transition to `running` when execution starts, and end in one of three terminal states.
|
|
49
47
|
|
|
@@ -67,9 +65,9 @@ flowchart TD
|
|
|
67
65
|
- `running`: Actively executing workflow code
|
|
68
66
|
- `completed`: Finished successfully with an output value
|
|
69
67
|
- `failed`: Terminated due to an unrecoverable error
|
|
70
|
-
- `cancelled`: Explicitly
|
|
68
|
+
- `cancelled`: Explicitly canceled by the user or system
|
|
71
69
|
|
|
72
|
-
### Step
|
|
70
|
+
### Step lifecycle
|
|
73
71
|
|
|
74
72
|
A step represents a single invocation of a step function. Steps can retry on failure, either transitioning back to `pending` via `step_retrying` or being re-executed directly with another `step_started` event.
|
|
75
73
|
|
|
@@ -94,7 +92,7 @@ flowchart TD
|
|
|
94
92
|
- `cancelled`: Reserved for future use (not currently emitted)
|
|
95
93
|
|
|
96
94
|
<Callout type="info">
|
|
97
|
-
The `step_retrying` event is optional. Steps can retry without it
|
|
95
|
+
The `step_retrying` event is optional. Steps can retry without it, regardless of whether the event is emitted. You may see consecutive `step_started` events when a step retries after a timeout, when the error isn't explicitly captured, or when concurrent replays each commit one. See [Duplicate events](#duplicate-events) and [Errors and retries](/docs/foundations/errors-and-retries) for more information.
|
|
98
96
|
</Callout>
|
|
99
97
|
|
|
100
98
|
When present, the `step_retrying` event moves a step back to `pending` state and records the error that caused the retry. This provides two benefits:
|
|
@@ -102,7 +100,7 @@ When present, the `step_retrying` event moves a step back to `pending` state and
|
|
|
102
100
|
- **Cleaner observability**: The event log explicitly shows retry transitions rather than consecutive `step_started` events
|
|
103
101
|
- **Error history**: The error that triggered the retry is preserved for debugging
|
|
104
102
|
|
|
105
|
-
### Hook
|
|
103
|
+
### Hook lifecycle
|
|
106
104
|
|
|
107
105
|
A hook represents a suspension point that can receive external data, created by [`createHook()`](/docs/api-reference/workflow/create-hook). Hooks enable workflows to pause and wait for external events, user interactions, or HTTP requests. Webhooks (created with [`createWebhook()`](/docs/api-reference/workflow/create-webhook)) are a higher-level abstraction built on hooks that adds automatic HTTP request/response handling.
|
|
108
106
|
|
|
@@ -125,15 +123,15 @@ flowchart TD
|
|
|
125
123
|
- `disposed`: No longer accepting payloads
|
|
126
124
|
- `conflicted`: Hook creation failed because the token is already in use by another workflow
|
|
127
125
|
|
|
128
|
-
Unlike other entities, hooks don't have a `status` field
|
|
126
|
+
Unlike other entities, hooks don't have a `status` field. The states above are conceptual. When a `hook_disposed` event is created, the hook record is removed rather than updated.
|
|
129
127
|
|
|
130
|
-
While a hook is active, its token is reserved and cannot be used by other workflows. If a workflow attempts to create a hook with a token reserved by another run
|
|
128
|
+
While a hook is active, its token is reserved and cannot be used by other workflows. If a workflow attempts to create a hook with a token reserved by another run, either by an active hook or by `experimental_minRetention` after its run ended, a `hook_conflict` event is recorded instead of `hook_created`. Current Worlds include the token and the run ID that owns it, though older persisted events or World implementations may only include the token. This causes `hook.getConflict()` to resolve with the conflicting run and the hook's payload promise to reject with a `HookConflictError`, which you can detect with `HookConflictError.is(error)`. See the [hook-conflict error](/docs/errors/hook-conflict) documentation for more details.
|
|
131
129
|
|
|
132
|
-
When a workflow ends, its
|
|
130
|
+
When a workflow ends, its hooks can no longer be resumed. They are normally removed, and their tokens become available again. With `experimental_minRetention`, a hook remains readable, and its token remains unavailable until retention ends. A `hook_disposed` event removes the hook and makes its token available immediately.
|
|
133
131
|
|
|
134
|
-
See [Hooks
|
|
132
|
+
See [Hooks and webhooks](/docs/foundations/hooks) for more on how hooks and webhooks work.
|
|
135
133
|
|
|
136
|
-
### Wait
|
|
134
|
+
### Wait lifecycle
|
|
137
135
|
|
|
138
136
|
A wait represents a sleep operation created by [`sleep()`](/docs/api-reference/workflow/sleep). Waits track when a delay period has elapsed.
|
|
139
137
|
|
|
@@ -151,10 +149,10 @@ flowchart TD
|
|
|
151
149
|
- `completed`: Delay period has elapsed, workflow can resume
|
|
152
150
|
|
|
153
151
|
<Callout type="info">
|
|
154
|
-
Like
|
|
152
|
+
Like runs, steps, and hooks, waits are materialized as entities in storage. Processing a `wait_created` event creates a wait entity with the `waiting` status. Processing a `wait_completed` event atomically transitions the wait entity to `completed`. This process guarantees that a wait can only be completed once, even if multiple concurrent invocations attempt to complete it simultaneously.
|
|
155
153
|
</Callout>
|
|
156
154
|
|
|
157
|
-
## Event
|
|
155
|
+
## Event types reference
|
|
158
156
|
|
|
159
157
|
Events are categorized by the entity type they affect. Each event contains metadata including a timestamp and a `correlationId` that links the event to a specific entity:
|
|
160
158
|
|
|
@@ -163,7 +161,7 @@ Events are categorized by the entity type they affect. Each event contains metad
|
|
|
163
161
|
- Wait events use the `waitId` as the correlation ID
|
|
164
162
|
- Run events do not require a correlation ID since the `runId` itself identifies the entity
|
|
165
163
|
|
|
166
|
-
### Run
|
|
164
|
+
### Run events
|
|
167
165
|
|
|
168
166
|
| Event | Description |
|
|
169
167
|
|-------|-------------|
|
|
@@ -173,7 +171,7 @@ Events are categorized by the entity type they affect. Each event contains metad
|
|
|
173
171
|
| `run_failed` | Transitions the run to `failed` state with error details and optional error code. |
|
|
174
172
|
| `run_cancelled` | Transitions the run to `cancelled` state. Can be triggered from `pending` or `running` states. |
|
|
175
173
|
|
|
176
|
-
### Step
|
|
174
|
+
### Step events
|
|
177
175
|
|
|
178
176
|
| Event | Description |
|
|
179
177
|
|-------|-------------|
|
|
@@ -183,7 +181,7 @@ Events are categorized by the entity type they affect. Each event contains metad
|
|
|
183
181
|
| `step_failed` | Transitions the step to `failed` state with error details. The step will not be retried. |
|
|
184
182
|
| `step_retrying` | (Optional) Transitions the step back to `pending` state for retry. Contains the error that caused the retry and optional delay before the next attempt. When not emitted, retries appear as consecutive `step_started` events. |
|
|
185
183
|
|
|
186
|
-
### Hook
|
|
184
|
+
### Hook events
|
|
187
185
|
|
|
188
186
|
| Event | Description |
|
|
189
187
|
|-------|-------------|
|
|
@@ -192,14 +190,20 @@ Events are categorized by the entity type they affect. Each event contains metad
|
|
|
192
190
|
| `hook_received` | Records that a payload was delivered to the hook. The hook remains `active` and can receive more payloads. |
|
|
193
191
|
| `hook_disposed` | Deletes the hook from storage (conceptually transitioning to `disposed` state). The token is released for reuse by future workflows. |
|
|
194
192
|
|
|
195
|
-
### Wait
|
|
193
|
+
### Wait events
|
|
196
194
|
|
|
197
195
|
| Event | Description |
|
|
198
196
|
|-------|-------------|
|
|
199
197
|
| `wait_created` | Creates a new wait in `waiting` state. Contains the timestamp when the wait should complete. |
|
|
200
198
|
| `wait_completed` | Transitions the wait to `completed` state when the delay period has elapsed. |
|
|
201
199
|
|
|
202
|
-
|
|
200
|
+
### System events
|
|
201
|
+
|
|
202
|
+
| Event | Description |
|
|
203
|
+
|-------|-------------|
|
|
204
|
+
| `noop` | Seals an abandoned log position (`specVersion` 7 and above). Only the backend writes this event; the create endpoints reject it. See [Sealed positions](#sealed-positions-noop-events). |
|
|
205
|
+
|
|
206
|
+
## Terminal states
|
|
203
207
|
|
|
204
208
|
Terminal states represent the end of an entity's lifecycle. Once an entity reaches a terminal state, no further events can transition it to another state.
|
|
205
209
|
|
|
@@ -207,7 +211,7 @@ Terminal states represent the end of an entity's lifecycle. Once an entity reach
|
|
|
207
211
|
|
|
208
212
|
- `completed`: Workflow finished successfully
|
|
209
213
|
- `failed`: Workflow encountered an unrecoverable error
|
|
210
|
-
- `cancelled`: Workflow was explicitly
|
|
214
|
+
- `cancelled`: Workflow was explicitly canceled
|
|
211
215
|
|
|
212
216
|
**Step terminal states:**
|
|
213
217
|
|
|
@@ -225,7 +229,59 @@ Terminal states represent the end of an entity's lifecycle. Once an entity reach
|
|
|
225
229
|
|
|
226
230
|
Attempting to create an event that would transition an entity out of a terminal state will result in an error. This prevents inconsistent state and ensures the integrity of the event log.
|
|
227
231
|
|
|
228
|
-
|
|
232
|
+
That guard sits on the write path. Replay handles duplicates that the write path permits, such as a second `step_created` for a step that isn't terminal.
|
|
233
|
+
|
|
234
|
+
## Duplicate events
|
|
235
|
+
|
|
236
|
+
Concurrent invocations replaying the same run share one event log. An invocation working from a stale prefix that predates another invocation's write can commit its own `step_created`, `step_started`, or `wait_created` for an entity that already has one in the log. These writes pass the terminal-state guard, so the write path commits them even when a backend validates transitions atomically with the insert.
|
|
237
|
+
|
|
238
|
+
Those duplicates are committed but inert. The outcome was decided by the first event of its kind at a lower position in the log, and every replay reads that same event at that same position, so a later copy cannot change what the workflow observes.
|
|
239
|
+
|
|
240
|
+
To prevent an inert copy from failing an otherwise healthy run, the runtime groups event types into **classes**. For each entity, it tracks which classes the current replay has consumed. If no registered consumer accepts an event and its class is already recorded for that entity, replay skips it instead of reporting a [replay divergence](/docs/errors/replay-divergence). After exhausting the recovery budget, a replay divergence ends the run with [`CORRUPTED_EVENT_LOG`](/docs/errors/corrupted-event-log).
|
|
241
|
+
|
|
242
|
+
| Class | Event types |
|
|
243
|
+
|-------|-------------|
|
|
244
|
+
| `run_started` | `run_started` |
|
|
245
|
+
| `step_created` | `step_created` |
|
|
246
|
+
| `step_started` | `step_started` |
|
|
247
|
+
| `step_retrying` | `step_retrying` |
|
|
248
|
+
| `step_terminal` | `step_completed`, `step_failed` |
|
|
249
|
+
| `wait_created` | `wait_created` |
|
|
250
|
+
| `wait_completed` | `wait_completed` |
|
|
251
|
+
| `hook_created` | `hook_created` |
|
|
252
|
+
| `hook_disposed` | `hook_disposed` |
|
|
253
|
+
|
|
254
|
+
Types that share a class are the mutually exclusive outcomes of one decision. A step either completes or fails, and the first recorded outcome counts. Classes are independent, so skipping one doesn't suppress another. A step whose result is already in the log has still recorded exactly one `step_created`, making a second one independently ignorable.
|
|
255
|
+
|
|
256
|
+
The two hook classes cover the same shape of duplicate, and replay reaches them less often because the write path resolves most hook duplicates before they reach the log: a run re-creating a hook it already owns converges on the existing `hook_created` rather than appending a second one, and a second `hook_disposed` for the same hook is refused as an idempotent no-op. A log that holds either anyway is read past like any other repeat.
|
|
257
|
+
|
|
258
|
+
The remaining event types belong to no class and are never skipped:
|
|
259
|
+
|
|
260
|
+
- `hook_received`: a hook legitimately receives many payloads under one ID, so a second `hook_received` is not a repeat of anything.
|
|
261
|
+
- `hook_conflict`: records a failed acquisition of a hook's token, which the same run can hit repeatedly over its lifetime as other runs take and release that token. The hook's own consumer stays registered and claims every copy it is offered, so a repeat is consumed rather than reaching the class check.
|
|
262
|
+
- `attr_set`: written on every [`setAttributes()`](/docs/api-reference/workflow/set-attributes) call, so a second write of the same key is a new fact rather than a repeat.
|
|
263
|
+
- `run_created` precedes every replay and is always consumed.
|
|
264
|
+
- `run_completed`, `run_failed`, and `run_cancelled` never reach the check. The runtime exits before replaying the workflow body once the log holds one of them, so no consumer ever takes one and no class is ever recorded for them.
|
|
265
|
+
|
|
266
|
+
Both kinds of skip are logged at `debug`, so neither reaches the console unless you run with `DEBUG=workflow:runtime:*`. A duplicate is a permanent part of the log. Every later replay reads it and reaches the same check, so an unconditional message would print once per replay for the life of the run without requiring action. A repeat that decides a class differently, such as a `step_failed` after a `step_completed` or the reverse, gets its own message. Unlike a recommit of the same outcome, both writers cannot be correct.
|
|
267
|
+
|
|
268
|
+
The observability UI grays out events it can identify this way and shows the reason on hover. Its set is narrower than the runtime's because it reads the log without consumer state. A consumer for an entity that is still open can legitimately claim a repeat because each step retry writes another `step_started`. The UI marks a repeat only when no consumer can remain for it: after a terminal event for the same entity or at a second `run_started`, of which the log records one per run. The UI marks nothing on a partial log view, such as one page of a paginated list or search results, because identifying the first copy requires the entire log.
|
|
269
|
+
|
|
270
|
+
## Sealed positions (noop events)
|
|
271
|
+
|
|
272
|
+
Runs at `specVersion` 7 and above use a *sealed log*. The backend gives each write its position from a per-run sequencer **before** the write commits, so concurrent writers hold distinct positions and never race for a slot. New runs use this behavior by default. [`WORKFLOW_SEALED_LOG=0`](/docs/configuration/runtime-tuning#workflow_sealed_log) returns a deployment to the previous scheme. Every runtime reads a sealed log regardless, and a run's version is fixed at creation, so changing the setting never affects an in-flight run. However, a writer can claim a position and then stop because of a crashed process or canceled transaction. This leaves a hole that no writer will fill, and a hole looks like an event the reader failed to load.
|
|
273
|
+
|
|
274
|
+
The backend restores the dense log at read time by **sealing** these positions. Once a hole is provably abandoned, bounded by the commit time of later positions, the backend writes a `noop` event into it. Positions are assigned in order, so a committed later position proves how long the hole has been open. A `noop` occupies its position, and length-based completeness checks, cursors, and pagination all count it. It has no other effect:
|
|
275
|
+
|
|
276
|
+
- It is **never offered to any consumer** during replay. The walk steps over it in the same synchronous pass that delivers the events around it, so its presence cannot perturb delivery order, promise scheduling, or which branch of a `Promise.all` resumes first.
|
|
277
|
+
- It **never advances the deterministic clock**. A `noop`'s `createdAt` is the *sealer's* wall clock and can postdate events at higher positions. Allowing it to feed the replay clock would make a log containing a seal replay differently from one whose original writer filled the hole. This behavior uses the same rule and mechanism as skipped duplicates.
|
|
278
|
+
- Its `correlationId` is `noop_` followed by the sealed position's zero-padded digits. This deterministic format ensures that two sealers racing for the same hole create an identical event that is recognizable in the log.
|
|
279
|
+
|
|
280
|
+
A sealed position races its original writer at the same uniqueness fence as every other write. Losing that race means the real event landed first, so readers receive it instead. A live writer that gets sealed over derives a new position and commits there, using the same recovery as any other lost write race. Sealing can require a retry but cannot produce an incorrect log.
|
|
281
|
+
|
|
282
|
+
`noop` is not user-creatable: it does not exist in the create schemas, and backends reject it on every create endpoint. Only a backend's own read path writes one.
|
|
283
|
+
|
|
284
|
+
## Event correlation
|
|
229
285
|
|
|
230
286
|
Events use a `correlationId` to link related events together. For step, hook, and wait events, the correlation ID identifies the specific entity instance:
|
|
231
287
|
|
|
@@ -241,7 +297,7 @@ This correlation enables:
|
|
|
241
297
|
- Building timelines of entity lifecycle transitions
|
|
242
298
|
- Debugging by tracing the complete history of any entity
|
|
243
299
|
|
|
244
|
-
### Request ID
|
|
300
|
+
### Request ID correlation
|
|
245
301
|
|
|
246
302
|
Some `World` implementations also attach a `requestId` to events for platform-log correlation. This is different from `correlationId`:
|
|
247
303
|
|
|
@@ -273,7 +329,7 @@ All entities in the Workflow SDK use a consistent ID format: a 4-character prefi
|
|
|
273
329
|
|
|
274
330
|
**Why this format?**
|
|
275
331
|
|
|
276
|
-
- **Prefixes enable introspection**: Given any ID, you can immediately identify what type of entity it refers to. This
|
|
332
|
+
- **Prefixes enable introspection**: Given any ID, you can immediately identify what type of entity it refers to. This helps you debug, log, and cross-reference entities across the system.
|
|
277
333
|
|
|
278
334
|
- **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
335
|
|
|
@@ -10,7 +10,7 @@ related:
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
12
|
<Callout>
|
|
13
|
-
**For users:** If you
|
|
13
|
+
**For users:** If you want to use Workflow SDK with an existing framework, see [Getting Started](/docs/getting-started). This page is for framework authors.
|
|
14
14
|
</Callout>
|
|
15
15
|
|
|
16
16
|
This guide uses Bun as a concrete example, but the same build and routing model applies to other JavaScript frameworks and runtimes.
|
|
@@ -48,7 +48,7 @@ Steps do not have their own HTTP route. A queued step invocation contains `stepI
|
|
|
48
48
|
|
|
49
49
|
### 1. Generate the bundles
|
|
50
50
|
|
|
51
|
-
The standalone CLI scans `workflows/` and creates the combined flow handler, an internal step registration module, and the webhook handler.
|
|
51
|
+
The standalone Workflow CLI scans `workflows/` and creates the combined flow handler, an internal step registration module, and the webhook handler.
|
|
52
52
|
|
|
53
53
|
```json title="package.json"
|
|
54
54
|
{
|
|
@@ -60,17 +60,17 @@ The standalone CLI scans `workflows/` and creates the combined flow handler, an
|
|
|
60
60
|
|
|
61
61
|
The default output is:
|
|
62
62
|
|
|
63
|
-
- `/.well-known/workflow/v1/flow.mjs
|
|
64
|
-
- `/.well-known/workflow/v1/__step_registrations.mjs
|
|
65
|
-
- `/.well-known/workflow/v1/webhook.mjs
|
|
63
|
+
- `/.well-known/workflow/v1/flow.mjs`: the combined workflow and step queue consumer
|
|
64
|
+
- `/.well-known/workflow/v1/__step_registrations.mjs`: an internal module imported by `flow.mjs`; do not route to it
|
|
65
|
+
- `/.well-known/workflow/v1/webhook.mjs`: webhook delivery
|
|
66
66
|
|
|
67
67
|
<Callout>
|
|
68
68
|
Production integrations should extend `BaseBuilder` from `@workflow/builders` so they can participate in the framework's build, watch, and routing lifecycle.
|
|
69
69
|
</Callout>
|
|
70
70
|
|
|
71
|
-
### 2. Add the
|
|
71
|
+
### 2. Add the app-code transform
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
Applying the step-mode transform to application code gives it the workflow IDs used by `start()` and prevents accidental direct workflow execution. (Earlier releases used a separate `client` mode for this; it merged into `step` in 5.0.)
|
|
74
74
|
|
|
75
75
|
{/* @skip-typecheck: incomplete code sample */}
|
|
76
76
|
```typescript title="workflow-plugin.ts" lineNumbers
|
|
@@ -91,7 +91,7 @@ plugin({
|
|
|
91
91
|
jsc: {
|
|
92
92
|
experimental: {
|
|
93
93
|
plugins: [
|
|
94
|
-
[require.resolve("@workflow/swc-plugin"), { mode: "
|
|
94
|
+
[require.resolve("@workflow/swc-plugin"), { mode: "step" }],
|
|
95
95
|
],
|
|
96
96
|
},
|
|
97
97
|
},
|
|
@@ -149,10 +149,10 @@ console.log(`Server listening on http://localhost:${server.port}`);
|
|
|
149
149
|
|
|
150
150
|
The handler consumes every workflow queue message. Depending on the payload and event log, it can:
|
|
151
151
|
|
|
152
|
-
-
|
|
153
|
-
-
|
|
154
|
-
-
|
|
155
|
-
-
|
|
152
|
+
- Start or replay workflow orchestration in the sandboxed VM.
|
|
153
|
+
- Execute a queued step in the host runtime.
|
|
154
|
+
- Continue replay inline after a step completes.
|
|
155
|
+
- Resume a run after a hook, webhook, sleep, retry, or recovery event.
|
|
156
156
|
|
|
157
157
|
### Webhook endpoint
|
|
158
158
|
|
|
@@ -21,7 +21,7 @@ This page explores how directives enable this execution model and the design pri
|
|
|
21
21
|
|
|
22
22
|
To understand how directives work, let's first understand what workflows and steps are in the Workflow SDK.
|
|
23
23
|
|
|
24
|
-
## Workflows and
|
|
24
|
+
## Workflows and steps primer
|
|
25
25
|
|
|
26
26
|
The Workflow SDK has two types of functions:
|
|
27
27
|
|
|
@@ -58,7 +58,7 @@ export async function onboardUser(userId: string) {
|
|
|
58
58
|
**The key insight:** Workflows resume from suspension by replaying their code using cached step results from the [event log](/docs/how-it-works/event-sourcing). When a step like `await fetchUserData(userId)` is called:
|
|
59
59
|
|
|
60
60
|
- **If already executed:** Returns the cached result immediately from the event log
|
|
61
|
-
- **If not yet executed:** Suspends the workflow,
|
|
61
|
+
- **If not yet executed:** Suspends the workflow, executes the step (usually inline in the same invocation, or through the queue when the invocation runs out of inline budget or nears its timeout), then resumes with the result
|
|
62
62
|
|
|
63
63
|
This replay mechanism requires deterministic code. If `Math.random()` weren't seeded, the first execution might return `0.7` (sending the email) but replay might return `0.3` (skipping it), thus breaking resumption. The Workflow SDK sandbox provides seeded `Math.random()` and `Date` to ensure consistent behavior across replays.
|
|
64
64
|
|
|
@@ -66,9 +66,9 @@ This replay mechanism requires deterministic code. If `Math.random()` weren't se
|
|
|
66
66
|
For a deeper dive into workflows and steps, see [Workflows and Steps](/docs/foundations/workflows-and-steps).
|
|
67
67
|
</Callout>
|
|
68
68
|
|
|
69
|
-
## The
|
|
69
|
+
## The core challenge
|
|
70
70
|
|
|
71
|
-
This execution model
|
|
71
|
+
This execution model provides durability: workflows can suspend for days, survive restarts, and resume from any point. However, it also requires a semantic boundary in the code that tells **the compiler, runtime, and developer** that execution semantics have changed.
|
|
72
72
|
|
|
73
73
|
The challenge: how do we mark this boundary in a way that:
|
|
74
74
|
|
|
@@ -77,7 +77,7 @@ The challenge: how do we mark this boundary in a way that:
|
|
|
77
77
|
3. Allows static analysis of workflow structure
|
|
78
78
|
4. Feels natural to JavaScript developers
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Directives have prior uses, and we considered several alternatives:
|
|
81
81
|
|
|
82
82
|
## Prior art on directives
|
|
83
83
|
|
|
@@ -104,11 +104,11 @@ The `"use workflow"` directive is also used by the Language Server Plugin shippe
|
|
|
104
104
|
|
|
105
105
|
But we didn't get here immediately. This took some discovery to arrive at:
|
|
106
106
|
|
|
107
|
-
## Alternatives
|
|
107
|
+
## Alternatives we explored
|
|
108
108
|
|
|
109
|
-
Before settling on directives, we prototyped several other approaches. Each had
|
|
109
|
+
Before settling on directives, we prototyped several other approaches. Each had limitations that made them unsuitable for production use.
|
|
110
110
|
|
|
111
|
-
### Runtime-
|
|
111
|
+
### Runtime-only "Suspense" API
|
|
112
112
|
|
|
113
113
|
Our first proof of concept used a wrapper-based API without a build step:
|
|
114
114
|
|
|
@@ -140,7 +140,7 @@ export const myWorkflow = workflow(async () => {
|
|
|
140
140
|
});
|
|
141
141
|
```
|
|
142
142
|
|
|
143
|
-
This was verbose and
|
|
143
|
+
This was verbose and developers could forget it. If a developer forgot to wrap something like `Date.now()`, it led to unstable runtime behavior.
|
|
144
144
|
|
|
145
145
|
For example:
|
|
146
146
|
|
|
@@ -200,7 +200,7 @@ export const myWorkflow = workflow(async () => {
|
|
|
200
200
|
});
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
### Generator-
|
|
203
|
+
### Generator-based API
|
|
204
204
|
|
|
205
205
|
We explored using generators for explicit suspension points, inspired by libraries like Effect.ts:
|
|
206
206
|
|
|
@@ -220,7 +220,7 @@ We're big fans of [Effect.ts](https://effect.website/) and the power of generato
|
|
|
220
220
|
|
|
221
221
|
**1. Syntax felt more like a DSL than JavaScript**
|
|
222
222
|
|
|
223
|
-
Generators require a custom mental model that differs
|
|
223
|
+
Generators require a custom mental model that differs from familiar async/await patterns. The `yield*` syntax and generator delegation were unfamiliar to many developers:
|
|
224
224
|
|
|
225
225
|
{/* @skip-typecheck: incomplete code sample */}
|
|
226
226
|
```typescript lineNumbers
|
|
@@ -267,7 +267,7 @@ export const myWorkflow = workflow(function*() {
|
|
|
267
267
|
|
|
268
268
|
The generator syntax addressed suspension but didn't solve the fundamental sandboxing problem.
|
|
269
269
|
|
|
270
|
-
### File
|
|
270
|
+
### File system-based conventions
|
|
271
271
|
|
|
272
272
|
We explored using file system conventions to identify workflows and steps, similar to how modern frameworks handle routing (Next.js, Hono, Nitro, SvelteKit):
|
|
273
273
|
|
|
@@ -282,7 +282,7 @@ We explored using file system conventions to identify workflows and steps, simil
|
|
|
282
282
|
</Folder>
|
|
283
283
|
</Files>
|
|
284
284
|
|
|
285
|
-
With this approach, any function in the `workflows/` directory would be transformed as a workflow, and any function in `steps/` would be a step.
|
|
285
|
+
With this approach, any function in the `workflows/` directory would be transformed as a workflow, and any function in `steps/` would be a step. File locations would replace directives.
|
|
286
286
|
|
|
287
287
|
**Why this could work:**
|
|
288
288
|
|
|
@@ -308,7 +308,7 @@ The directive approach solved all these issues: it works in any project structur
|
|
|
308
308
|
|
|
309
309
|
### Decorators
|
|
310
310
|
|
|
311
|
-
We considered decorators, but they presented
|
|
311
|
+
We considered decorators, but they presented technical and ergonomic challenges.
|
|
312
312
|
|
|
313
313
|
**Decorators are non-yet-standard and class-focused**
|
|
314
314
|
|
|
@@ -349,7 +349,7 @@ While decorators can be handled at compile-time with build tool support, they pr
|
|
|
349
349
|
See the [Macro Wrapper](#macro-wrapper-approach) section below for a deeper dive into why this approach breaks down with concrete examples.
|
|
350
350
|
</Callout>
|
|
351
351
|
|
|
352
|
-
### Macro
|
|
352
|
+
### Macro wrapper approach
|
|
353
353
|
|
|
354
354
|
We also explored compile-time macro approaches - using a compiler to transform wrapper functions or decorators into directive-based code:
|
|
355
355
|
|
|
@@ -385,7 +385,7 @@ export const processOrder = async (orderId: string) => {
|
|
|
385
385
|
};
|
|
386
386
|
```
|
|
387
387
|
|
|
388
|
-
The benefit is that macros could enforce types and provide "Go To Definition" or other LSP features
|
|
388
|
+
The benefit is that macros could enforce types and provide "Go To Definition" or other LSP features without additional configuration.
|
|
389
389
|
|
|
390
390
|
However, **the core problem remains: Workflows aren't runtime values**
|
|
391
391
|
|
|
@@ -431,7 +431,7 @@ To detect that `processOrder` is actually a workflow, the compiler would need wh
|
|
|
431
431
|
|
|
432
432
|
This level of cross-function analysis is impractical for build tools - it would require analyzing every function call chain in your entire codebase and all dependencies. The compiler can only reliably detect direct `useWorkflow` calls, not calls hidden behind abstractions.
|
|
433
433
|
|
|
434
|
-
## How
|
|
434
|
+
## How directives solve these problems
|
|
435
435
|
|
|
436
436
|
Directives address all the issues we encountered with previous approaches:
|
|
437
437
|
|
|
@@ -527,7 +527,7 @@ export async function processOrder(orderId: string) {
|
|
|
527
527
|
|
|
528
528
|
The `"use step"` directive maintains consistency. While steps run in the full Node.js runtime and *could* work without a directive, they need some way to signal to the workflow runtime that they're steps.
|
|
529
529
|
|
|
530
|
-
We could have used a function wrapper
|
|
530
|
+
We could have used a function wrapper for steps:
|
|
531
531
|
|
|
532
532
|
{/* @skip-typecheck: incomplete code sample */}
|
|
533
533
|
```typescript lineNumbers
|
|
@@ -584,7 +584,7 @@ By requiring explicit `"use step"` directives, developers have fine-grained cont
|
|
|
584
584
|
To understand how directives are transformed at compile time, see [How the Code Transform Works](/docs/how-it-works/code-transform).
|
|
585
585
|
</Callout>
|
|
586
586
|
|
|
587
|
-
## What
|
|
587
|
+
## What directives enable
|
|
588
588
|
|
|
589
589
|
Because `"use workflow"` defines a compile-time semantic boundary, we can provide:
|
|
590
590
|
|
|
@@ -603,7 +603,7 @@ Because `"use workflow"` defines a compile-time semantic boundary, we can provid
|
|
|
603
603
|
</Card>
|
|
604
604
|
</Cards>
|
|
605
605
|
|
|
606
|
-
## Directives as a JavaScript
|
|
606
|
+
## Directives as a JavaScript pattern
|
|
607
607
|
|
|
608
608
|
Directives in JavaScript have always been contracts between the developer and the execution environment. `"use strict"` made this pattern familiar - it's a string literal that changes how code is interpreted.
|
|
609
609
|
|
|
@@ -611,7 +611,7 @@ While JavaScript doesn't yet have first-class support for custom directives (lik
|
|
|
611
611
|
|
|
612
612
|
As TC39 members, we at Vercel are actively working with the standards body and broader ecosystem to explore formal specifications for pragma-like syntax or macro annotations that can express execution semantics.
|
|
613
613
|
|
|
614
|
-
## Closing
|
|
614
|
+
## Closing thoughts
|
|
615
615
|
|
|
616
616
|
Directives aren't about syntax preference, they're about expressing semantic boundaries. `"use workflow"` tells the compiler, developer, and runtime that this code is deterministic, resumable, and sandboxed.
|
|
617
617
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workflow/core",
|
|
3
|
-
"version": "5.0.0-beta.
|
|
3
|
+
"version": "5.0.0-beta.44",
|
|
4
4
|
"description": "Core runtime and engine for Workflow SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -94,12 +94,12 @@
|
|
|
94
94
|
"semver": "7.7.4",
|
|
95
95
|
"ulid": "~3.0.1",
|
|
96
96
|
"zod": "~4.3.6",
|
|
97
|
-
"@workflow/errors": "5.0.0-beta.
|
|
97
|
+
"@workflow/errors": "5.0.0-beta.18",
|
|
98
98
|
"@workflow/serde": "5.0.0-beta.2",
|
|
99
|
-
"@workflow/utils": "5.0.0-beta.
|
|
100
|
-
"@workflow/world": "5.0.0-beta.
|
|
101
|
-
"@workflow/world-local": "5.0.0-beta.
|
|
102
|
-
"@workflow/world-vercel": "5.0.0-beta.
|
|
99
|
+
"@workflow/utils": "5.0.0-beta.9",
|
|
100
|
+
"@workflow/world": "5.0.0-beta.29",
|
|
101
|
+
"@workflow/world-local": "5.0.0-beta.38",
|
|
102
|
+
"@workflow/world-vercel": "5.0.0-beta.40"
|
|
103
103
|
},
|
|
104
104
|
"devDependencies": {
|
|
105
105
|
"@opentelemetry/api": "1.9.0",
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
"build": "genversion --es6 src/version.ts && node scripts/build-quickjs-assets.js && tsc",
|
|
128
128
|
"dev": "genversion --es6 src/version.ts && tsc --watch",
|
|
129
129
|
"clean": "tsc --build --clean && rm -rf dist src/version.ts docs ||:",
|
|
130
|
-
"test": "cross-env WORKFLOW_TARGET_WORLD=local vitest run src",
|
|
130
|
+
"test": "cross-env WORKFLOW_TARGET_WORLD=local vitest run src e2e/utils.test.ts",
|
|
131
131
|
"test:e2e": "vitest run e2e",
|
|
132
132
|
"typecheck": "genversion --es6 src/version.ts && tsc --noEmit"
|
|
133
133
|
}
|