@rstreamlabs/tunnels 3.8.2 → 3.9.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/README.md +8 -0
- package/dist/index.d.mts +277 -245
- package/dist/index.d.ts +277 -245
- package/dist/index.js +140 -9
- package/dist/index.mjs +137 -9
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -84,7 +84,9 @@ __export(index_exports, {
|
|
|
84
84
|
resolveTunnelsCredentials: () => resolveTunnelsCredentials,
|
|
85
85
|
resolveTunnelsEngine: () => resolveTunnelsEngine,
|
|
86
86
|
resolveTunnelsRegion: () => resolveTunnelsRegion,
|
|
87
|
+
resolveWebTTYBrowserEndpoint: () => resolveWebTTYBrowserEndpoint,
|
|
87
88
|
resolveWebTTYControlRequestParamsSchema: () => resolveWebTTYControlRequestParamsSchema,
|
|
89
|
+
resolveWebTTYServerTransport: () => resolveWebTTYServerTransport,
|
|
88
90
|
signWebhookPayload: () => signWebhookPayload,
|
|
89
91
|
streamEndpointSchema: () => streamEndpointSchema,
|
|
90
92
|
streamEntrySchema: () => streamEntrySchema,
|
|
@@ -114,6 +116,7 @@ __export(index_exports, {
|
|
|
114
116
|
webTTYTransportSchema: () => webTTYTransportSchema,
|
|
115
117
|
webhookEventsSchema: () => webhookEventsSchema,
|
|
116
118
|
webttyServerSchema: () => webttyServerSchema,
|
|
119
|
+
webttyTransportSchema: () => webttyTransportSchema,
|
|
117
120
|
wsEventsSchema: () => wsEventsSchema
|
|
118
121
|
});
|
|
119
122
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -1775,6 +1778,52 @@ var import_rstream8 = require("@rstreamlabs/rstream");
|
|
|
1775
1778
|
var import_rstream9 = require("@rstreamlabs/rstream");
|
|
1776
1779
|
var import_rstream10 = require("@rstreamlabs/rstream");
|
|
1777
1780
|
var import_turn = require("@rstreamlabs/rstream/turn");
|
|
1781
|
+
|
|
1782
|
+
// src/control-plane-fetch.ts
|
|
1783
|
+
function createRequestLifetime(parent, timeoutMilliseconds) {
|
|
1784
|
+
const controller = new AbortController();
|
|
1785
|
+
const abort = () => controller.abort(parent.reason);
|
|
1786
|
+
if (parent.aborted) {
|
|
1787
|
+
abort();
|
|
1788
|
+
} else {
|
|
1789
|
+
parent.addEventListener("abort", abort, { once: true });
|
|
1790
|
+
}
|
|
1791
|
+
const timeout = setTimeout(
|
|
1792
|
+
() => controller.abort(new Error("Control plane request timed out.")),
|
|
1793
|
+
timeoutMilliseconds
|
|
1794
|
+
);
|
|
1795
|
+
return {
|
|
1796
|
+
close: () => {
|
|
1797
|
+
clearTimeout(timeout);
|
|
1798
|
+
parent.removeEventListener("abort", abort);
|
|
1799
|
+
},
|
|
1800
|
+
signal: controller.signal
|
|
1801
|
+
};
|
|
1802
|
+
}
|
|
1803
|
+
function createBoundedControlPlaneFetch(timeoutMilliseconds) {
|
|
1804
|
+
return async (input, init) => {
|
|
1805
|
+
const request = new Request(input, init);
|
|
1806
|
+
const headers = new Headers(request.headers);
|
|
1807
|
+
headers.set("Connection", "close");
|
|
1808
|
+
const lifetime = createRequestLifetime(request.signal, timeoutMilliseconds);
|
|
1809
|
+
try {
|
|
1810
|
+
const response = await globalThis.fetch(request, {
|
|
1811
|
+
headers,
|
|
1812
|
+
signal: lifetime.signal
|
|
1813
|
+
});
|
|
1814
|
+
const responseBody = response.body === null ? null : new Uint8Array(await response.arrayBuffer());
|
|
1815
|
+
return new Response(responseBody, {
|
|
1816
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
1817
|
+
status: response.status,
|
|
1818
|
+
statusText: response.statusText
|
|
1819
|
+
});
|
|
1820
|
+
} finally {
|
|
1821
|
+
lifetime.close();
|
|
1822
|
+
}
|
|
1823
|
+
};
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
// src/turn.ts
|
|
1778
1827
|
var z8 = __toESM(require("zod"));
|
|
1779
1828
|
var import_crypto = __toESM(require("crypto"));
|
|
1780
1829
|
var defaultTURNPort = 3478;
|
|
@@ -1782,6 +1831,8 @@ var defaultTURNSPort = 5349;
|
|
|
1782
1831
|
var defaultTURNCredentialTTLSeconds = 10 * 60;
|
|
1783
1832
|
var maxTURNCredentialTTLSeconds = 60 * 60;
|
|
1784
1833
|
var maxTURNKeyringBytes = 8 * 1024;
|
|
1834
|
+
var defaultAPIRequestTimeoutMilliseconds = 1e4;
|
|
1835
|
+
var maxAPIRequestTimeoutMilliseconds = 6e4;
|
|
1785
1836
|
var turnTokenClaimsSchema = z8.object({
|
|
1786
1837
|
exp: z8.number().int().optional(),
|
|
1787
1838
|
iat: z8.number().int().optional(),
|
|
@@ -1815,6 +1866,15 @@ function normalizeTTLSeconds(ttlSeconds) {
|
|
|
1815
1866
|
}
|
|
1816
1867
|
return ttl;
|
|
1817
1868
|
}
|
|
1869
|
+
function normalizeAPIRequestTimeoutMilliseconds(timeoutMilliseconds) {
|
|
1870
|
+
const timeout = timeoutMilliseconds ?? defaultAPIRequestTimeoutMilliseconds;
|
|
1871
|
+
if (!Number.isInteger(timeout) || timeout < 1 || timeout > maxAPIRequestTimeoutMilliseconds) {
|
|
1872
|
+
throw new Error(
|
|
1873
|
+
"TURN API request timeout must be an integer between 1 and 60000 milliseconds."
|
|
1874
|
+
);
|
|
1875
|
+
}
|
|
1876
|
+
return timeout;
|
|
1877
|
+
}
|
|
1818
1878
|
function createAPITURNCredentialsParams(ttlSeconds) {
|
|
1819
1879
|
return ttlSeconds === void 0 ? {} : { ttlSeconds: normalizeTTLSeconds(ttlSeconds) };
|
|
1820
1880
|
}
|
|
@@ -2008,11 +2068,16 @@ async function createAPITURNCredentials(options) {
|
|
|
2008
2068
|
"Project ID or project endpoint is required for TURN API mode."
|
|
2009
2069
|
);
|
|
2010
2070
|
}
|
|
2071
|
+
const requestTimeoutMilliseconds = normalizeAPIRequestTimeoutMilliseconds(
|
|
2072
|
+
options.requestTimeoutMilliseconds
|
|
2073
|
+
);
|
|
2074
|
+
const params = createAPITURNCredentialsParams(options.ttlSeconds);
|
|
2075
|
+
const request = options.fetch ?? createBoundedControlPlaneFetch(requestTimeoutMilliseconds);
|
|
2011
2076
|
const client = new import_rstream10.RstreamClient({
|
|
2012
2077
|
apiUrl: options.apiUrl,
|
|
2013
|
-
credentials: options.credentials
|
|
2078
|
+
credentials: options.credentials,
|
|
2079
|
+
fetch: request
|
|
2014
2080
|
});
|
|
2015
|
-
const params = createAPITURNCredentialsParams(options.ttlSeconds);
|
|
2016
2081
|
if (projectId) {
|
|
2017
2082
|
return await client.tunnels.projects.createTurnCredentials(
|
|
2018
2083
|
projectId,
|
|
@@ -2153,8 +2218,10 @@ async function createTURNCredentials(options) {
|
|
|
2153
2218
|
return await createAPITURNCredentials({
|
|
2154
2219
|
apiUrl: options.apiUrl,
|
|
2155
2220
|
credentials: controlPlaneCredentials,
|
|
2221
|
+
fetch: options.fetch,
|
|
2156
2222
|
projectEndpoint,
|
|
2157
2223
|
projectId: options.projectId,
|
|
2224
|
+
requestTimeoutMilliseconds: options.requestTimeoutMilliseconds,
|
|
2158
2225
|
ttlSeconds: options.ttlSeconds
|
|
2159
2226
|
});
|
|
2160
2227
|
}
|
|
@@ -2784,6 +2851,11 @@ var webttyEncryptionPolicySchema = z9.enum([
|
|
|
2784
2851
|
"explicit_key",
|
|
2785
2852
|
"workspace_managed"
|
|
2786
2853
|
]);
|
|
2854
|
+
var webttyTransportSchema = z9.enum([
|
|
2855
|
+
"plain",
|
|
2856
|
+
"websocket",
|
|
2857
|
+
"webtransport"
|
|
2858
|
+
]);
|
|
2787
2859
|
var webttyServerSchema = z9.object({
|
|
2788
2860
|
tunnel_id: z9.string(),
|
|
2789
2861
|
tunnel_name: z9.string().optional(),
|
|
@@ -2793,7 +2865,11 @@ var webttyServerSchema = z9.object({
|
|
|
2793
2865
|
publish: z9.boolean().optional(),
|
|
2794
2866
|
workspace_id: z9.string().optional(),
|
|
2795
2867
|
project_id: z9.string().optional(),
|
|
2796
|
-
tunnel_protocol: z9.enum(["http", "webtty"]),
|
|
2868
|
+
tunnel_protocol: z9.enum(["http", "webtty"]).optional(),
|
|
2869
|
+
tunnel_type: z9.enum(["bytestream", "datagram"]).optional(),
|
|
2870
|
+
http_version: z9.string().optional(),
|
|
2871
|
+
transport: webttyTransportSchema.optional(),
|
|
2872
|
+
transport_error: z9.string().optional(),
|
|
2797
2873
|
managed: z9.boolean(),
|
|
2798
2874
|
host: z9.string(),
|
|
2799
2875
|
token_auth: z9.boolean(),
|
|
@@ -2807,6 +2883,7 @@ var webttyServerSchema = z9.object({
|
|
|
2807
2883
|
exec_path: z9.string().optional(),
|
|
2808
2884
|
fs_path: z9.string().optional(),
|
|
2809
2885
|
fs_mode: webttyFSModeSchema.optional(),
|
|
2886
|
+
fs_backend: z9.enum(["webdav", "webrtc"]).optional(),
|
|
2810
2887
|
os_family: osFamilySchema.optional(),
|
|
2811
2888
|
arch: z9.string().optional(),
|
|
2812
2889
|
os_id: z9.string().optional(),
|
|
@@ -2833,11 +2910,12 @@ function webTTYCapabilitiesForLabels(labels) {
|
|
|
2833
2910
|
function webTTYHasCapability(capabilities, capability) {
|
|
2834
2911
|
return capabilities.includes(capability);
|
|
2835
2912
|
}
|
|
2836
|
-
function parser(tunnel) {
|
|
2913
|
+
function parser(tunnel, includePrivate) {
|
|
2837
2914
|
if (tunnel.status !== "online") return null;
|
|
2838
|
-
if (tunnel.publish !== true) return null;
|
|
2915
|
+
if (tunnel.publish !== true && !includePrivate) return null;
|
|
2839
2916
|
const managedProtocol = tunnel.protocol === "webtty";
|
|
2840
|
-
if (tunnel.protocol !== "http" && !managedProtocol)
|
|
2917
|
+
if (tunnel.protocol !== void 0 && tunnel.protocol !== "http" && !managedProtocol)
|
|
2918
|
+
return null;
|
|
2841
2919
|
const tunnelLabels = tunnel.labels ?? {};
|
|
2842
2920
|
if (!managedProtocol && tunnelLabels["application-protocol"] !== "rstream.webtty") {
|
|
2843
2921
|
return null;
|
|
@@ -2865,8 +2943,11 @@ function parser(tunnel) {
|
|
|
2865
2943
|
workspace_id: tunnel.workspace_id,
|
|
2866
2944
|
project_id: tunnel.project_id,
|
|
2867
2945
|
tunnel_protocol: tunnel.protocol,
|
|
2946
|
+
tunnel_type: tunnel.type,
|
|
2947
|
+
http_version: tunnel.http_version,
|
|
2948
|
+
...webTTYTransportMetadata(tunnelLabels["rstream.webtty.transport"]),
|
|
2868
2949
|
managed: managedProtocol,
|
|
2869
|
-
host: formatTunnelHost(tunnel),
|
|
2950
|
+
host: formatTunnelHost(tunnel) ?? "",
|
|
2870
2951
|
token_auth: tunnel.token_auth === true,
|
|
2871
2952
|
server_id: serverId || void 0,
|
|
2872
2953
|
server_name: serverName || void 0,
|
|
@@ -2877,6 +2958,7 @@ function parser(tunnel) {
|
|
|
2877
2958
|
capabilities,
|
|
2878
2959
|
exec_path: webTTYHasCapability(capabilities, "exec") ? tunnelLabels["rstream.webtty.exec.path"] ?? "/" : void 0,
|
|
2879
2960
|
fs_path: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.path"] ?? "/fs" : void 0,
|
|
2961
|
+
fs_backend: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.backend"] ?? "webdav" : void 0,
|
|
2880
2962
|
fs_mode: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.mode"] ?? "read-write" : void 0,
|
|
2881
2963
|
os_family: tunnelLabels["rstream.webtty.os_family"],
|
|
2882
2964
|
arch: tunnelLabels["rstream.webtty.arch"],
|
|
@@ -2892,8 +2974,54 @@ function parser(tunnel) {
|
|
|
2892
2974
|
if (!parsed.success) return null;
|
|
2893
2975
|
return parsed.data;
|
|
2894
2976
|
}
|
|
2895
|
-
function parseWebTTYServers(tunnels) {
|
|
2896
|
-
return tunnels.map((tunnel) => parser(tunnel)).filter((server) => server !== null);
|
|
2977
|
+
function parseWebTTYServers(tunnels, options = {}) {
|
|
2978
|
+
return tunnels.map((tunnel) => parser(tunnel, options.includePrivate === true)).filter((server) => server !== null);
|
|
2979
|
+
}
|
|
2980
|
+
function webTTYTransportMetadata(value) {
|
|
2981
|
+
if (value === void 0) return {};
|
|
2982
|
+
const parsed = webttyTransportSchema.safeParse(value);
|
|
2983
|
+
if (!parsed.success)
|
|
2984
|
+
return {
|
|
2985
|
+
transport_error: "Server advertises an invalid WebTTY transport."
|
|
2986
|
+
};
|
|
2987
|
+
return { transport: parsed.data };
|
|
2988
|
+
}
|
|
2989
|
+
function resolveWebTTYServerTransport(server, requested) {
|
|
2990
|
+
if (server.transport_error !== void 0)
|
|
2991
|
+
throw new Error(server.transport_error);
|
|
2992
|
+
const advertised = server.transport ?? (server.tunnel_type === "datagram" ? "webtransport" : void 0);
|
|
2993
|
+
if (advertised !== void 0 && server.tunnel_type !== void 0 && advertised === "webtransport" !== (server.tunnel_type === "datagram")) {
|
|
2994
|
+
throw new Error(
|
|
2995
|
+
`Server WebTTY transport ${advertised} conflicts with tunnel type ${server.tunnel_type}.`
|
|
2996
|
+
);
|
|
2997
|
+
}
|
|
2998
|
+
if (advertised === "webtransport" && server.http_version !== void 0 && server.http_version !== "h3") {
|
|
2999
|
+
throw new Error("Server WebTransport requires HTTP/3.");
|
|
3000
|
+
}
|
|
3001
|
+
if (requested !== void 0 && advertised !== void 0 && requested !== advertised) {
|
|
3002
|
+
throw new Error(
|
|
3003
|
+
`Requested WebTTY transport ${requested} conflicts with server transport ${advertised}.`
|
|
3004
|
+
);
|
|
3005
|
+
}
|
|
3006
|
+
return advertised ?? requested ?? "websocket";
|
|
3007
|
+
}
|
|
3008
|
+
function resolveWebTTYBrowserEndpoint(server) {
|
|
3009
|
+
const transport = resolveWebTTYServerTransport(server);
|
|
3010
|
+
if (transport === "plain")
|
|
3011
|
+
throw new Error(
|
|
3012
|
+
"Plain WebTTY requires a native client; this server cannot be opened in a browser."
|
|
3013
|
+
);
|
|
3014
|
+
if (server.publish === false || server.host === "")
|
|
3015
|
+
throw new Error(
|
|
3016
|
+
"Private WebTTY requires a native client and its rstrm:// address."
|
|
3017
|
+
);
|
|
3018
|
+
const url = new URL(
|
|
3019
|
+
`${transport === "webtransport" ? "https" : "wss"}://${server.host}`
|
|
3020
|
+
);
|
|
3021
|
+
const execPath = server.exec_path?.trim();
|
|
3022
|
+
if (execPath)
|
|
3023
|
+
url.pathname = execPath.startsWith("/") ? execPath : `/${execPath}`;
|
|
3024
|
+
return { url: url.toString(), transport };
|
|
2897
3025
|
}
|
|
2898
3026
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2899
3027
|
0 && (module.exports = {
|
|
@@ -2951,7 +3079,9 @@ function parseWebTTYServers(tunnels) {
|
|
|
2951
3079
|
resolveTunnelsCredentials,
|
|
2952
3080
|
resolveTunnelsEngine,
|
|
2953
3081
|
resolveTunnelsRegion,
|
|
3082
|
+
resolveWebTTYBrowserEndpoint,
|
|
2954
3083
|
resolveWebTTYControlRequestParamsSchema,
|
|
3084
|
+
resolveWebTTYServerTransport,
|
|
2955
3085
|
signWebhookPayload,
|
|
2956
3086
|
streamEndpointSchema,
|
|
2957
3087
|
streamEntrySchema,
|
|
@@ -2981,5 +3111,6 @@ function parseWebTTYServers(tunnels) {
|
|
|
2981
3111
|
webTTYTransportSchema,
|
|
2982
3112
|
webhookEventsSchema,
|
|
2983
3113
|
webttyServerSchema,
|
|
3114
|
+
webttyTransportSchema,
|
|
2984
3115
|
wsEventsSchema
|
|
2985
3116
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -412,6 +412,52 @@ import { isClientCredentials as isClientCredentials2 } from "@rstreamlabs/rstrea
|
|
|
412
412
|
import { isTokenCredentials as isTokenCredentials2 } from "@rstreamlabs/rstream";
|
|
413
413
|
import { RstreamClient as RstreamClient2 } from "@rstreamlabs/rstream";
|
|
414
414
|
import { turnCredentialsSchema } from "@rstreamlabs/rstream/turn";
|
|
415
|
+
|
|
416
|
+
// src/control-plane-fetch.ts
|
|
417
|
+
function createRequestLifetime(parent, timeoutMilliseconds) {
|
|
418
|
+
const controller = new AbortController();
|
|
419
|
+
const abort = () => controller.abort(parent.reason);
|
|
420
|
+
if (parent.aborted) {
|
|
421
|
+
abort();
|
|
422
|
+
} else {
|
|
423
|
+
parent.addEventListener("abort", abort, { once: true });
|
|
424
|
+
}
|
|
425
|
+
const timeout = setTimeout(
|
|
426
|
+
() => controller.abort(new Error("Control plane request timed out.")),
|
|
427
|
+
timeoutMilliseconds
|
|
428
|
+
);
|
|
429
|
+
return {
|
|
430
|
+
close: () => {
|
|
431
|
+
clearTimeout(timeout);
|
|
432
|
+
parent.removeEventListener("abort", abort);
|
|
433
|
+
},
|
|
434
|
+
signal: controller.signal
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
function createBoundedControlPlaneFetch(timeoutMilliseconds) {
|
|
438
|
+
return async (input, init) => {
|
|
439
|
+
const request = new Request(input, init);
|
|
440
|
+
const headers = new Headers(request.headers);
|
|
441
|
+
headers.set("Connection", "close");
|
|
442
|
+
const lifetime = createRequestLifetime(request.signal, timeoutMilliseconds);
|
|
443
|
+
try {
|
|
444
|
+
const response = await globalThis.fetch(request, {
|
|
445
|
+
headers,
|
|
446
|
+
signal: lifetime.signal
|
|
447
|
+
});
|
|
448
|
+
const responseBody = response.body === null ? null : new Uint8Array(await response.arrayBuffer());
|
|
449
|
+
return new Response(responseBody, {
|
|
450
|
+
headers: Object.fromEntries(response.headers.entries()),
|
|
451
|
+
status: response.status,
|
|
452
|
+
statusText: response.statusText
|
|
453
|
+
});
|
|
454
|
+
} finally {
|
|
455
|
+
lifetime.close();
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// src/turn.ts
|
|
415
461
|
import * as z from "zod";
|
|
416
462
|
import crypto from "crypto";
|
|
417
463
|
var defaultTURNPort = 3478;
|
|
@@ -419,6 +465,8 @@ var defaultTURNSPort = 5349;
|
|
|
419
465
|
var defaultTURNCredentialTTLSeconds = 10 * 60;
|
|
420
466
|
var maxTURNCredentialTTLSeconds = 60 * 60;
|
|
421
467
|
var maxTURNKeyringBytes = 8 * 1024;
|
|
468
|
+
var defaultAPIRequestTimeoutMilliseconds = 1e4;
|
|
469
|
+
var maxAPIRequestTimeoutMilliseconds = 6e4;
|
|
422
470
|
var turnTokenClaimsSchema = z.object({
|
|
423
471
|
exp: z.number().int().optional(),
|
|
424
472
|
iat: z.number().int().optional(),
|
|
@@ -452,6 +500,15 @@ function normalizeTTLSeconds(ttlSeconds) {
|
|
|
452
500
|
}
|
|
453
501
|
return ttl;
|
|
454
502
|
}
|
|
503
|
+
function normalizeAPIRequestTimeoutMilliseconds(timeoutMilliseconds) {
|
|
504
|
+
const timeout = timeoutMilliseconds ?? defaultAPIRequestTimeoutMilliseconds;
|
|
505
|
+
if (!Number.isInteger(timeout) || timeout < 1 || timeout > maxAPIRequestTimeoutMilliseconds) {
|
|
506
|
+
throw new Error(
|
|
507
|
+
"TURN API request timeout must be an integer between 1 and 60000 milliseconds."
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
return timeout;
|
|
511
|
+
}
|
|
455
512
|
function createAPITURNCredentialsParams(ttlSeconds) {
|
|
456
513
|
return ttlSeconds === void 0 ? {} : { ttlSeconds: normalizeTTLSeconds(ttlSeconds) };
|
|
457
514
|
}
|
|
@@ -645,11 +702,16 @@ async function createAPITURNCredentials(options) {
|
|
|
645
702
|
"Project ID or project endpoint is required for TURN API mode."
|
|
646
703
|
);
|
|
647
704
|
}
|
|
705
|
+
const requestTimeoutMilliseconds = normalizeAPIRequestTimeoutMilliseconds(
|
|
706
|
+
options.requestTimeoutMilliseconds
|
|
707
|
+
);
|
|
708
|
+
const params = createAPITURNCredentialsParams(options.ttlSeconds);
|
|
709
|
+
const request = options.fetch ?? createBoundedControlPlaneFetch(requestTimeoutMilliseconds);
|
|
648
710
|
const client = new RstreamClient2({
|
|
649
711
|
apiUrl: options.apiUrl,
|
|
650
|
-
credentials: options.credentials
|
|
712
|
+
credentials: options.credentials,
|
|
713
|
+
fetch: request
|
|
651
714
|
});
|
|
652
|
-
const params = createAPITURNCredentialsParams(options.ttlSeconds);
|
|
653
715
|
if (projectId) {
|
|
654
716
|
return await client.tunnels.projects.createTurnCredentials(
|
|
655
717
|
projectId,
|
|
@@ -790,8 +852,10 @@ async function createTURNCredentials(options) {
|
|
|
790
852
|
return await createAPITURNCredentials({
|
|
791
853
|
apiUrl: options.apiUrl,
|
|
792
854
|
credentials: controlPlaneCredentials,
|
|
855
|
+
fetch: options.fetch,
|
|
793
856
|
projectEndpoint,
|
|
794
857
|
projectId: options.projectId,
|
|
858
|
+
requestTimeoutMilliseconds: options.requestTimeoutMilliseconds,
|
|
795
859
|
ttlSeconds: options.ttlSeconds
|
|
796
860
|
});
|
|
797
861
|
}
|
|
@@ -1421,6 +1485,11 @@ var webttyEncryptionPolicySchema = z2.enum([
|
|
|
1421
1485
|
"explicit_key",
|
|
1422
1486
|
"workspace_managed"
|
|
1423
1487
|
]);
|
|
1488
|
+
var webttyTransportSchema = z2.enum([
|
|
1489
|
+
"plain",
|
|
1490
|
+
"websocket",
|
|
1491
|
+
"webtransport"
|
|
1492
|
+
]);
|
|
1424
1493
|
var webttyServerSchema = z2.object({
|
|
1425
1494
|
tunnel_id: z2.string(),
|
|
1426
1495
|
tunnel_name: z2.string().optional(),
|
|
@@ -1430,7 +1499,11 @@ var webttyServerSchema = z2.object({
|
|
|
1430
1499
|
publish: z2.boolean().optional(),
|
|
1431
1500
|
workspace_id: z2.string().optional(),
|
|
1432
1501
|
project_id: z2.string().optional(),
|
|
1433
|
-
tunnel_protocol: z2.enum(["http", "webtty"]),
|
|
1502
|
+
tunnel_protocol: z2.enum(["http", "webtty"]).optional(),
|
|
1503
|
+
tunnel_type: z2.enum(["bytestream", "datagram"]).optional(),
|
|
1504
|
+
http_version: z2.string().optional(),
|
|
1505
|
+
transport: webttyTransportSchema.optional(),
|
|
1506
|
+
transport_error: z2.string().optional(),
|
|
1434
1507
|
managed: z2.boolean(),
|
|
1435
1508
|
host: z2.string(),
|
|
1436
1509
|
token_auth: z2.boolean(),
|
|
@@ -1444,6 +1517,7 @@ var webttyServerSchema = z2.object({
|
|
|
1444
1517
|
exec_path: z2.string().optional(),
|
|
1445
1518
|
fs_path: z2.string().optional(),
|
|
1446
1519
|
fs_mode: webttyFSModeSchema.optional(),
|
|
1520
|
+
fs_backend: z2.enum(["webdav", "webrtc"]).optional(),
|
|
1447
1521
|
os_family: osFamilySchema.optional(),
|
|
1448
1522
|
arch: z2.string().optional(),
|
|
1449
1523
|
os_id: z2.string().optional(),
|
|
@@ -1470,11 +1544,12 @@ function webTTYCapabilitiesForLabels(labels) {
|
|
|
1470
1544
|
function webTTYHasCapability(capabilities, capability) {
|
|
1471
1545
|
return capabilities.includes(capability);
|
|
1472
1546
|
}
|
|
1473
|
-
function parser(tunnel) {
|
|
1547
|
+
function parser(tunnel, includePrivate) {
|
|
1474
1548
|
if (tunnel.status !== "online") return null;
|
|
1475
|
-
if (tunnel.publish !== true) return null;
|
|
1549
|
+
if (tunnel.publish !== true && !includePrivate) return null;
|
|
1476
1550
|
const managedProtocol = tunnel.protocol === "webtty";
|
|
1477
|
-
if (tunnel.protocol !== "http" && !managedProtocol)
|
|
1551
|
+
if (tunnel.protocol !== void 0 && tunnel.protocol !== "http" && !managedProtocol)
|
|
1552
|
+
return null;
|
|
1478
1553
|
const tunnelLabels = tunnel.labels ?? {};
|
|
1479
1554
|
if (!managedProtocol && tunnelLabels["application-protocol"] !== "rstream.webtty") {
|
|
1480
1555
|
return null;
|
|
@@ -1502,8 +1577,11 @@ function parser(tunnel) {
|
|
|
1502
1577
|
workspace_id: tunnel.workspace_id,
|
|
1503
1578
|
project_id: tunnel.project_id,
|
|
1504
1579
|
tunnel_protocol: tunnel.protocol,
|
|
1580
|
+
tunnel_type: tunnel.type,
|
|
1581
|
+
http_version: tunnel.http_version,
|
|
1582
|
+
...webTTYTransportMetadata(tunnelLabels["rstream.webtty.transport"]),
|
|
1505
1583
|
managed: managedProtocol,
|
|
1506
|
-
host: formatTunnelHost(tunnel),
|
|
1584
|
+
host: formatTunnelHost(tunnel) ?? "",
|
|
1507
1585
|
token_auth: tunnel.token_auth === true,
|
|
1508
1586
|
server_id: serverId || void 0,
|
|
1509
1587
|
server_name: serverName || void 0,
|
|
@@ -1514,6 +1592,7 @@ function parser(tunnel) {
|
|
|
1514
1592
|
capabilities,
|
|
1515
1593
|
exec_path: webTTYHasCapability(capabilities, "exec") ? tunnelLabels["rstream.webtty.exec.path"] ?? "/" : void 0,
|
|
1516
1594
|
fs_path: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.path"] ?? "/fs" : void 0,
|
|
1595
|
+
fs_backend: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.backend"] ?? "webdav" : void 0,
|
|
1517
1596
|
fs_mode: webTTYHasCapability(capabilities, "fs") ? tunnelLabels["rstream.webtty.fs.mode"] ?? "read-write" : void 0,
|
|
1518
1597
|
os_family: tunnelLabels["rstream.webtty.os_family"],
|
|
1519
1598
|
arch: tunnelLabels["rstream.webtty.arch"],
|
|
@@ -1529,8 +1608,54 @@ function parser(tunnel) {
|
|
|
1529
1608
|
if (!parsed.success) return null;
|
|
1530
1609
|
return parsed.data;
|
|
1531
1610
|
}
|
|
1532
|
-
function parseWebTTYServers(tunnels) {
|
|
1533
|
-
return tunnels.map((tunnel) => parser(tunnel)).filter((server) => server !== null);
|
|
1611
|
+
function parseWebTTYServers(tunnels, options = {}) {
|
|
1612
|
+
return tunnels.map((tunnel) => parser(tunnel, options.includePrivate === true)).filter((server) => server !== null);
|
|
1613
|
+
}
|
|
1614
|
+
function webTTYTransportMetadata(value) {
|
|
1615
|
+
if (value === void 0) return {};
|
|
1616
|
+
const parsed = webttyTransportSchema.safeParse(value);
|
|
1617
|
+
if (!parsed.success)
|
|
1618
|
+
return {
|
|
1619
|
+
transport_error: "Server advertises an invalid WebTTY transport."
|
|
1620
|
+
};
|
|
1621
|
+
return { transport: parsed.data };
|
|
1622
|
+
}
|
|
1623
|
+
function resolveWebTTYServerTransport(server, requested) {
|
|
1624
|
+
if (server.transport_error !== void 0)
|
|
1625
|
+
throw new Error(server.transport_error);
|
|
1626
|
+
const advertised = server.transport ?? (server.tunnel_type === "datagram" ? "webtransport" : void 0);
|
|
1627
|
+
if (advertised !== void 0 && server.tunnel_type !== void 0 && advertised === "webtransport" !== (server.tunnel_type === "datagram")) {
|
|
1628
|
+
throw new Error(
|
|
1629
|
+
`Server WebTTY transport ${advertised} conflicts with tunnel type ${server.tunnel_type}.`
|
|
1630
|
+
);
|
|
1631
|
+
}
|
|
1632
|
+
if (advertised === "webtransport" && server.http_version !== void 0 && server.http_version !== "h3") {
|
|
1633
|
+
throw new Error("Server WebTransport requires HTTP/3.");
|
|
1634
|
+
}
|
|
1635
|
+
if (requested !== void 0 && advertised !== void 0 && requested !== advertised) {
|
|
1636
|
+
throw new Error(
|
|
1637
|
+
`Requested WebTTY transport ${requested} conflicts with server transport ${advertised}.`
|
|
1638
|
+
);
|
|
1639
|
+
}
|
|
1640
|
+
return advertised ?? requested ?? "websocket";
|
|
1641
|
+
}
|
|
1642
|
+
function resolveWebTTYBrowserEndpoint(server) {
|
|
1643
|
+
const transport = resolveWebTTYServerTransport(server);
|
|
1644
|
+
if (transport === "plain")
|
|
1645
|
+
throw new Error(
|
|
1646
|
+
"Plain WebTTY requires a native client; this server cannot be opened in a browser."
|
|
1647
|
+
);
|
|
1648
|
+
if (server.publish === false || server.host === "")
|
|
1649
|
+
throw new Error(
|
|
1650
|
+
"Private WebTTY requires a native client and its rstrm:// address."
|
|
1651
|
+
);
|
|
1652
|
+
const url = new URL(
|
|
1653
|
+
`${transport === "webtransport" ? "https" : "wss"}://${server.host}`
|
|
1654
|
+
);
|
|
1655
|
+
const execPath = server.exec_path?.trim();
|
|
1656
|
+
if (execPath)
|
|
1657
|
+
url.pathname = execPath.startsWith("/") ? execPath : `/${execPath}`;
|
|
1658
|
+
return { url: url.toString(), transport };
|
|
1534
1659
|
}
|
|
1535
1660
|
export {
|
|
1536
1661
|
RstreamAuthResource,
|
|
@@ -1587,7 +1712,9 @@ export {
|
|
|
1587
1712
|
resolveTunnelsCredentials,
|
|
1588
1713
|
resolveTunnelsEngine,
|
|
1589
1714
|
resolveTunnelsRegion,
|
|
1715
|
+
resolveWebTTYBrowserEndpoint,
|
|
1590
1716
|
resolveWebTTYControlRequestParamsSchema,
|
|
1717
|
+
resolveWebTTYServerTransport,
|
|
1591
1718
|
signWebhookPayload,
|
|
1592
1719
|
streamEndpointSchema,
|
|
1593
1720
|
streamEntrySchema,
|
|
@@ -1617,5 +1744,6 @@ export {
|
|
|
1617
1744
|
webTTYTransportSchema,
|
|
1618
1745
|
webhookEventsSchema,
|
|
1619
1746
|
webttyServerSchema,
|
|
1747
|
+
webttyTransportSchema,
|
|
1620
1748
|
wsEventsSchema
|
|
1621
1749
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rstreamlabs/tunnels",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.9.0",
|
|
4
4
|
"description": "JS/TS SDK for the rstream Engine API and tunnel runtime APIs.",
|
|
5
5
|
"author": "@uartnet <hello@rstream.io>",
|
|
6
6
|
"repository": {
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
],
|
|
31
31
|
"scripts": {
|
|
32
32
|
"build:watch": "tsup --watch",
|
|
33
|
-
"build": "tsup",
|
|
34
|
-
"clean": "rm -rf dist",
|
|
33
|
+
"build": "tsc --emitDeclarationOnly --declarationMap false --rootDir src --outDir .generated/types && tsup",
|
|
34
|
+
"clean": "rm -rf dist .generated/types",
|
|
35
35
|
"lint": "eslint \"./**/*.ts*\"",
|
|
36
36
|
"pretest": "npm --prefix ../rstream run build && npm run build",
|
|
37
37
|
"test": "node --test ./test/**/*.test.cjs",
|
|
38
38
|
"type-check": "tsc --noEmit"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@turbo/gen": "^2.10.
|
|
41
|
+
"@turbo/gen": "^2.10.11",
|
|
42
42
|
"@types/node": "^26",
|
|
43
43
|
"eslint": "9.39.5",
|
|
44
44
|
"eslint-config": "*",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"typescript-config": "*"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@rstreamlabs/rstream": "3.8.
|
|
50
|
+
"@rstreamlabs/rstream": "3.8.2",
|
|
51
51
|
"@types/jsonwebtoken": "^9.0.10",
|
|
52
52
|
"jsonwebtoken": "^9.0.3",
|
|
53
53
|
"zod": "^4.4.3"
|