@claxedo/workspace-relay 0.5.2 → 0.7.0
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/bun.d.ts +31 -0
- package/dist/cloudflare.d.ts +98 -0
- package/dist/index.mjs +309 -39
- package/dist/main.d.ts +47 -27
- package/dist/main.mjs +19692 -39183
- package/dist/worker.d.ts +12 -0
- package/dist/workerd-fixture/binary-frame-worker.d.ts +33 -0
- package/package.json +13 -6
package/dist/bun.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ type RelayClientWebSocketData = {
|
|
|
7
7
|
payload: string | Buffer<ArrayBuffer>;
|
|
8
8
|
queuedAt: number;
|
|
9
9
|
}>;
|
|
10
|
+
/** Running byte size of `queue`; reset when the queue is flushed upstream. */
|
|
11
|
+
queuedBytes?: number;
|
|
10
12
|
upstream?: WebSocket;
|
|
11
13
|
upstreamOpenTimer?: ReturnType<typeof setTimeout>;
|
|
12
14
|
trace?: RelayClientWebSocketTrace;
|
|
@@ -70,6 +72,7 @@ export type WorkspaceRelayBackpressureOptions = {
|
|
|
70
72
|
directHttpConcurrency?: number;
|
|
71
73
|
upstreamWebSocketOpenTimeoutMs?: number;
|
|
72
74
|
upstreamWebSocketPreOpenQueueMaxFrames?: number;
|
|
75
|
+
upstreamWebSocketPreOpenQueueMaxBytes?: number;
|
|
73
76
|
upstreamWebSocket?: UpstreamWebSocketConstructor;
|
|
74
77
|
webSocketBufferedAmountMaxBytes?: number;
|
|
75
78
|
};
|
|
@@ -103,6 +106,34 @@ export type WorkspaceRelayBunDrainController = {
|
|
|
103
106
|
remaining: number;
|
|
104
107
|
}>;
|
|
105
108
|
};
|
|
109
|
+
/**
|
|
110
|
+
* Bytes queued in a socket's send buffer, or `undefined` if it cannot report.
|
|
111
|
+
*
|
|
112
|
+
* Bun spells this `getBufferedAmount()` — a method, not the browser's
|
|
113
|
+
* `bufferedAmount` property. The two guards below used to read the property
|
|
114
|
+
* behind a non-null assertion, which typechecked and then evaluated
|
|
115
|
+
* `undefined > 8388608` forever: neither had ever closed a connection. Measured
|
|
116
|
+
* in `relay-workerd-backpressure.test.ts` at 16.8 MB queued, twice the limit,
|
|
117
|
+
* guard silent.
|
|
118
|
+
*
|
|
119
|
+
* FAILS OPEN on a socket that cannot report, deliberately. Test doubles and any
|
|
120
|
+
* exotic socket must not be closed for lacking the accessor — an unguarded
|
|
121
|
+
* healthy connection beats a guard that kills healthy connections. Note this is
|
|
122
|
+
* also why the same guard is NOT portable to `cloudflare.ts`: workerd's
|
|
123
|
+
* WebSocket exposes no buffer depth in any form, so there it would fail open
|
|
124
|
+
* unconditionally. Bounding the Cloudflare path needs a protocol-level
|
|
125
|
+
* credit/ack window instead.
|
|
126
|
+
*/
|
|
127
|
+
export declare const relayBufferedBytes: (socket: unknown) => number | undefined;
|
|
128
|
+
/**
|
|
129
|
+
* True only when the socket reports a depth over the limit. Unknown ⇒ false.
|
|
130
|
+
*
|
|
131
|
+
* Exported for `bun.test.ts`. Forcing a real over-limit buffer through the
|
|
132
|
+
* server's own sockets is not achievable locally — a loopback peer drains far
|
|
133
|
+
* faster than a test can outrun it, which is precisely why the dead guard went
|
|
134
|
+
* unnoticed for so long — so the decision itself is asserted directly.
|
|
135
|
+
*/
|
|
136
|
+
export declare const relayOverBackpressureLimit: (socket: unknown, limitBytes: number) => boolean;
|
|
106
137
|
export type FragmentationStats = {
|
|
107
138
|
fragmentsBuffered: number;
|
|
108
139
|
oversizedClosed: number;
|
package/dist/cloudflare.d.ts
CHANGED
|
@@ -11,6 +11,44 @@ export type WorkspaceRelayDurableObjectNamespace = {
|
|
|
11
11
|
get: (id: WorkspaceRelayDurableObjectId, options?: WorkspaceRelayDurableObjectGetOptions) => WorkspaceRelayDurableObjectStub;
|
|
12
12
|
};
|
|
13
13
|
export declare const DEFAULT_RELAY_LOCATION_HINT = "apac";
|
|
14
|
+
/**
|
|
15
|
+
* The complete set of Cloudflare Durable Object location hints.
|
|
16
|
+
*
|
|
17
|
+
* Cloudflare rejects an unknown hint, and a rejected `get()` fails the request —
|
|
18
|
+
* so an unrecognised region must fall back rather than be passed through. This
|
|
19
|
+
* list is the validator for that.
|
|
20
|
+
*/
|
|
21
|
+
export declare const RELAY_LOCATION_HINTS: readonly ["wnam", "enam", "sam", "weur", "eeur", "apac", "oc", "afr", "me"];
|
|
22
|
+
export type RelayLocationHint = typeof RELAY_LOCATION_HINTS[number];
|
|
23
|
+
/** A workspace region carried on the request, if the caller supplied one. */
|
|
24
|
+
export declare const RELAY_REGION_HEADER = "x-claxedo-workspace-region";
|
|
25
|
+
/**
|
|
26
|
+
* Chooses the location hint for a workspace's Durable Object.
|
|
27
|
+
*
|
|
28
|
+
* This decision is PERMANENT: a DO's location is fixed at first creation and
|
|
29
|
+
* never migrates, so a workspace created with the wrong hint pays the extra
|
|
30
|
+
* ocean crossing on every frame for its entire life. That is why an
|
|
31
|
+
* unrecognised value falls back to the configured default instead of being
|
|
32
|
+
* passed through or guessed at.
|
|
33
|
+
*
|
|
34
|
+
* Precedence, most authoritative first:
|
|
35
|
+
* 1. the workspace's own region (header or `?region=`), which is what the
|
|
36
|
+
* control plane knows and the only signal tied to the WORKSPACE rather than
|
|
37
|
+
* to whoever happens to be connecting first;
|
|
38
|
+
* 2. the requesting user's Cloudflare edge country, a decent proxy when the
|
|
39
|
+
* workspace has no region yet;
|
|
40
|
+
* 3. the deployment-wide configured hint (`CLAXEDO_RELAY_LOCATION_HINT`), then
|
|
41
|
+
* `DEFAULT_RELAY_LOCATION_HINT`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function relayLocationHint(input: {
|
|
44
|
+
configured?: string;
|
|
45
|
+
region?: string;
|
|
46
|
+
country?: string;
|
|
47
|
+
}): string;
|
|
48
|
+
/** The workspace region a request declares, if any. */
|
|
49
|
+
export declare function relayRequestRegion(request: Request): string | undefined;
|
|
50
|
+
/** The Cloudflare edge country for the request, when running on Cloudflare. */
|
|
51
|
+
export declare function relayRequestCountry(request: Request): string | undefined;
|
|
14
52
|
export type WorkspaceRelayDurableObjectEnv = Record<string, unknown>;
|
|
15
53
|
export type WorkspaceRelayDurableObjectGatewayOptions = {
|
|
16
54
|
bindingName?: string;
|
|
@@ -43,6 +81,7 @@ export type WorkspaceRelayDurableObjectRoomOptions = WorkspaceRelayOptions & {
|
|
|
43
81
|
connectWebSocket?: WorkspaceRelayDurableObjectConnectWebSocket;
|
|
44
82
|
hibernation?: WorkspaceRelayDurableObjectHibernation;
|
|
45
83
|
upstreamWebSocketPreOpenQueueMaxFrames?: number;
|
|
84
|
+
upstreamWebSocketPreOpenQueueMaxBytes?: number;
|
|
46
85
|
tunnelPendingHttpCap?: number;
|
|
47
86
|
tunnelStartedStreamCap?: number;
|
|
48
87
|
tunnelChannelCap?: number;
|
|
@@ -52,6 +91,23 @@ export type WorkspaceRelayDurableObjectRoomOptions = WorkspaceRelayOptions & {
|
|
|
52
91
|
slowConsumerTimeoutMs?: number;
|
|
53
92
|
runtimeAccessTokenActiveCheckIntervalMs?: number;
|
|
54
93
|
workspaceTargetActiveCheckIntervalMs?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Consecutive resolver-unreachable answers tolerated on an established
|
|
96
|
+
* connection before closing it. Applies ONLY to "could not reach the
|
|
97
|
+
* resolver"; an explicit revoked/inactive answer still closes immediately.
|
|
98
|
+
*/
|
|
99
|
+
resolverOutageGraceAttempts?: number;
|
|
100
|
+
/**
|
|
101
|
+
* Interval for the alarm-based revocation re-check that covers hibernated
|
|
102
|
+
* connections, where `setInterval` watchers cannot survive. 0 disables it.
|
|
103
|
+
*/
|
|
104
|
+
hibernatedRevocationCheckIntervalMs?: number;
|
|
105
|
+
/**
|
|
106
|
+
* Durable Object alarm surface, supplied by the Worker (`state.storage`) so the
|
|
107
|
+
* room can schedule the hibernation-safe revocation re-check. Absent on the
|
|
108
|
+
* non-hibernating path and in tests that do not exercise it.
|
|
109
|
+
*/
|
|
110
|
+
alarms?: WorkspaceRelayDurableObjectAlarms;
|
|
55
111
|
traceSampleRate?: number;
|
|
56
112
|
traceForceHeaderSecret?: string;
|
|
57
113
|
traceLog?: (event: WorkspaceRelayTraceLogEvent) => void;
|
|
@@ -72,6 +128,19 @@ export type WorkspaceRelayDurableObjectHibernation = {
|
|
|
72
128
|
acceptWebSocket: (socket: WorkspaceRelayDurableObjectSocket) => void;
|
|
73
129
|
getWebSockets: () => WorkspaceRelayDurableObjectSocket[];
|
|
74
130
|
};
|
|
131
|
+
/**
|
|
132
|
+
* The Durable Object alarm surface, narrowed to what the room needs.
|
|
133
|
+
*
|
|
134
|
+
* Alarms are the only periodic mechanism that survives hibernation — a
|
|
135
|
+
* `setInterval` dies when the DO is evicted, which is exactly why the token and
|
|
136
|
+
* target watchers are not installed on the hibernating path. Backed by
|
|
137
|
+
* `state.storage` in the Worker.
|
|
138
|
+
*/
|
|
139
|
+
export type WorkspaceRelayDurableObjectAlarms = {
|
|
140
|
+
getAlarm: () => Promise<number | null> | number | null;
|
|
141
|
+
setAlarm: (scheduledTime: number) => Promise<void> | void;
|
|
142
|
+
deleteAlarm?: () => Promise<void> | void;
|
|
143
|
+
};
|
|
75
144
|
export type WorkspaceRelaySocketAttachment = {
|
|
76
145
|
kind: "host-tunnel";
|
|
77
146
|
hostId: string;
|
|
@@ -96,6 +165,30 @@ export type WorkspaceRelayDurableObjectDrainController = {
|
|
|
96
165
|
remaining: number;
|
|
97
166
|
}>;
|
|
98
167
|
};
|
|
168
|
+
/**
|
|
169
|
+
* Exported for `relay-base64.test.ts`, which proves byte-equality against a
|
|
170
|
+
* per-byte reference across the chunk boundaries. This sits on the path every
|
|
171
|
+
* frame pays, so its equivalence is worth asserting directly rather than
|
|
172
|
+
* inferring from a round-trip.
|
|
173
|
+
*/
|
|
174
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
175
|
+
/**
|
|
176
|
+
* Runs the two frame decoders over one runtime-delivered `event.data` and
|
|
177
|
+
* reports what they produced.
|
|
178
|
+
*
|
|
179
|
+
* Exists so `relay-workerd-binary.test.ts` can assert the decoders against what
|
|
180
|
+
* REAL workerd delivers. The rest of the suite drives hand-rolled socket
|
|
181
|
+
* doubles, which by construction hand us whatever we chose to hand ourselves —
|
|
182
|
+
* so they cannot catch the hazard that binary `event.data` becomes a `Blob` at
|
|
183
|
+
* `compatibility_date >= 2026-03-17`. Test-support surface, not a relay path.
|
|
184
|
+
*/
|
|
185
|
+
export declare function relayFrameDiagnostics(data: unknown): Promise<{
|
|
186
|
+
frameDropped: boolean;
|
|
187
|
+
payloadDropped: boolean;
|
|
188
|
+
binary?: boolean | undefined;
|
|
189
|
+
dataBase64?: string | undefined;
|
|
190
|
+
payloadBase64: string | undefined;
|
|
191
|
+
}>;
|
|
99
192
|
export declare function workspaceRelayDurableObjectRoomName(workspaceId: string): string;
|
|
100
193
|
export declare function workspaceRelayDurableObjectWorkspaceId(request: Request): string | undefined;
|
|
101
194
|
export declare function setWorkspaceRelayAppOrigins(raw: string | undefined): void;
|
|
@@ -106,6 +199,11 @@ export declare function createWorkspaceRelayDurableObjectGateway(options?: Works
|
|
|
106
199
|
export declare function createWorkspaceRelayDurableObjectRoom(options: WorkspaceRelayDurableObjectRoomOptions): {
|
|
107
200
|
fetch(request: Request): Promise<Response>;
|
|
108
201
|
webSocketMessage(socket: WorkspaceRelayDurableObjectSocket, message: string | ArrayBuffer): Promise<void>;
|
|
202
|
+
/**
|
|
203
|
+
* Durable Object alarm entry point. Runs the hibernation-safe revocation
|
|
204
|
+
* sweep (W6b.2). The Worker must forward `alarm()` here.
|
|
205
|
+
*/
|
|
206
|
+
alarm(): Promise<void>;
|
|
109
207
|
webSocketClose(socket: WorkspaceRelayDurableObjectSocket, code: number, reason: string): void;
|
|
110
208
|
webSocketError(socket: WorkspaceRelayDurableObjectSocket): void;
|
|
111
209
|
state(): {
|