mirror of
https://github.com/vercel/workflow.git
synced 2026-09-14 19:59:43 +08:00
feat(streams): add WebSocket capability gate (#3764)
## Summary & Motivation `WORKFLOW_STREAMS_TRANSPORT=ws` advertises client support for `workflow-stream-ws/v1` on stream writes; anything else keeps HTTP. It's a capability signal only — the server decides each upgrade, so there's no version or tenant-policy heuristic on the client side. ## Test Plan Unit tests for the gate's accepted values; typecheck, build, and lint pass locally.
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@workflow/world-vercel': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add the default-off stream WebSocket capability gate.
|
||||||
@@ -281,6 +281,14 @@ Platform-provided values such as `VERCEL_DEPLOYMENT_ID`, `VERCEL_PROJECT_ID`, an
|
|||||||
- Default: `1000`
|
- Default: `1000`
|
||||||
- Maximum stream chunks written in one Vercel World request. Larger batches are split.
|
- Maximum stream chunks written in one Vercel World request. Larger batches are split.
|
||||||
|
|
||||||
|
### `WORKFLOW_STREAMS_TRANSPORT`
|
||||||
|
|
||||||
|
- Factory option: none
|
||||||
|
- CLI flag: none
|
||||||
|
- Default: `http`
|
||||||
|
- Experimental stream-write transport capability. Set to exactly `ws` to advertise support for `workflow-stream-ws/v1` when it becomes available. The server authoritatively accepts or declines an upgrade; a decline uses HTTP directly. Stream reads remain HTTP and demand-driven.
|
||||||
|
- This is not tenant rollout policy or a package-version check. HTTP remains the compatibility path. `/websockets/v1` is independent of REST v2/v4 and persisted workflow `specVersion` values.
|
||||||
|
|
||||||
### `WORKFLOW_DISABLE_ANALYTICS_READS`
|
### `WORKFLOW_DISABLE_ANALYTICS_READS`
|
||||||
|
|
||||||
- Factory option: none
|
- Factory option: none
|
||||||
|
|||||||
@@ -200,6 +200,12 @@ Below the floor the deadline starts canceling requests that would have succeeded
|
|||||||
|
|
||||||
Maximum stream chunks written in one Vercel World request. Larger batches are split across multiple requests. Default: `1000`. Minimum: `1`.
|
Maximum stream chunks written in one Vercel World request. Larger batches are split across multiple requests. Default: `1000`. Minimum: `1`.
|
||||||
|
|
||||||
|
### `WORKFLOW_STREAMS_TRANSPORT`
|
||||||
|
|
||||||
|
Experimental stream-write transport capability. Default: `http`. Set `WORKFLOW_STREAMS_TRANSPORT=ws` to advertise support for `workflow-stream-ws/v1` when it becomes available. The server authoritatively accepts or declines every upgrade; a decline goes directly to HTTP. Reads remain HTTP and demand-driven.
|
||||||
|
|
||||||
|
This setting does not own tenant rollout policy and does not infer server support from package versions. HTTP remains the compatibility path. The protocol route (`/websockets/v1`) is versioned independently from REST v2/v4 and persisted workflow `specVersion` values. Drain/de-opt control semantics are a rollout prerequisite and are intentionally not part of this capability gate.
|
||||||
|
|
||||||
### `WORKFLOW_EVENTS_TRANSPORT`
|
### `WORKFLOW_EVENTS_TRANSPORT`
|
||||||
|
|
||||||
Workflow run events ship to the Vercel World over a WebSocket instead of one HTTP request each. Default: `ws`.
|
Workflow run events ship to the Vercel World over a WebSocket instead of one HTTP request each. Default: `ws`.
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { afterEach, describe, expect, it } from 'vitest';
|
||||||
|
import { isWsStreamsTransportEnabled } from './ws-transport-enabled.js';
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
delete process.env.WORKFLOW_STREAMS_TRANSPORT;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isWsStreamsTransportEnabled', () => {
|
||||||
|
it.each([
|
||||||
|
[undefined, false],
|
||||||
|
['', false],
|
||||||
|
['http', false],
|
||||||
|
['HTTP', false],
|
||||||
|
['ws ', false],
|
||||||
|
['WS', false],
|
||||||
|
['ws', true],
|
||||||
|
])('advertises v1 only for the exact ws value: %j', (value, expected) => {
|
||||||
|
if (value === undefined) {
|
||||||
|
delete process.env.WORKFLOW_STREAMS_TRANSPORT;
|
||||||
|
} else {
|
||||||
|
process.env.WORKFLOW_STREAMS_TRANSPORT = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(isWsStreamsTransportEnabled()).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -53,3 +53,20 @@ export function isWsEventsTransportStrict(): boolean {
|
|||||||
const raw = process.env.WORKFLOW_INTERNAL_EVENTS_TRANSPORT_STRICT;
|
const raw = process.env.WORKFLOW_INTERNAL_EVENTS_TRANSPORT_STRICT;
|
||||||
return raw === '1' || raw === 'true';
|
return raw === '1' || raw === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Advertise the experimental v1 stream-write protocol only when explicitly
|
||||||
|
* requested. This is a client capability signal, not an entitlement: the
|
||||||
|
* server authoritatively accepts or declines every upgrade, and a decline
|
||||||
|
* falls back directly to the HTTP stream writer.
|
||||||
|
*
|
||||||
|
* HTTP is the compatibility path and the default. Unlike the default-on events
|
||||||
|
* gate above, this opt-in is exact-match: a typo must fail toward HTTP rather
|
||||||
|
* than unexpectedly enabling an experimental transport. This deliberately has
|
||||||
|
* no package-version or tenant-policy heuristic; rollout policy belongs to the
|
||||||
|
* server. v1 is `/websockets/v1`, independently versioned from REST v2/v4 and
|
||||||
|
* persisted workflow spec versions.
|
||||||
|
*/
|
||||||
|
export function isWsStreamsTransportEnabled(): boolean {
|
||||||
|
return process.env.WORKFLOW_STREAMS_TRANSPORT === 'ws';
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user