@claxedo/workspace-relay 0.10.0 → 0.10.2
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/cloudflare.d.ts +2 -12
- package/dist/index.mjs +63 -51
- package/dist/relay-trace.d.ts +54 -0
- package/dist/workerd-fixture/boot.d.ts +5 -13
- package/dist/workerd-fixture/compatibility.d.ts +2 -0
- package/package.json +6 -6
package/dist/cloudflare.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type AuthorizedWorkspaceRelayRequest, type WorkspaceRelayOptions } from "./server";
|
|
2
2
|
import { type SessionHostNamespace } from "./session-host-forwarding";
|
|
3
|
+
import { type WorkspaceRelayTraceLogEvent } from "./relay-trace";
|
|
4
|
+
export type { WorkspaceRelayTraceLogEvent } from "./relay-trace";
|
|
3
5
|
export type WorkspaceRelayDurableObjectId = unknown;
|
|
4
6
|
export type WorkspaceRelayDurableObjectStub = {
|
|
5
7
|
fetch: (request: Request) => Promise<Response>;
|
|
@@ -94,17 +96,6 @@ export type WorkspaceRelayDurableObjectRoomOptions = WorkspaceRelayOptions & {
|
|
|
94
96
|
traceLog?: (event: WorkspaceRelayTraceLogEvent) => void;
|
|
95
97
|
now?: () => number;
|
|
96
98
|
};
|
|
97
|
-
export type WorkspaceRelayTraceLogEvent = {
|
|
98
|
-
event: "workspace_relay_request";
|
|
99
|
-
traceId: string;
|
|
100
|
-
workspaceId?: string;
|
|
101
|
-
routeKind: string;
|
|
102
|
-
method: string;
|
|
103
|
-
path: string;
|
|
104
|
-
status: number;
|
|
105
|
-
totalMs: number;
|
|
106
|
-
phases: Record<string, number>;
|
|
107
|
-
};
|
|
108
99
|
export type WorkspaceRelayDurableObjectHibernation = {
|
|
109
100
|
acceptWebSocket: (socket: WorkspaceRelayDurableObjectSocket) => void;
|
|
110
101
|
getWebSockets: () => WorkspaceRelayDurableObjectSocket[];
|
|
@@ -199,4 +190,3 @@ export declare function createWorkspaceRelayDurableObjectRoom(options: Workspace
|
|
|
199
190
|
};
|
|
200
191
|
drain: WorkspaceRelayDurableObjectDrainController;
|
|
201
192
|
};
|
|
202
|
-
export {};
|
package/dist/index.mjs
CHANGED
|
@@ -482,11 +482,19 @@ var PUBLIC_API_ERRORS = {
|
|
|
482
482
|
enrollment_paused: { status: 403, message: "enrollment_paused" },
|
|
483
483
|
enrollment_owner_ineligible: { status: 403, message: "enrollment_owner_ineligible" },
|
|
484
484
|
enrollment_key_version_mismatch: { status: 403, message: "enrollment_key_version_mismatch" },
|
|
485
|
+
enrollment_denied: { status: 403, message: "enrollment_denied" },
|
|
486
|
+
enrollment_approval_expired: { status: 403, message: "enrollment_approval_expired" },
|
|
487
|
+
enrollment_awaiting_approval: { status: 403, message: "enrollment_awaiting_approval" },
|
|
485
488
|
invitation_invalid: { status: 403, message: "invitation_invalid" },
|
|
486
489
|
invitation_expired: { status: 410, message: "invitation_expired" },
|
|
487
490
|
invitation_revoked: { status: 410, message: "invitation_revoked" },
|
|
488
491
|
invitation_redeemed: { status: 409, message: "invitation_redeemed" },
|
|
489
492
|
invitation_host_conflict: { status: 409, message: "invitation_host_conflict" },
|
|
493
|
+
pairing_invalid: { status: 403, message: "pairing_invalid" },
|
|
494
|
+
pairing_expired: { status: 410, message: "pairing_expired" },
|
|
495
|
+
pairing_used: { status: 409, message: "pairing_used" },
|
|
496
|
+
pairing_owner_signed_out: { status: 410, message: "pairing_owner_signed_out" },
|
|
497
|
+
pairing_decided: { status: 409, message: "pairing_decided" },
|
|
490
498
|
enrollment_generation_superseded: { status: 409, message: "enrollment_generation_superseded" },
|
|
491
499
|
host_assignment_outside_scope: { status: 400, message: "host_assignment_outside_scope" },
|
|
492
500
|
host_sealing_key_undeclared: { status: 409, message: "host_sealing_key_undeclared" },
|
|
@@ -525,7 +533,7 @@ function hostTunnelFailureResponse(error) {
|
|
|
525
533
|
}
|
|
526
534
|
|
|
527
535
|
// src/cloudflare.ts
|
|
528
|
-
import { trimToUndefined as
|
|
536
|
+
import { trimToUndefined as trimToUndefined5 } from "@claxedo/helpers/string";
|
|
529
537
|
|
|
530
538
|
// src/http.ts
|
|
531
539
|
import { bearerToken, trimToUndefined } from "@claxedo/helpers/string";
|
|
@@ -549,6 +557,8 @@ function workspaceIdsFromSearch(url) {
|
|
|
549
557
|
|
|
550
558
|
// src/cloudflare.ts
|
|
551
559
|
import {
|
|
560
|
+
closeCode,
|
|
561
|
+
closeReason,
|
|
552
562
|
makeTunnelPong,
|
|
553
563
|
TUNNEL_PROTOCOL_VERSION,
|
|
554
564
|
validateTunnelMessage
|
|
@@ -1425,30 +1435,11 @@ function workspaceRelayRoomPlacement(request, env, workspaceId) {
|
|
|
1425
1435
|
});
|
|
1426
1436
|
}
|
|
1427
1437
|
|
|
1428
|
-
// src/
|
|
1429
|
-
|
|
1430
|
-
var TUNNEL_STARTED_STREAM_CAP_DEFAULT = 16;
|
|
1431
|
-
var TUNNEL_CHANNEL_CAP_DEFAULT = 16;
|
|
1432
|
-
var TUNNEL_REQUEST_BODY_MAX_BYTES_DEFAULT = 16 * 1024 * 1024;
|
|
1433
|
-
var TUNNEL_RESPONSE_BODY_MAX_BYTES_DEFAULT = 16 * 1024 * 1024;
|
|
1434
|
-
var SLOW_CONSUMER_HIGH_WATER_MARK_BYTES_DEFAULT = 8 * 1024 * 1024;
|
|
1435
|
-
var SLOW_CONSUMER_TIMEOUT_MS_DEFAULT = 3e4;
|
|
1436
|
-
var UPSTREAM_WS_PRE_OPEN_QUEUE_MAX_FRAMES_DEFAULT = 8192;
|
|
1437
|
-
var UPSTREAM_WS_PRE_OPEN_QUEUE_MAX_BYTES_DEFAULT = 8 * 1024 * 1024;
|
|
1438
|
-
var UPSTREAM_WS_PRE_OPEN_QUEUE_FRAME_OVERHEAD_BYTES = 64;
|
|
1439
|
-
var WORKSPACE_TARGET_ACTIVE_CHECK_INTERVAL_MS_DEFAULT = 3e4;
|
|
1440
|
-
var RESOLVER_OUTAGE_GRACE_ATTEMPTS_DEFAULT = 3;
|
|
1441
|
-
var HIBERNATED_REVOCATION_ALARM_INTERVAL_MS_DEFAULT = 3e4;
|
|
1442
|
-
var HOST_GENERATION_CHECK_INTERVAL_MS_DEFAULT = 3e4;
|
|
1438
|
+
// src/relay-trace.ts
|
|
1439
|
+
import { trimToUndefined as trimToUndefined4 } from "@claxedo/helpers/string";
|
|
1443
1440
|
var TRACE_ID_HEADER = "x-claxedo-trace-id";
|
|
1444
1441
|
var TRACE_FORCE_HEADER = "x-claxedo-relay-trace";
|
|
1445
1442
|
var TRACE_SAMPLED_RESPONSE_HEADER = "x-claxedo-relay-sampled";
|
|
1446
|
-
function unrefTimer(timer) {
|
|
1447
|
-
if (typeof timer.unref === "function") timer.unref();
|
|
1448
|
-
}
|
|
1449
|
-
function isHostTunnelClient(client) {
|
|
1450
|
-
return "channelId" in client && typeof client.channelId === "string";
|
|
1451
|
-
}
|
|
1452
1443
|
function clampRate(input) {
|
|
1453
1444
|
if (input === void 0 || !Number.isFinite(input)) return 0;
|
|
1454
1445
|
if (input <= 0) return 0;
|
|
@@ -1492,13 +1483,21 @@ function tracedRequest(request, trace, forwarded = request) {
|
|
|
1492
1483
|
function roundedMs(value) {
|
|
1493
1484
|
return Math.round(value * 100) / 100;
|
|
1494
1485
|
}
|
|
1495
|
-
function serverTiming(
|
|
1496
|
-
return
|
|
1486
|
+
function serverTiming(phases) {
|
|
1487
|
+
return phases.map((phase) => `${phase.name};dur=${roundedMs(phase.ms)}`).join(", ");
|
|
1488
|
+
}
|
|
1489
|
+
function webSocketTraceFrame(upstreamStartedAt, queueTimes, openedAt) {
|
|
1490
|
+
return {
|
|
1491
|
+
type: "relay.trace",
|
|
1492
|
+
wsUpstreamOpenMs: roundedMs(openedAt - upstreamStartedAt),
|
|
1493
|
+
queuedFrames: queueTimes.length,
|
|
1494
|
+
maxQueuedDelayMs: roundedMs(queueTimes.reduce((max, at) => Math.max(max, openedAt - at), 0))
|
|
1495
|
+
};
|
|
1497
1496
|
}
|
|
1498
1497
|
function withTraceHeaders(response, trace) {
|
|
1499
1498
|
const headers = new Headers(response.headers);
|
|
1500
1499
|
headers.set(TRACE_ID_HEADER, trace.id);
|
|
1501
|
-
const timing = trace.sampled ? serverTiming(trace) : "";
|
|
1500
|
+
const timing = trace.sampled ? serverTiming(trace.phases) : "";
|
|
1502
1501
|
if (timing) {
|
|
1503
1502
|
const existing = headers.get("server-timing");
|
|
1504
1503
|
headers.set("server-timing", existing ? `${existing}, ${timing}` : timing);
|
|
@@ -1531,6 +1530,28 @@ function logTrace(options, request, workspaceId, trace, response) {
|
|
|
1531
1530
|
}
|
|
1532
1531
|
console.log(JSON.stringify(event));
|
|
1533
1532
|
}
|
|
1533
|
+
|
|
1534
|
+
// src/cloudflare.ts
|
|
1535
|
+
var TUNNEL_PENDING_HTTP_CAP_DEFAULT = 32;
|
|
1536
|
+
var TUNNEL_STARTED_STREAM_CAP_DEFAULT = 16;
|
|
1537
|
+
var TUNNEL_CHANNEL_CAP_DEFAULT = 16;
|
|
1538
|
+
var TUNNEL_REQUEST_BODY_MAX_BYTES_DEFAULT = 16 * 1024 * 1024;
|
|
1539
|
+
var TUNNEL_RESPONSE_BODY_MAX_BYTES_DEFAULT = 16 * 1024 * 1024;
|
|
1540
|
+
var SLOW_CONSUMER_HIGH_WATER_MARK_BYTES_DEFAULT = 8 * 1024 * 1024;
|
|
1541
|
+
var SLOW_CONSUMER_TIMEOUT_MS_DEFAULT = 3e4;
|
|
1542
|
+
var UPSTREAM_WS_PRE_OPEN_QUEUE_MAX_FRAMES_DEFAULT = 8192;
|
|
1543
|
+
var UPSTREAM_WS_PRE_OPEN_QUEUE_MAX_BYTES_DEFAULT = 8 * 1024 * 1024;
|
|
1544
|
+
var UPSTREAM_WS_PRE_OPEN_QUEUE_FRAME_OVERHEAD_BYTES = 64;
|
|
1545
|
+
var WORKSPACE_TARGET_ACTIVE_CHECK_INTERVAL_MS_DEFAULT = 3e4;
|
|
1546
|
+
var RESOLVER_OUTAGE_GRACE_ATTEMPTS_DEFAULT = 3;
|
|
1547
|
+
var HIBERNATED_REVOCATION_ALARM_INTERVAL_MS_DEFAULT = 3e4;
|
|
1548
|
+
var HOST_GENERATION_CHECK_INTERVAL_MS_DEFAULT = 3e4;
|
|
1549
|
+
function unrefTimer(timer) {
|
|
1550
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
1551
|
+
}
|
|
1552
|
+
function isHostTunnelClient(client) {
|
|
1553
|
+
return "channelId" in client && typeof client.channelId === "string";
|
|
1554
|
+
}
|
|
1534
1555
|
function workspaceIdFromRelayPath(pathname) {
|
|
1535
1556
|
return /^\/workspaces\/([^/]+)(?:\/|$)/.exec(pathname)?.[1];
|
|
1536
1557
|
}
|
|
@@ -1680,20 +1701,15 @@ async function readRequestBodyCapped(request, maxBytes) {
|
|
|
1680
1701
|
}
|
|
1681
1702
|
return concatBytes(chunks);
|
|
1682
1703
|
}
|
|
1683
|
-
function safeCloseCode(code, fallback = 1011) {
|
|
1684
|
-
if (code === void 0) return fallback;
|
|
1685
|
-
if (code >= 1e3 && code <= 4999 && ![1004, 1005, 1006].includes(code)) return code;
|
|
1686
|
-
return fallback;
|
|
1687
|
-
}
|
|
1688
1704
|
function workspaceRelayDurableObjectRoomName(workspaceId) {
|
|
1689
1705
|
return `workspace:${workspaceId}`;
|
|
1690
1706
|
}
|
|
1691
1707
|
function workspaceRelayDurableObjectWorkspaceId(request) {
|
|
1692
1708
|
const url = new URL(request.url);
|
|
1693
|
-
const workspaceId =
|
|
1709
|
+
const workspaceId = trimToUndefined5(workspaceIdFromRelayPath(url.pathname));
|
|
1694
1710
|
if (workspaceId) return decodeURIComponent(workspaceId);
|
|
1695
1711
|
if (!hostIdFromTunnelPath(url.pathname)) return void 0;
|
|
1696
|
-
return
|
|
1712
|
+
return trimToUndefined5(url.searchParams.get("workspaceId")) ?? trimToUndefined5(url.searchParams.get("workspace_id"));
|
|
1697
1713
|
}
|
|
1698
1714
|
function namespaceFromEnv(env, options) {
|
|
1699
1715
|
if (options.namespace) return options.namespace;
|
|
@@ -1743,7 +1759,7 @@ function createWorkspaceRelayDurableObjectGateway(options = {}) {
|
|
|
1743
1759
|
return {
|
|
1744
1760
|
async fetch(request, env = {}) {
|
|
1745
1761
|
const gatewayStartedAt = performance.now();
|
|
1746
|
-
const gatewayTraceId =
|
|
1762
|
+
const gatewayTraceId = trimToUndefined5(request.headers.get(TRACE_ID_HEADER)) ?? crypto.randomUUID();
|
|
1747
1763
|
const url = new URL(request.url);
|
|
1748
1764
|
if (request.method === "OPTIONS" && (url.pathname.startsWith("/workspaces/") || url.pathname.startsWith("/host-tunnels/"))) {
|
|
1749
1765
|
return corsPreflight(request);
|
|
@@ -1804,12 +1820,12 @@ function createWorkspaceRelayDurableObjectGateway(options = {}) {
|
|
|
1804
1820
|
tracedResponse.headers.delete(TRACE_SAMPLED_RESPONSE_HEADER);
|
|
1805
1821
|
const roomTiming = tracedResponse.headers.get("server-timing") ?? "";
|
|
1806
1822
|
if (sampled) {
|
|
1807
|
-
const gatewayTiming = [
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
]
|
|
1823
|
+
const gatewayTiming = serverTiming([
|
|
1824
|
+
{ name: "relay-gateway-route", ms: routeFinishedAt - gatewayStartedAt },
|
|
1825
|
+
{ name: "relay-gateway-do-fetch", ms: fetchFinishedAt - routeFinishedAt },
|
|
1826
|
+
{ name: "relay-gateway-response", ms: gatewayFinishedAt - gatewayResponseStartedAt },
|
|
1827
|
+
{ name: "relay-gateway", ms: gatewayFinishedAt - gatewayStartedAt }
|
|
1828
|
+
]);
|
|
1813
1829
|
tracedResponse.headers.set("server-timing", `${roomTiming}, ${gatewayTiming}`);
|
|
1814
1830
|
}
|
|
1815
1831
|
return url.pathname.startsWith("/workspaces/") ? withRelayResponseHeaders(request, tracedResponse) : tracedResponse;
|
|
@@ -1845,7 +1861,7 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
1845
1861
|
};
|
|
1846
1862
|
const closeSocket = (socket, code, reason) => {
|
|
1847
1863
|
try {
|
|
1848
|
-
socket.close?.(code, reason);
|
|
1864
|
+
socket.close?.(code, closeReason(reason));
|
|
1849
1865
|
} catch {
|
|
1850
1866
|
}
|
|
1851
1867
|
};
|
|
@@ -2446,7 +2462,7 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
2446
2462
|
if (!channel) return;
|
|
2447
2463
|
tunnel.channels.delete(message.channel_id);
|
|
2448
2464
|
clients.delete(channel);
|
|
2449
|
-
closeSocket(channel.socket,
|
|
2465
|
+
closeSocket(channel.socket, closeCode(message.code), message.reason ?? "");
|
|
2450
2466
|
return;
|
|
2451
2467
|
}
|
|
2452
2468
|
if (parsed.message.type === "error" && parsed.message.channel_id) {
|
|
@@ -2484,7 +2500,7 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
2484
2500
|
type: "ws.close",
|
|
2485
2501
|
protocol: TUNNEL_PROTOCOL_VERSION,
|
|
2486
2502
|
channel_id: channelId,
|
|
2487
|
-
code:
|
|
2503
|
+
code: closeCode(event?.code, 1e3),
|
|
2488
2504
|
reason: event?.reason ?? "Client WebSocket closed"
|
|
2489
2505
|
}));
|
|
2490
2506
|
return;
|
|
@@ -2492,7 +2508,7 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
2492
2508
|
};
|
|
2493
2509
|
const admitHostTunnel = async (request, roomWorkspaceId, url) => {
|
|
2494
2510
|
if (!websocketRequest(request)) return json("websocket_upgrade_required", "Workspace Relay requires a WebSocket upgrade", 426);
|
|
2495
|
-
const hostId =
|
|
2511
|
+
const hostId = trimToUndefined5(hostIdFromTunnelPath(url.pathname));
|
|
2496
2512
|
if (!hostId) return json("host_tunnel_host_required", "Host tunnel id is required", 400);
|
|
2497
2513
|
const workspaceIds = workspaceIdsFromSearch(url);
|
|
2498
2514
|
if (!workspaceIds.includes(roomWorkspaceId)) {
|
|
@@ -2594,12 +2610,7 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
2594
2610
|
const emitTrace = (openedAt) => {
|
|
2595
2611
|
if (!trace || trace.emitted) return;
|
|
2596
2612
|
trace.emitted = true;
|
|
2597
|
-
sendSocket(pair.server, JSON.stringify(
|
|
2598
|
-
type: "relay.trace",
|
|
2599
|
-
wsUpstreamOpenMs: roundedMs(openedAt - trace.upstreamStartedAt),
|
|
2600
|
-
queuedFrames: trace.queueTimes.length,
|
|
2601
|
-
maxQueuedDelayMs: roundedMs(trace.queueTimes.reduce((max, at) => Math.max(max, openedAt - at), 0))
|
|
2602
|
-
}));
|
|
2613
|
+
sendSocket(pair.server, JSON.stringify(webSocketTraceFrame(trace.upstreamStartedAt, trace.queueTimes, openedAt)));
|
|
2603
2614
|
};
|
|
2604
2615
|
const activeTokenTimer = watchRuntimeAccessToken(client, (reason) => {
|
|
2605
2616
|
closeSocket(pair.server, 1008, reason);
|
|
@@ -2609,14 +2620,14 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
2609
2620
|
closed = true;
|
|
2610
2621
|
if (activeTokenTimer) clearInterval(activeTokenTimer);
|
|
2611
2622
|
clients.delete(client);
|
|
2612
|
-
closeSocket(upstream,
|
|
2623
|
+
closeSocket(upstream, closeCode(event?.code, 1e3), event?.reason ?? "Client WebSocket closed");
|
|
2613
2624
|
};
|
|
2614
2625
|
const cleanupUpstream = (event) => {
|
|
2615
2626
|
if (closed) return;
|
|
2616
2627
|
closed = true;
|
|
2617
2628
|
if (activeTokenTimer) clearInterval(activeTokenTimer);
|
|
2618
2629
|
clients.delete(client);
|
|
2619
|
-
closeSocket(pair.server,
|
|
2630
|
+
closeSocket(pair.server, closeCode(event?.code), event?.reason ?? "");
|
|
2620
2631
|
};
|
|
2621
2632
|
const flushQueue = () => {
|
|
2622
2633
|
upstreamOpen = true;
|
|
@@ -2896,6 +2907,7 @@ function createWorkspaceRelayDurableObjectRoom(options) {
|
|
|
2896
2907
|
},
|
|
2897
2908
|
webSocketClose(socket, code, reason) {
|
|
2898
2909
|
ensureHibernatedSocketsRebuilt();
|
|
2910
|
+
closeSocket(socket, closeCode(code, 1e3), reason);
|
|
2899
2911
|
const attachment = socketAttachment(socket);
|
|
2900
2912
|
if (!attachment) return;
|
|
2901
2913
|
if (attachment.kind === "host-tunnel") {
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export declare const TRACE_ID_HEADER = "x-claxedo-trace-id";
|
|
2
|
+
/**
|
|
3
|
+
* Set by a room on a sampled trace's response and consumed by the gateway: the
|
|
4
|
+
* gateway appends its own phases only behind this marker, never behind an
|
|
5
|
+
* upstream's `server-timing`, which a session host answers on every response.
|
|
6
|
+
*/
|
|
7
|
+
export declare const TRACE_SAMPLED_RESPONSE_HEADER = "x-claxedo-relay-sampled";
|
|
8
|
+
export type WorkspaceRelayTraceLogEvent = {
|
|
9
|
+
event: "workspace_relay_request";
|
|
10
|
+
traceId: string;
|
|
11
|
+
workspaceId?: string;
|
|
12
|
+
routeKind: string;
|
|
13
|
+
method: string;
|
|
14
|
+
path: string;
|
|
15
|
+
status: number;
|
|
16
|
+
totalMs: number;
|
|
17
|
+
phases: Record<string, number>;
|
|
18
|
+
};
|
|
19
|
+
export type RelayTraceSampling = {
|
|
20
|
+
traceSampleRate?: number;
|
|
21
|
+
traceForceHeaderSecret?: string;
|
|
22
|
+
random?: () => number;
|
|
23
|
+
};
|
|
24
|
+
export type RelayTrace = {
|
|
25
|
+
id: string;
|
|
26
|
+
sampled: boolean;
|
|
27
|
+
startedAt: number;
|
|
28
|
+
routeKind: string;
|
|
29
|
+
phases: Array<{
|
|
30
|
+
name: string;
|
|
31
|
+
ms: number;
|
|
32
|
+
}>;
|
|
33
|
+
};
|
|
34
|
+
export declare function traceRequest(request: Request, routeKind: string, options: RelayTraceSampling): RelayTrace;
|
|
35
|
+
export declare function traceSpan<T>(trace: RelayTrace | undefined, name: string, fn: () => Promise<T>): Promise<T>;
|
|
36
|
+
export declare function tracedRequest(request: Request, trace: RelayTrace, forwarded?: {
|
|
37
|
+
url: string;
|
|
38
|
+
headers: Headers;
|
|
39
|
+
}): Request;
|
|
40
|
+
export declare function serverTiming(phases: ReadonlyArray<{
|
|
41
|
+
name: string;
|
|
42
|
+
ms: number;
|
|
43
|
+
}>): string;
|
|
44
|
+
/** The `relay.trace` frame a client that opted in with `x-claxedo-relay-ws-trace` receives once the upstream opens. */
|
|
45
|
+
export declare function webSocketTraceFrame(upstreamStartedAt: number, queueTimes: readonly number[], openedAt: number): {
|
|
46
|
+
type: string;
|
|
47
|
+
wsUpstreamOpenMs: number;
|
|
48
|
+
queuedFrames: number;
|
|
49
|
+
maxQueuedDelayMs: number;
|
|
50
|
+
};
|
|
51
|
+
export declare function withTraceHeaders(response: Response, trace: RelayTrace): Response;
|
|
52
|
+
export declare function logTrace(options: {
|
|
53
|
+
traceLog?: (event: WorkspaceRelayTraceLogEvent) => void;
|
|
54
|
+
}, request: Request, workspaceId: string | undefined, trace: RelayTrace, response: Response): void;
|
|
@@ -1,18 +1,10 @@
|
|
|
1
1
|
import { Miniflare, type MiniflareOptions } from "miniflare";
|
|
2
2
|
/**
|
|
3
|
-
* Kill this process's workerd children
|
|
4
|
-
*
|
|
5
|
-
* direct children of this process whose command line names workerd
|
|
6
|
-
*
|
|
7
|
-
* runs files sequentially, the only children alive when a file's `afterAll`
|
|
8
|
-
* runs are ones whose tests have already finished.
|
|
3
|
+
* Kill this process's workerd children. `bun test` exits without running
|
|
4
|
+
* `process.on("exit")` handlers, so a child left running would outlive the
|
|
5
|
+
* run. Only direct children of this process whose command line names workerd
|
|
6
|
+
* can match.
|
|
9
7
|
*/
|
|
10
8
|
export declare function reapWorkerd(): void;
|
|
11
|
-
/**
|
|
12
|
-
* Boot workerd once and return the ready Miniflare. Call it exactly once per
|
|
13
|
-
* test file, from `beforeAll`, with a `workers` array hosting every
|
|
14
|
-
* configuration the file tests. Do NOT dispose the result — not in `afterAll`,
|
|
15
|
-
* not anywhere: disposal wedges every later boot in the process (see header).
|
|
16
|
-
* Tear down with `reapWorkerd()` in `afterAll` instead.
|
|
17
|
-
*/
|
|
9
|
+
/** Boots workerd once per process; tear it down with `reapWorkerd()`. */
|
|
18
10
|
export declare function bootWorkerd(options: MiniflareOptions): Promise<Miniflare>;
|
package/package.json
CHANGED
|
@@ -33,26 +33,26 @@
|
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
35
|
"name": "@claxedo/workspace-relay",
|
|
36
|
-
"version": "0.10.
|
|
36
|
+
"version": "0.10.2",
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsx scripts/build.ts",
|
|
39
39
|
"prepublishOnly": "npm run build",
|
|
40
40
|
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
41
|
-
"test": "mkdir -p .artifacts/unit && node ../../script/test-deadline.mjs bun test src scripts --path-ignore-patterns='**/relay-workerd-*' --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml && node ../../script/test-deadline.mjs
|
|
41
|
+
"test": "mkdir -p .artifacts/unit && node ../../script/test-deadline.mjs bun test src scripts --path-ignore-patterns='**/relay-workerd-*' --timeout 30000 --reporter=junit --reporter-outfile=.artifacts/unit/junit.xml && node ../../script/test-deadline.mjs node scripts/test-workerd.mjs"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@claxedo/helpers": "0.2.
|
|
45
|
-
"@claxedo/workspace-relay-protocol": "0.10.
|
|
44
|
+
"@claxedo/helpers": "0.2.2",
|
|
45
|
+
"@claxedo/workspace-relay-protocol": "0.10.2",
|
|
46
46
|
"jose": "6.0.11"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@tsconfig/node-lts": "22.0.4",
|
|
50
50
|
"@types/bun": "1.3.13",
|
|
51
51
|
"esbuild": "0.25.12",
|
|
52
|
-
"miniflare": "4.
|
|
52
|
+
"miniflare": "4.20260730.0",
|
|
53
53
|
"tsx": "4.22.3",
|
|
54
54
|
"typescript": "catalog:",
|
|
55
|
-
"wrangler": "4.
|
|
55
|
+
"wrangler": "4.116.0",
|
|
56
56
|
"@cloudflare/workers-types": "catalog:"
|
|
57
57
|
}
|
|
58
58
|
}
|