@floegence/flowersec-core 0.26.0 → 2.0.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 +91 -22
- package/THIRD_PARTY_NOTICES.md +141 -0
- package/dist/browser/connectSession.d.ts +13 -0
- package/dist/browser/connectSession.js +40 -0
- package/dist/browser/index.d.ts +3 -10
- package/dist/browser/index.js +2 -5
- package/dist/browser/runtimeCapability.d.ts +13 -0
- package/dist/browser/runtimeCapability.js +27 -0
- package/dist/browser/sessionRuntime.d.ts +2 -0
- package/dist/browser/sessionRuntime.js +21 -0
- package/dist/browser/webTransportClient.d.ts +8 -0
- package/dist/browser/webTransportClient.js +55 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +126 -0
- package/dist/cliSpendMarker.d.ts +1 -0
- package/dist/cliSpendMarker.js +22 -0
- package/dist/connectionController.d.ts +54 -0
- package/dist/connectionController.js +438 -0
- package/dist/connector/adapters/webSocketCandidate.d.ts +7 -0
- package/dist/connector/adapters/webSocketCandidate.js +116 -0
- package/dist/connector/adapters/webTransportCandidate.d.ts +5 -0
- package/dist/connector/adapters/webTransportCandidate.js +73 -0
- package/dist/connector/admissionCommit.d.ts +31 -0
- package/dist/connector/admissionCommit.js +239 -0
- package/dist/connector/sessionAcceptor.d.ts +16 -0
- package/dist/connector/sessionAcceptor.js +114 -0
- package/dist/connector/sessionConfig.d.ts +3 -0
- package/dist/connector/sessionConfig.js +30 -0
- package/dist/connector/sessionConnector.d.ts +45 -0
- package/dist/connector/sessionConnector.js +603 -0
- package/dist/defaults.d.ts +7 -7
- package/dist/defaults.js +7 -7
- package/dist/facade.d.ts +12 -30
- package/dist/facade.js +6 -30
- package/dist/node/connectSession.d.ts +21 -0
- package/dist/node/connectSession.js +62 -0
- package/dist/node/index.d.ts +3 -11
- package/dist/node/index.js +2 -8
- package/dist/node/runtimeCapability.d.ts +7 -0
- package/dist/node/runtimeCapability.js +10 -0
- package/dist/node/sessionRuntime.d.ts +2 -0
- package/dist/node/sessionRuntime.js +13 -0
- package/dist/node/webTransportClient.d.ts +9 -0
- package/dist/node/webTransportClient.js +56 -0
- package/dist/node/webTransportServer.d.ts +22 -0
- package/dist/node/webTransportServer.js +100 -0
- package/dist/node/wsFactory.d.ts +3 -1
- package/dist/node/wsFactory.js +4 -3
- package/dist/proxy/controllerGuard.d.ts +4 -4
- package/dist/proxy/controllerGuard.js +83 -177
- package/dist/proxy/disableUpstreamServiceWorkerRegister.d.ts +3 -1
- package/dist/proxy/disableUpstreamServiceWorkerRegister.js +19 -17
- package/dist/proxy/index.d.ts +16 -15
- package/dist/proxy/index.js +8 -14
- package/dist/proxy/integration.d.ts +23 -71
- package/dist/proxy/integration.js +37 -340
- package/dist/proxy/registerServiceWorker.d.ts +1 -10
- package/dist/proxy/registerServiceWorker.js +43 -105
- package/dist/proxy/runtime.d.ts +3 -63
- package/dist/proxy/runtime.js +349 -462
- package/dist/proxy/scope.d.ts +6 -0
- package/dist/proxy/scope.js +131 -0
- package/dist/proxy/serviceWorker.d.ts +5 -16
- package/dist/proxy/serviceWorker.js +230 -617
- package/dist/proxy/stream.d.ts +9 -0
- package/dist/proxy/stream.js +40 -0
- package/dist/proxy/types.d.ts +78 -30
- package/dist/proxy/windowBridge.d.ts +61 -0
- package/dist/proxy/windowBridge.js +344 -0
- package/dist/proxy/wsPatch.d.ts +4 -14
- package/dist/proxy/wsPatch.js +173 -268
- package/dist/public/artifact.d.ts +11 -0
- package/dist/public/artifact.js +44 -0
- package/dist/public/artifactLease.d.ts +10 -0
- package/dist/public/artifactLease.js +48 -0
- package/dist/public/connectError.d.ts +6 -0
- package/dist/public/connectError.js +9 -0
- package/dist/public/contract.d.ts +83 -0
- package/dist/public/contract.js +17 -0
- package/dist/public/streamMetadata.d.ts +10 -0
- package/dist/public/streamMetadata.js +40 -0
- package/dist/rpc/caller.d.ts +1 -1
- package/dist/rpc/client.d.ts +1 -4
- package/dist/rpc/client.js +0 -21
- package/dist/rpc/rpcProxy.d.ts +1 -1
- package/dist/rpc/server.d.ts +1 -1
- package/dist/rpc/validate.d.ts +1 -1
- package/dist/rpc/wire.d.ts +15 -0
- package/dist/runtime/errors.d.ts +6 -0
- package/dist/runtime/errors.js +10 -0
- package/dist/transport/webSocketAdapter.d.ts +24 -0
- package/dist/transport/webSocketAdapter.js +234 -0
- package/dist/transport/webTransportAdapter.d.ts +27 -0
- package/dist/transport/webTransportAdapter.js +519 -0
- package/dist/utils/errors.d.ts +2 -20
- package/dist/utils/errors.js +73 -20
- package/dist/v2/admissionError.d.ts +4 -0
- package/dist/v2/admissionError.js +8 -0
- package/dist/v2/artifact.d.ts +128 -0
- package/dist/v2/artifact.js +1126 -0
- package/dist/v2/artifactLease.d.ts +1 -0
- package/dist/v2/artifactLease.js +7 -0
- package/dist/v2/capability.d.ts +29 -0
- package/dist/v2/capability.js +190 -0
- package/dist/v2/carrier.d.ts +79 -0
- package/dist/v2/carrier.js +405 -0
- package/dist/v2/contract.d.ts +37 -0
- package/dist/v2/contract.js +1 -0
- package/dist/v2/handshake.d.ts +80 -0
- package/dist/v2/handshake.js +470 -0
- package/dist/v2/opaqueArtifact.d.ts +1 -0
- package/dist/v2/opaqueArtifact.js +2 -0
- package/dist/v2/protocol.d.ts +99 -0
- package/dist/v2/protocol.js +739 -0
- package/dist/v2/publicSession.d.ts +1 -0
- package/dist/v2/publicSession.js +179 -0
- package/dist/v2/retryDisposition.d.ts +16 -0
- package/dist/v2/retryDisposition.js +30 -0
- package/dist/v2/session.d.ts +277 -0
- package/dist/v2/session.js +1879 -0
- package/dist/v2/streamLifetimeLedger.d.ts +31 -0
- package/dist/v2/streamLifetimeLedger.js +131 -0
- package/dist/v2/streamMetadata.d.ts +1 -0
- package/dist/v2/streamMetadata.js +7 -0
- package/dist/v2/unreliableMessage.d.ts +43 -0
- package/dist/v2/unreliableMessage.js +252 -0
- package/dist/vendor/tr46.d.ts +10 -0
- package/dist/vendor/tr46.js +501 -0
- package/dist/ws-client/binaryTransport.d.ts +0 -3
- package/dist/ws-client/binaryTransport.js +15 -39
- package/dist/yamux/errors.d.ts +0 -4
- package/dist/yamux/errors.js +0 -9
- package/dist/yamux/session.d.ts +0 -5
- package/dist/yamux/session.js +4 -70
- package/dist/yamux/stream.d.ts +1 -0
- package/dist/yamux/stream.js +4 -0
- package/package.json +29 -83
- package/sbom/cyclonedx.json +2764 -0
- package/sbom/spdx.json +1947 -0
- package/YAMUX_ALIGNMENT.md +0 -127
- package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +0 -12
- package/dist/_examples/flowersec/demo/v1.facade.gen.js +0 -15
- package/dist/_examples/flowersec/demo/v1.gen.d.ts +0 -16
- package/dist/_examples/flowersec/demo/v1.gen.js +0 -86
- package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +0 -11
- package/dist/_examples/flowersec/demo/v1.rpc.gen.js +0 -22
- package/dist/browser/connect.d.ts +0 -12
- package/dist/browser/connect.js +0 -31
- package/dist/browser/controlplane.d.ts +0 -15
- package/dist/browser/controlplane.js +0 -64
- package/dist/browser/reconnectConfig.d.ts +0 -15
- package/dist/browser/reconnectConfig.js +0 -20
- package/dist/client-connect/common.d.ts +0 -26
- package/dist/client-connect/common.js +0 -167
- package/dist/client-connect/connectCore.d.ts +0 -61
- package/dist/client-connect/connectCore.js +0 -572
- package/dist/client-connect/contract.d.ts +0 -8
- package/dist/client-connect/contract.js +0 -51
- package/dist/client-connect/termination.d.ts +0 -6
- package/dist/client-connect/termination.js +0 -7
- package/dist/client-connect/transportSecurity.d.ts +0 -27
- package/dist/client-connect/transportSecurity.js +0 -199
- package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
- package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
- package/dist/client.d.ts +0 -23
- package/dist/connect/artifact.d.ts +0 -37
- package/dist/connect/artifact.js +0 -217
- package/dist/connect/internalNormalize.d.ts +0 -22
- package/dist/connect/internalNormalize.js +0 -173
- package/dist/controlplane/channelInit.d.ts +0 -27
- package/dist/controlplane/channelInit.js +0 -116
- package/dist/controlplane/http.d.ts +0 -16
- package/dist/controlplane/http.js +0 -59
- package/dist/controlplane/index.d.ts +0 -6
- package/dist/controlplane/index.js +0 -5
- package/dist/controlplane/issuer.d.ts +0 -18
- package/dist/controlplane/issuer.js +0 -113
- package/dist/controlplane/request.d.ts +0 -51
- package/dist/controlplane/request.js +0 -273
- package/dist/controlplane/token.d.ts +0 -33
- package/dist/controlplane/token.js +0 -119
- package/dist/direct-client/connect.d.ts +0 -9
- package/dist/direct-client/connect.js +0 -90
- package/dist/direct-client/index.d.ts +0 -1
- package/dist/direct-client/index.js +0 -1
- package/dist/e2ee/constants.d.ts +0 -9
- package/dist/e2ee/constants.js +0 -18
- package/dist/e2ee/errors.d.ts +0 -5
- package/dist/e2ee/errors.js +0 -8
- package/dist/e2ee/framing.d.ts +0 -12
- package/dist/e2ee/framing.js +0 -57
- package/dist/e2ee/handshake.d.ts +0 -88
- package/dist/e2ee/handshake.js +0 -353
- package/dist/e2ee/index.d.ts +0 -7
- package/dist/e2ee/index.js +0 -7
- package/dist/e2ee/kdf.d.ts +0 -15
- package/dist/e2ee/kdf.js +0 -39
- package/dist/e2ee/record.d.ts +0 -11
- package/dist/e2ee/record.js +0 -76
- package/dist/e2ee/secureChannel.d.ts +0 -92
- package/dist/e2ee/secureChannel.js +0 -328
- package/dist/e2ee/transcript.d.ts +0 -23
- package/dist/e2ee/transcript.js +0 -31
- package/dist/endpoint/index.d.ts +0 -84
- package/dist/endpoint/index.js +0 -557
- package/dist/endpoint/node.d.ts +0 -9
- package/dist/endpoint/node.js +0 -51
- package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
- package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
- package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
- package/dist/gen/flowersec/direct/v1.gen.js +0 -101
- package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
- package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
- package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
- package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
- package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
- package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
- package/dist/index.d.ts +0 -22
- package/dist/index.js +0 -22
- package/dist/node/connect.d.ts +0 -12
- package/dist/node/connect.js +0 -29
- package/dist/node/reconnectConfig.d.ts +0 -16
- package/dist/node/reconnectConfig.js +0 -20
- package/dist/observability/index.d.ts +0 -1
- package/dist/observability/index.js +0 -1
- package/dist/observability/observer.d.ts +0 -52
- package/dist/observability/observer.js +0 -314
- package/dist/proxy/appWindow.d.ts +0 -37
- package/dist/proxy/appWindow.js +0 -273
- package/dist/proxy/bootstrap.d.ts +0 -35
- package/dist/proxy/bootstrap.js +0 -119
- package/dist/proxy/constants.d.ts +0 -7
- package/dist/proxy/constants.js +0 -8
- package/dist/proxy/controllerWindow.d.ts +0 -12
- package/dist/proxy/controllerWindow.js +0 -384
- package/dist/proxy/cookieJar.d.ts +0 -7
- package/dist/proxy/cookieJar.js +0 -136
- package/dist/proxy/headerPolicy.d.ts +0 -14
- package/dist/proxy/headerPolicy.js +0 -134
- package/dist/proxy/portStream.d.ts +0 -5
- package/dist/proxy/portStream.js +0 -256
- package/dist/proxy/preset.d.ts +0 -29
- package/dist/proxy/preset.js +0 -111
- package/dist/proxy/runtimeScope.d.ts +0 -57
- package/dist/proxy/runtimeScope.js +0 -218
- package/dist/proxy/server.d.ts +0 -28
- package/dist/proxy/server.js +0 -833
- package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
- package/dist/proxy/windowBridgeProtocol.js +0 -11
- package/dist/reconnect/artifactControlplane.d.ts +0 -16
- package/dist/reconnect/artifactControlplane.js +0 -35
- package/dist/reconnect/index.d.ts +0 -36
- package/dist/reconnect/index.js +0 -388
- package/dist/rpc-proxy/index.d.ts +0 -1
- package/dist/rpc-proxy/index.js +0 -1
- package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
- package/dist/rpc-proxy/rpcProxy.js +0 -1
- package/dist/streamhello/index.d.ts +0 -1
- package/dist/streamhello/index.js +0 -1
- package/dist/streamhello/streamHello.d.ts +0 -3
- package/dist/streamhello/streamHello.js +0 -13
- package/dist/streamio/index.d.ts +0 -32
- package/dist/streamio/index.js +0 -87
- package/dist/tunnel-client/connect.d.ts +0 -9
- package/dist/tunnel-client/connect.js +0 -157
- package/dist/tunnel-client/index.d.ts +0 -1
- package/dist/tunnel-client/index.js +0 -1
- package/dist/ws/index.d.ts +0 -1
- package/dist/ws/index.js +0 -1
- /package/dist/{client.js → rpc/wire.js} +0 -0
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { type YamuxLimits } from "../yamux/session.js";
|
|
2
|
-
import { type ClientObserverLike } from "../observability/observer.js";
|
|
3
|
-
import { type WebSocketLike, type WebSocketLimits } from "../ws-client/binaryTransport.js";
|
|
4
|
-
import type { ClientInternal } from "../client.js";
|
|
5
|
-
import type { ConnectScopeResolverMap } from "../connect/internalNormalize.js";
|
|
6
|
-
import { type TransportSecurityPolicy } from "./transportSecurity.js";
|
|
7
|
-
export type LivenessOptions = Readonly<{
|
|
8
|
-
intervalMs?: number;
|
|
9
|
-
timeoutMs?: number;
|
|
10
|
-
}>;
|
|
11
|
-
export type ConnectOptionsBase = Readonly<{
|
|
12
|
-
/** Explicit Origin value (required). In browsers this must match window.location.origin. */
|
|
13
|
-
origin: string;
|
|
14
|
-
/** Optional AbortSignal to cancel connect/handshake. */
|
|
15
|
-
signal?: AbortSignal;
|
|
16
|
-
/** Optional websocket connect timeout in milliseconds (0 disables). */
|
|
17
|
-
connectTimeoutMs?: number;
|
|
18
|
-
/** Optional total E2EE handshake timeout in milliseconds (0 disables). */
|
|
19
|
-
handshakeTimeoutMs?: number;
|
|
20
|
-
/** Feature bitset advertised during the E2EE handshake. */
|
|
21
|
-
clientFeatures?: number;
|
|
22
|
-
/** Maximum allowed bytes for handshake payloads (0 uses default). */
|
|
23
|
-
maxHandshakePayload?: number;
|
|
24
|
-
/** Maximum encrypted record size on the wire (0 uses default). */
|
|
25
|
-
maxRecordBytes?: number;
|
|
26
|
-
/** Maximum queued inbound plaintext bytes in the secure channel (0 uses default). */
|
|
27
|
-
maxBufferedBytes?: number;
|
|
28
|
-
/** Maximum queued outbound plaintext bytes in the secure channel (default 4 MiB; 0 uses default). */
|
|
29
|
-
maxOutboundBufferedBytes?: number;
|
|
30
|
-
/** Preferred plaintext bytes per outbound encrypted record (default 64 KiB). */
|
|
31
|
-
outboundRecordChunkBytes?: number;
|
|
32
|
-
/** WebSocket inbound and outbound queue limits. */
|
|
33
|
-
webSocketLimits?: Partial<WebSocketLimits>;
|
|
34
|
-
/** Yamux stream, frame, receive-memory, and per-stream write-queue limits. */
|
|
35
|
-
yamuxLimits?: Partial<YamuxLimits>;
|
|
36
|
-
/** Optional factory for creating the WebSocket instance. */
|
|
37
|
-
wsFactory?: (url: string, origin: string) => WebSocketLike;
|
|
38
|
-
/** Optional observer for client metrics. */
|
|
39
|
-
observer?: ClientObserverLike;
|
|
40
|
-
/** Policy evaluated before any WebSocket network activity. */
|
|
41
|
-
transportSecurityPolicy?: TransportSecurityPolicy;
|
|
42
|
-
/** Scope validators keyed by scope name. */
|
|
43
|
-
scopeResolvers?: ConnectScopeResolverMap;
|
|
44
|
-
/** Explicit compatibility switch for optional scope failures. */
|
|
45
|
-
relaxedOptionalScopeValidation?: boolean;
|
|
46
|
-
/** Acknowledged Yamux liveness checks, or false to disable automatic checks. */
|
|
47
|
-
liveness?: false | LivenessOptions;
|
|
48
|
-
}>;
|
|
49
|
-
export type ConnectCoreArgs = Readonly<{
|
|
50
|
-
path: "tunnel" | "direct";
|
|
51
|
-
wsUrl: string;
|
|
52
|
-
channelId: string;
|
|
53
|
-
e2eePskB64u: string;
|
|
54
|
-
defaultSuite: number;
|
|
55
|
-
opts: ConnectOptionsBase;
|
|
56
|
-
attach?: Readonly<{
|
|
57
|
-
attachJson: string;
|
|
58
|
-
endpointInstanceId: string;
|
|
59
|
-
}>;
|
|
60
|
-
}>;
|
|
61
|
-
export declare function connectCore(args: ConnectCoreArgs): Promise<ClientInternal>;
|
|
@@ -1,572 +0,0 @@
|
|
|
1
|
-
import { clientHandshake } from "../e2ee/handshake.js";
|
|
2
|
-
import { ByteReader } from "../yamux/byteReader.js";
|
|
3
|
-
import { YamuxSession } from "../yamux/session.js";
|
|
4
|
-
import { DEFAULT_YAMUX_LIMITS } from "../yamux/session.js";
|
|
5
|
-
import { SDK_DEFAULTS } from "../defaults.js";
|
|
6
|
-
import { RpcClient } from "../rpc/client.js";
|
|
7
|
-
import { writeStreamHello } from "../streamhello/streamHello.js";
|
|
8
|
-
import { emitObserverDiagnostic, normalizeObserver, nowSeconds } from "../observability/observer.js";
|
|
9
|
-
import { base64urlDecode } from "../utils/base64url.js";
|
|
10
|
-
import { AbortError, FlowersecError, throwIfAborted } from "../utils/errors.js";
|
|
11
|
-
import { DEFAULT_WEB_SOCKET_LIMITS, WebSocketBinaryTransport, WsCloseError } from "../ws-client/binaryTransport.js";
|
|
12
|
-
import { OriginMismatchError, WsFactoryRequiredError, classifyConnectError, classifyHandshakeError, createWebSocket, waitOpen, withAbortAndTimeout, } from "./common.js";
|
|
13
|
-
import { prepareChannelId } from "./contract.js";
|
|
14
|
-
import { isTunnelAttachCloseReason } from "./tunnelAttachCloseReason.js";
|
|
15
|
-
import { enforceTransportSecurity } from "./transportSecurity.js";
|
|
16
|
-
import { maxPlaintextBytes } from "../e2ee/record.js";
|
|
17
|
-
import { isYamuxPingTimeoutError, isYamuxResourceExhaustedError } from "../yamux/errors.js";
|
|
18
|
-
import { registerClientTermination } from "./termination.js";
|
|
19
|
-
export async function connectCore(args) {
|
|
20
|
-
const observer = normalizeObserver(args.opts.observer, { path: args.path });
|
|
21
|
-
const signal = args.opts.signal;
|
|
22
|
-
const connectStart = nowSeconds();
|
|
23
|
-
let attachState = args.path === "tunnel" ? "not_started" : "accepted";
|
|
24
|
-
const reportAttachSuccess = () => {
|
|
25
|
-
if (args.path !== "tunnel" || attachState !== "sent")
|
|
26
|
-
return;
|
|
27
|
-
observer.onAttach("ok", undefined);
|
|
28
|
-
attachState = "accepted";
|
|
29
|
-
};
|
|
30
|
-
const reportAttachFailure = (reason) => {
|
|
31
|
-
if (args.path !== "tunnel" || attachState !== "sent")
|
|
32
|
-
return;
|
|
33
|
-
observer.onAttach("fail", reason);
|
|
34
|
-
attachState = "failed";
|
|
35
|
-
};
|
|
36
|
-
const origin = typeof args.opts.origin === "string" ? args.opts.origin.trim() : "";
|
|
37
|
-
if (origin === "") {
|
|
38
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "missing_origin", message: "missing origin" });
|
|
39
|
-
}
|
|
40
|
-
const invalidOption = (message) => {
|
|
41
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_option", message });
|
|
42
|
-
};
|
|
43
|
-
if (args.path === "tunnel" && args.attach == null) {
|
|
44
|
-
throw new FlowersecError({
|
|
45
|
-
path: args.path,
|
|
46
|
-
stage: "validate",
|
|
47
|
-
code: "invalid_option",
|
|
48
|
-
message: "missing attach payload",
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
const wsUrl = typeof args.wsUrl === "string" ? args.wsUrl.trim() : "";
|
|
52
|
-
if (wsUrl === "") {
|
|
53
|
-
const code = args.path === "tunnel" ? "missing_tunnel_url" : "missing_ws_url";
|
|
54
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code, message: "missing websocket url" });
|
|
55
|
-
}
|
|
56
|
-
const connectTimeoutMs = args.opts.connectTimeoutMs ?? SDK_DEFAULTS.transport.connectTimeoutMs;
|
|
57
|
-
if (!Number.isFinite(connectTimeoutMs) || connectTimeoutMs < 0) {
|
|
58
|
-
invalidOption("connectTimeoutMs must be a non-negative number");
|
|
59
|
-
}
|
|
60
|
-
const handshakeTimeoutMs = args.opts.handshakeTimeoutMs ?? SDK_DEFAULTS.transport.handshakeTimeoutMs;
|
|
61
|
-
if (!Number.isFinite(handshakeTimeoutMs) || handshakeTimeoutMs < 0) {
|
|
62
|
-
invalidOption("handshakeTimeoutMs must be a non-negative number");
|
|
63
|
-
}
|
|
64
|
-
const clientFeatures = args.opts.clientFeatures ?? 0;
|
|
65
|
-
if (!Number.isSafeInteger(clientFeatures) || clientFeatures < 0 || clientFeatures > 0xffffffff) {
|
|
66
|
-
invalidOption("clientFeatures must be a uint32");
|
|
67
|
-
}
|
|
68
|
-
const maxHandshakePayload = args.opts.maxHandshakePayload ?? 0;
|
|
69
|
-
if (!Number.isSafeInteger(maxHandshakePayload) || maxHandshakePayload < 0) {
|
|
70
|
-
invalidOption("maxHandshakePayload must be a non-negative integer");
|
|
71
|
-
}
|
|
72
|
-
const maxRecordBytes = args.opts.maxRecordBytes ?? 0;
|
|
73
|
-
if (!Number.isSafeInteger(maxRecordBytes) || maxRecordBytes < 0) {
|
|
74
|
-
invalidOption("maxRecordBytes must be a non-negative integer");
|
|
75
|
-
}
|
|
76
|
-
const maxBufferedBytes = args.opts.maxBufferedBytes ?? 0;
|
|
77
|
-
if (!Number.isSafeInteger(maxBufferedBytes) || maxBufferedBytes < 0) {
|
|
78
|
-
invalidOption("maxBufferedBytes must be a non-negative integer");
|
|
79
|
-
}
|
|
80
|
-
const maxOutboundBufferedBytes = args.opts.maxOutboundBufferedBytes ?? 0;
|
|
81
|
-
if (!Number.isSafeInteger(maxOutboundBufferedBytes) || maxOutboundBufferedBytes < 0) {
|
|
82
|
-
invalidOption("maxOutboundBufferedBytes must be a non-negative integer");
|
|
83
|
-
}
|
|
84
|
-
const effectiveMaxRecordBytes = maxRecordBytes > 0 ? maxRecordBytes : SDK_DEFAULTS.e2ee.maxRecordBytes;
|
|
85
|
-
const outboundRecordChunkBytes = args.opts.outboundRecordChunkBytes ?? 64 * 1024;
|
|
86
|
-
if (!Number.isSafeInteger(outboundRecordChunkBytes) || outboundRecordChunkBytes <= 0 || outboundRecordChunkBytes > maxPlaintextBytes(effectiveMaxRecordBytes)) {
|
|
87
|
-
invalidOption("outboundRecordChunkBytes must be a positive integer within maxRecordBytes");
|
|
88
|
-
}
|
|
89
|
-
validateLimitObject(args.opts.webSocketLimits, "webSocketLimits", invalidOption);
|
|
90
|
-
validateLimitObject(args.opts.yamuxLimits, "yamuxLimits", invalidOption);
|
|
91
|
-
validateWebSocketLimitRelationships(args.opts.webSocketLimits, invalidOption);
|
|
92
|
-
validateYamuxLimitRelationships(args.opts.yamuxLimits, invalidOption);
|
|
93
|
-
const liveness = normalizeLiveness(args.opts.liveness, invalidOption);
|
|
94
|
-
await enforceTransportSecurity({
|
|
95
|
-
rawUrl: wsUrl,
|
|
96
|
-
path: args.path,
|
|
97
|
-
...(args.opts.transportSecurityPolicy === undefined ? {} : { policy: args.opts.transportSecurityPolicy }),
|
|
98
|
-
...(args.opts.observer === undefined ? {} : { observer: args.opts.observer }),
|
|
99
|
-
});
|
|
100
|
-
const channelId = prepareChannelId(args.channelId, args.path);
|
|
101
|
-
let psk;
|
|
102
|
-
try {
|
|
103
|
-
const pskB64u = typeof args.e2eePskB64u === "string" ? args.e2eePskB64u.trim() : "";
|
|
104
|
-
psk = base64urlDecode(pskB64u);
|
|
105
|
-
}
|
|
106
|
-
catch (e) {
|
|
107
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_psk", message: "invalid e2ee_psk_b64u", cause: e });
|
|
108
|
-
}
|
|
109
|
-
if (psk.length !== 32) {
|
|
110
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_psk", message: "psk must be 32 bytes" });
|
|
111
|
-
}
|
|
112
|
-
const suite = args.defaultSuite;
|
|
113
|
-
if (suite !== 1 && suite !== 2) {
|
|
114
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_suite", message: "invalid suite" });
|
|
115
|
-
}
|
|
116
|
-
let ws;
|
|
117
|
-
try {
|
|
118
|
-
ws = createWebSocket(wsUrl, origin, args.opts.wsFactory);
|
|
119
|
-
}
|
|
120
|
-
catch (e) {
|
|
121
|
-
if (e instanceof OriginMismatchError) {
|
|
122
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_option", message: e.message, cause: e });
|
|
123
|
-
}
|
|
124
|
-
if (e instanceof WsFactoryRequiredError) {
|
|
125
|
-
throw new FlowersecError({ path: args.path, stage: "validate", code: "invalid_option", message: e.message, cause: e });
|
|
126
|
-
}
|
|
127
|
-
throw new FlowersecError({ path: args.path, stage: "connect", code: "dial_failed", message: "dial failed", cause: e });
|
|
128
|
-
}
|
|
129
|
-
// Install close/error/message listeners before waiting for "open" to avoid a gap where a peer close
|
|
130
|
-
// (for example a tunnel attach rejection with a reason token) can be missed and misclassified as a handshake timeout.
|
|
131
|
-
const transport = new WebSocketBinaryTransport(ws, {
|
|
132
|
-
...(args.opts.webSocketLimits === undefined ? {} : { webSocketLimits: args.opts.webSocketLimits }),
|
|
133
|
-
observer,
|
|
134
|
-
});
|
|
135
|
-
try {
|
|
136
|
-
try {
|
|
137
|
-
await waitOpen(ws, {
|
|
138
|
-
timeoutMs: connectTimeoutMs,
|
|
139
|
-
...(signal !== undefined ? { signal } : {}),
|
|
140
|
-
});
|
|
141
|
-
observer.onConnect(args.path, "ok", undefined, nowSeconds() - connectStart);
|
|
142
|
-
}
|
|
143
|
-
catch (err) {
|
|
144
|
-
const reason = classifyConnectError(err);
|
|
145
|
-
observer.onConnect(args.path, "fail", reason, nowSeconds() - connectStart);
|
|
146
|
-
const code = reason === "timeout" ? "timeout" : reason === "canceled" ? "canceled" : "dial_failed";
|
|
147
|
-
throw new FlowersecError({
|
|
148
|
-
path: args.path,
|
|
149
|
-
stage: "connect",
|
|
150
|
-
code,
|
|
151
|
-
message: `connect failed: ${reason}`,
|
|
152
|
-
cause: err,
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
throwIfAborted(signal, "connect aborted");
|
|
156
|
-
if (args.path === "tunnel") {
|
|
157
|
-
try {
|
|
158
|
-
ws.send(args.attach.attachJson);
|
|
159
|
-
attachState = "sent";
|
|
160
|
-
}
|
|
161
|
-
catch (err) {
|
|
162
|
-
observer.onAttach("fail", "send_failed");
|
|
163
|
-
attachState = "failed";
|
|
164
|
-
throw new FlowersecError({ path: args.path, stage: "attach", code: "attach_failed", message: "attach failed", cause: err });
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
const handshakeStart = nowSeconds();
|
|
168
|
-
let secure;
|
|
169
|
-
try {
|
|
170
|
-
secure = await withAbortAndTimeout(clientHandshake(transport, {
|
|
171
|
-
channelId,
|
|
172
|
-
suite,
|
|
173
|
-
psk,
|
|
174
|
-
clientFeatures,
|
|
175
|
-
maxHandshakePayload: maxHandshakePayload > 0 ? maxHandshakePayload : SDK_DEFAULTS.e2ee.maxHandshakePayloadBytes,
|
|
176
|
-
maxRecordBytes: effectiveMaxRecordBytes,
|
|
177
|
-
outboundRecordChunkBytes,
|
|
178
|
-
...(maxBufferedBytes > 0 ? { maxBufferedBytes } : {}),
|
|
179
|
-
...(maxOutboundBufferedBytes > 0 ? { maxOutboundBufferedBytes } : {}),
|
|
180
|
-
timeoutMs: handshakeTimeoutMs,
|
|
181
|
-
...(signal !== undefined ? { signal } : {}),
|
|
182
|
-
}), {
|
|
183
|
-
timeoutMs: handshakeTimeoutMs,
|
|
184
|
-
...(signal !== undefined ? { signal } : {}),
|
|
185
|
-
onCancel: () => transport.close(),
|
|
186
|
-
});
|
|
187
|
-
reportAttachSuccess();
|
|
188
|
-
observer.onHandshake(args.path, "ok", undefined, nowSeconds() - handshakeStart);
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
const handshakeElapsedSeconds = nowSeconds() - handshakeStart;
|
|
192
|
-
const handshakeCode = classifyHandshakeError(err);
|
|
193
|
-
if (args.path === "tunnel" && err instanceof WsCloseError) {
|
|
194
|
-
const reason = err.reason;
|
|
195
|
-
if (isTunnelAttachCloseReason(reason)) {
|
|
196
|
-
reportAttachFailure(reason);
|
|
197
|
-
throw new FlowersecError({ path: args.path, stage: "attach", code: reason, message: "tunnel rejected attach", cause: err });
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
if (args.path === "tunnel") {
|
|
201
|
-
reportAttachFailure(handshakeCode === "timeout" ? "timeout" : handshakeCode === "canceled" ? "canceled" : "attach_failed");
|
|
202
|
-
}
|
|
203
|
-
observer.onHandshake(args.path, "fail", handshakeCode, handshakeElapsedSeconds);
|
|
204
|
-
throw new FlowersecError({
|
|
205
|
-
path: args.path,
|
|
206
|
-
stage: "handshake",
|
|
207
|
-
code: handshakeCode,
|
|
208
|
-
message: "handshake failed",
|
|
209
|
-
cause: err,
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
const conn = {
|
|
213
|
-
read: () => secure.read(),
|
|
214
|
-
write: (b) => secure.write(b),
|
|
215
|
-
close: () => secure.close(),
|
|
216
|
-
};
|
|
217
|
-
let resolveTermination;
|
|
218
|
-
const termination = new Promise((resolve) => {
|
|
219
|
-
resolveTermination = resolve;
|
|
220
|
-
});
|
|
221
|
-
let terminationReported = false;
|
|
222
|
-
let closeAll = () => {
|
|
223
|
-
runSyncCleanups([() => secure.close()]);
|
|
224
|
-
};
|
|
225
|
-
const reportTermination = (error) => {
|
|
226
|
-
if (terminationReported)
|
|
227
|
-
return;
|
|
228
|
-
terminationReported = true;
|
|
229
|
-
let terminalError = error;
|
|
230
|
-
try {
|
|
231
|
-
closeAll();
|
|
232
|
-
}
|
|
233
|
-
catch (cleanupError) {
|
|
234
|
-
terminalError = errorWithCleanup(error, cleanupError, "session termination cleanup failed");
|
|
235
|
-
}
|
|
236
|
-
resolveTermination({ error: terminalError });
|
|
237
|
-
};
|
|
238
|
-
const mux = new YamuxSession(conn, {
|
|
239
|
-
client: true,
|
|
240
|
-
...(args.opts.yamuxLimits === undefined ? {} : { limits: args.opts.yamuxLimits }),
|
|
241
|
-
onTerminal: reportTermination,
|
|
242
|
-
onDiagnostic: (event) => emitObserverDiagnostic(args.opts.observer, {
|
|
243
|
-
path: args.path,
|
|
244
|
-
stage: "yamux",
|
|
245
|
-
code_domain: "event",
|
|
246
|
-
code: event.code,
|
|
247
|
-
result: "fail",
|
|
248
|
-
resource: event.resource,
|
|
249
|
-
current: event.current,
|
|
250
|
-
limit: event.limit,
|
|
251
|
-
}),
|
|
252
|
-
});
|
|
253
|
-
closeAll = () => {
|
|
254
|
-
runSyncCleanups([() => mux.close(), () => secure.close()]);
|
|
255
|
-
};
|
|
256
|
-
let rpcStream;
|
|
257
|
-
try {
|
|
258
|
-
rpcStream = await mux.openStream(signal === undefined ? {} : { signal });
|
|
259
|
-
}
|
|
260
|
-
catch (e) {
|
|
261
|
-
const cleanupError = captureSyncCleanup(() => runSyncCleanups([() => mux.close(), () => secure.close()]));
|
|
262
|
-
throw new FlowersecError({
|
|
263
|
-
path: args.path,
|
|
264
|
-
stage: "yamux",
|
|
265
|
-
code: "open_stream_failed",
|
|
266
|
-
message: "open rpc stream failed",
|
|
267
|
-
cause: causeWithCleanup(e, cleanupError, "RPC stream setup cleanup failed"),
|
|
268
|
-
});
|
|
269
|
-
}
|
|
270
|
-
const reader = new ByteReader(() => rpcStream.read());
|
|
271
|
-
const readExactly = (n) => reader.readExactly(n);
|
|
272
|
-
const write = (b) => rpcStream.write(b);
|
|
273
|
-
try {
|
|
274
|
-
await writeStreamHello(write, "rpc");
|
|
275
|
-
}
|
|
276
|
-
catch (e) {
|
|
277
|
-
const cleanupError = await captureAsyncCleanup(async () => {
|
|
278
|
-
const streamCloseError = await captureAsyncCleanup(() => rpcStream.close());
|
|
279
|
-
const stackCloseError = captureSyncCleanup(() => runSyncCleanups([() => mux.close(), () => secure.close()]));
|
|
280
|
-
const combined = errorsFrom([streamCloseError, stackCloseError]);
|
|
281
|
-
if (combined != null)
|
|
282
|
-
throw combined;
|
|
283
|
-
});
|
|
284
|
-
throw new FlowersecError({
|
|
285
|
-
path: args.path,
|
|
286
|
-
stage: "rpc",
|
|
287
|
-
code: "stream_hello_failed",
|
|
288
|
-
message: "rpc stream hello failed",
|
|
289
|
-
cause: causeWithCleanup(e, cleanupError, "RPC bootstrap cleanup failed"),
|
|
290
|
-
});
|
|
291
|
-
}
|
|
292
|
-
const rpc = new RpcClient(readExactly, write, { observer, onTerminal: reportTermination });
|
|
293
|
-
const ping = async () => {
|
|
294
|
-
try {
|
|
295
|
-
await secure.sendPing();
|
|
296
|
-
}
|
|
297
|
-
catch (e) {
|
|
298
|
-
throw new FlowersecError({ path: args.path, stage: "secure", code: "ping_failed", message: "ping failed", cause: e });
|
|
299
|
-
}
|
|
300
|
-
};
|
|
301
|
-
const probeLiveness = async () => {
|
|
302
|
-
try {
|
|
303
|
-
return await mux.probeLiveness(liveness.timeoutMs);
|
|
304
|
-
}
|
|
305
|
-
catch (e) {
|
|
306
|
-
const timedOut = isYamuxPingTimeoutError(e);
|
|
307
|
-
if (timedOut) {
|
|
308
|
-
emitObserverDiagnostic(args.opts.observer, { path: args.path, stage: "yamux", code_domain: "event", code: "liveness_timeout", result: "fail" });
|
|
309
|
-
}
|
|
310
|
-
const error = new FlowersecError({ path: args.path, stage: "yamux", code: timedOut ? "timeout" : "ping_failed", message: "liveness probe failed", cause: e });
|
|
311
|
-
reportTermination(error);
|
|
312
|
-
throw error;
|
|
313
|
-
}
|
|
314
|
-
};
|
|
315
|
-
let livenessTimer;
|
|
316
|
-
let livenessInFlight = false;
|
|
317
|
-
const stopLiveness = () => {
|
|
318
|
-
if (livenessTimer === undefined)
|
|
319
|
-
return;
|
|
320
|
-
clearInterval(livenessTimer);
|
|
321
|
-
livenessTimer = undefined;
|
|
322
|
-
};
|
|
323
|
-
closeAll = () => {
|
|
324
|
-
stopLiveness();
|
|
325
|
-
runSyncCleanups([() => rpc.close(), () => mux.close(), () => secure.close()]);
|
|
326
|
-
};
|
|
327
|
-
if (liveness.intervalMs > 0) {
|
|
328
|
-
livenessTimer = setInterval(() => {
|
|
329
|
-
if (livenessInFlight)
|
|
330
|
-
return;
|
|
331
|
-
livenessInFlight = true;
|
|
332
|
-
probeLiveness()
|
|
333
|
-
.catch(() => {
|
|
334
|
-
stopLiveness();
|
|
335
|
-
})
|
|
336
|
-
.finally(() => {
|
|
337
|
-
livenessInFlight = false;
|
|
338
|
-
});
|
|
339
|
-
}, liveness.intervalMs);
|
|
340
|
-
livenessTimer?.unref?.();
|
|
341
|
-
}
|
|
342
|
-
const client = {
|
|
343
|
-
path: args.path,
|
|
344
|
-
...(args.attach != null ? { endpointInstanceId: args.attach.endpointInstanceId } : {}),
|
|
345
|
-
secure,
|
|
346
|
-
mux,
|
|
347
|
-
rpc,
|
|
348
|
-
ping,
|
|
349
|
-
rekey: async () => {
|
|
350
|
-
try {
|
|
351
|
-
await secure.rekeyNow();
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
throw new FlowersecError({ path: args.path, stage: "secure", code: "rekey_failed", message: "rekey failed", cause: error });
|
|
355
|
-
}
|
|
356
|
-
},
|
|
357
|
-
probeLiveness,
|
|
358
|
-
openStream: async (kind, opts = {}) => {
|
|
359
|
-
const streamKind = kind?.trim() ?? "";
|
|
360
|
-
if (streamKind === "")
|
|
361
|
-
throw new FlowersecError({ path: args.path, stage: "rpc", code: "missing_stream_kind", message: "missing stream kind" });
|
|
362
|
-
if (opts.signal?.aborted) {
|
|
363
|
-
throw new FlowersecError({
|
|
364
|
-
path: args.path,
|
|
365
|
-
stage: "yamux",
|
|
366
|
-
code: "canceled",
|
|
367
|
-
message: "open stream aborted",
|
|
368
|
-
cause: opts.signal.reason,
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
const abortReason = (signal) => {
|
|
372
|
-
const r = signal.reason;
|
|
373
|
-
if (r instanceof Error)
|
|
374
|
-
return r;
|
|
375
|
-
if (typeof r === "string" && r !== "")
|
|
376
|
-
return new AbortError(r);
|
|
377
|
-
return new AbortError("aborted");
|
|
378
|
-
};
|
|
379
|
-
const signal = opts.signal;
|
|
380
|
-
let abortListener;
|
|
381
|
-
let abortReset;
|
|
382
|
-
let s;
|
|
383
|
-
try {
|
|
384
|
-
s = await mux.openStream(signal === undefined ? {} : { signal });
|
|
385
|
-
}
|
|
386
|
-
catch (e) {
|
|
387
|
-
if (signal?.aborted) {
|
|
388
|
-
throw new FlowersecError({ path: args.path, stage: "yamux", code: "canceled", message: "open stream aborted", cause: signal.reason });
|
|
389
|
-
}
|
|
390
|
-
const exhausted = isYamuxResourceExhaustedError(e);
|
|
391
|
-
throw new FlowersecError({ path: args.path, stage: "yamux", code: exhausted ? "resource_exhausted" : "open_stream_failed", message: exhausted ? "yamux stream limit reached" : "open stream failed", cause: e });
|
|
392
|
-
}
|
|
393
|
-
if (signal != null) {
|
|
394
|
-
abortListener = () => {
|
|
395
|
-
abortReset ??= captureAsyncCleanup(() => Promise.resolve(s.reset(abortReason(signal))));
|
|
396
|
-
};
|
|
397
|
-
signal.addEventListener("abort", abortListener, { once: true });
|
|
398
|
-
if (signal.aborted)
|
|
399
|
-
abortListener();
|
|
400
|
-
}
|
|
401
|
-
if (signal?.aborted) {
|
|
402
|
-
if (abortListener != null)
|
|
403
|
-
signal.removeEventListener("abort", abortListener);
|
|
404
|
-
const cleanupError = await captureAsyncCleanup(async () => {
|
|
405
|
-
const resetError = abortReset == null ? undefined : await abortReset;
|
|
406
|
-
const closeError = await captureAsyncCleanup(() => s.close());
|
|
407
|
-
const combined = errorsFrom([resetError, closeError]);
|
|
408
|
-
if (combined != null)
|
|
409
|
-
throw combined;
|
|
410
|
-
});
|
|
411
|
-
throw new FlowersecError({
|
|
412
|
-
path: args.path,
|
|
413
|
-
stage: "yamux",
|
|
414
|
-
code: "canceled",
|
|
415
|
-
message: "open stream aborted",
|
|
416
|
-
cause: causeWithCleanup(signal.reason, cleanupError, "aborted stream cleanup failed"),
|
|
417
|
-
});
|
|
418
|
-
}
|
|
419
|
-
try {
|
|
420
|
-
await writeStreamHello((b) => s.write(b), streamKind);
|
|
421
|
-
}
|
|
422
|
-
catch (err) {
|
|
423
|
-
if (signal?.aborted) {
|
|
424
|
-
if (abortListener != null)
|
|
425
|
-
signal.removeEventListener("abort", abortListener);
|
|
426
|
-
const cleanupError = await captureAsyncCleanup(async () => {
|
|
427
|
-
const resetError = abortReset == null ? undefined : await abortReset;
|
|
428
|
-
const closeError = await captureAsyncCleanup(() => s.close());
|
|
429
|
-
const combined = errorsFrom([resetError, closeError]);
|
|
430
|
-
if (combined != null)
|
|
431
|
-
throw combined;
|
|
432
|
-
});
|
|
433
|
-
throw new FlowersecError({
|
|
434
|
-
path: args.path,
|
|
435
|
-
stage: "yamux",
|
|
436
|
-
code: "canceled",
|
|
437
|
-
message: "open stream aborted",
|
|
438
|
-
cause: causeWithCleanup(signal.reason, cleanupError, "aborted stream cleanup failed"),
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
if (signal != null && abortListener != null)
|
|
442
|
-
signal.removeEventListener("abort", abortListener);
|
|
443
|
-
const cleanupError = await captureAsyncCleanup(() => s.close());
|
|
444
|
-
throw new FlowersecError({
|
|
445
|
-
path: args.path,
|
|
446
|
-
stage: "rpc",
|
|
447
|
-
code: "stream_hello_failed",
|
|
448
|
-
message: "stream hello failed",
|
|
449
|
-
cause: causeWithCleanup(err, cleanupError, "stream hello cleanup failed"),
|
|
450
|
-
});
|
|
451
|
-
}
|
|
452
|
-
if (signal != null && abortListener != null) {
|
|
453
|
-
// AbortSignal is only used to cancel the open + StreamHello phase.
|
|
454
|
-
// After the stream is ready, callers should close/reset it explicitly.
|
|
455
|
-
signal.removeEventListener("abort", abortListener);
|
|
456
|
-
}
|
|
457
|
-
return s;
|
|
458
|
-
},
|
|
459
|
-
close: closeAll,
|
|
460
|
-
};
|
|
461
|
-
registerClientTermination(client, termination);
|
|
462
|
-
return client;
|
|
463
|
-
}
|
|
464
|
-
catch (e) {
|
|
465
|
-
const cleanupError = captureSyncCleanup(() => transport.close());
|
|
466
|
-
throw cleanupError == null ? e : errorWithCleanup(e, cleanupError, "transport cleanup failed");
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
function runSyncCleanups(actions) {
|
|
470
|
-
const failures = [];
|
|
471
|
-
for (const action of actions) {
|
|
472
|
-
try {
|
|
473
|
-
action();
|
|
474
|
-
}
|
|
475
|
-
catch (error) {
|
|
476
|
-
failures.push(errorValue(error));
|
|
477
|
-
}
|
|
478
|
-
}
|
|
479
|
-
if (failures.length > 0)
|
|
480
|
-
throw new AggregateError(failures, "Flowersec cleanup failed");
|
|
481
|
-
}
|
|
482
|
-
function captureSyncCleanup(action) {
|
|
483
|
-
try {
|
|
484
|
-
action();
|
|
485
|
-
return undefined;
|
|
486
|
-
}
|
|
487
|
-
catch (error) {
|
|
488
|
-
return error;
|
|
489
|
-
}
|
|
490
|
-
}
|
|
491
|
-
async function captureAsyncCleanup(action) {
|
|
492
|
-
try {
|
|
493
|
-
await action();
|
|
494
|
-
return undefined;
|
|
495
|
-
}
|
|
496
|
-
catch (error) {
|
|
497
|
-
return error;
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
function causeWithCleanup(primary, cleanup, message) {
|
|
501
|
-
if (cleanup === undefined)
|
|
502
|
-
return primary;
|
|
503
|
-
return new AggregateError([errorValue(primary), errorValue(cleanup)], message);
|
|
504
|
-
}
|
|
505
|
-
function errorWithCleanup(primary, cleanup, message) {
|
|
506
|
-
const cause = causeWithCleanup(primary, cleanup, message);
|
|
507
|
-
if (primary instanceof FlowersecError) {
|
|
508
|
-
return new FlowersecError({
|
|
509
|
-
path: primary.path,
|
|
510
|
-
stage: primary.stage,
|
|
511
|
-
code: primary.code,
|
|
512
|
-
message,
|
|
513
|
-
cause,
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
return new AggregateError([errorValue(primary), errorValue(cleanup)], message);
|
|
517
|
-
}
|
|
518
|
-
function errorsFrom(values) {
|
|
519
|
-
const failures = values.filter((value) => value !== undefined).map(errorValue);
|
|
520
|
-
return failures.length === 0 ? undefined : new AggregateError(failures, "Flowersec cleanup failed");
|
|
521
|
-
}
|
|
522
|
-
function errorValue(error) {
|
|
523
|
-
return error instanceof Error ? error : new Error(String(error));
|
|
524
|
-
}
|
|
525
|
-
function validateLimitObject(input, name, invalid) {
|
|
526
|
-
if (input === undefined)
|
|
527
|
-
return;
|
|
528
|
-
for (const [key, value] of Object.entries(input)) {
|
|
529
|
-
if (value !== undefined && (!Number.isSafeInteger(value) || value <= 0))
|
|
530
|
-
invalid(`${name}.${key} must be a positive integer`);
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
function normalizeLiveness(input, invalid) {
|
|
534
|
-
if (input === false || input === undefined)
|
|
535
|
-
return { intervalMs: 0, timeoutMs: 10_000 };
|
|
536
|
-
const intervalMs = input.intervalMs ?? 0;
|
|
537
|
-
const timeoutMs = input.timeoutMs ?? (intervalMs > 0 ? Math.min(10_000, intervalMs) : 10_000);
|
|
538
|
-
if (!Number.isFinite(intervalMs) || intervalMs < 0)
|
|
539
|
-
invalid("liveness.intervalMs must be a non-negative number");
|
|
540
|
-
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
|
|
541
|
-
invalid("liveness.timeoutMs must be a positive number");
|
|
542
|
-
return { intervalMs, timeoutMs };
|
|
543
|
-
}
|
|
544
|
-
function validateWebSocketLimitRelationships(input, invalid) {
|
|
545
|
-
if (input === undefined)
|
|
546
|
-
return;
|
|
547
|
-
const low = input.outboundLowWatermarkBytes ?? DEFAULT_WEB_SOCKET_LIMITS.outboundLowWatermarkBytes;
|
|
548
|
-
const high = input.outboundHighWatermarkBytes ?? DEFAULT_WEB_SOCKET_LIMITS.outboundHighWatermarkBytes;
|
|
549
|
-
const hard = input.outboundHardLimitBytes ?? DEFAULT_WEB_SOCKET_LIMITS.outboundHardLimitBytes;
|
|
550
|
-
if (low > high || high > hard)
|
|
551
|
-
invalid("webSocketLimits outbound watermarks must satisfy low <= high <= hard");
|
|
552
|
-
}
|
|
553
|
-
function validateYamuxLimitRelationships(input, invalid) {
|
|
554
|
-
if (input === undefined)
|
|
555
|
-
return;
|
|
556
|
-
const active = input.maxActiveStreams ?? DEFAULT_YAMUX_LIMITS.maxActiveStreams;
|
|
557
|
-
const inbound = input.maxInboundStreams ?? DEFAULT_YAMUX_LIMITS.maxInboundStreams;
|
|
558
|
-
const frame = input.maxFrameBytes ?? DEFAULT_YAMUX_LIMITS.maxFrameBytes;
|
|
559
|
-
const outbound = input.preferredOutboundFrameBytes ?? Math.min(DEFAULT_YAMUX_LIMITS.preferredOutboundFrameBytes, frame);
|
|
560
|
-
const streamReceive = input.maxStreamReceiveBytes ?? DEFAULT_YAMUX_LIMITS.maxStreamReceiveBytes;
|
|
561
|
-
const sessionReceive = input.maxSessionReceiveBytes ?? DEFAULT_YAMUX_LIMITS.maxSessionReceiveBytes;
|
|
562
|
-
if (inbound > active)
|
|
563
|
-
invalid("yamuxLimits.maxInboundStreams must not exceed maxActiveStreams");
|
|
564
|
-
if (outbound > frame)
|
|
565
|
-
invalid("yamuxLimits.preferredOutboundFrameBytes must not exceed maxFrameBytes");
|
|
566
|
-
if (frame > streamReceive)
|
|
567
|
-
invalid("yamuxLimits.maxFrameBytes must not exceed maxStreamReceiveBytes");
|
|
568
|
-
if (streamReceive < DEFAULT_YAMUX_LIMITS.maxStreamReceiveBytes)
|
|
569
|
-
invalid("yamuxLimits.maxStreamReceiveBytes must cover the 256 KiB initial stream window");
|
|
570
|
-
if (streamReceive > sessionReceive)
|
|
571
|
-
invalid("yamuxLimits.maxStreamReceiveBytes must not exceed maxSessionReceiveBytes");
|
|
572
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { ChannelInitGrant, Role as ControlRole } from "../gen/flowersec/controlplane/v1.gen.js";
|
|
2
|
-
import type { DirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
|
|
3
|
-
import { type FlowersecPath } from "../utils/errors.js";
|
|
4
|
-
export declare const CHANNEL_ID_MAX_BYTES = 256;
|
|
5
|
-
export declare function prepareChannelId(raw: string, path: Exclude<FlowersecPath, "auto">): string;
|
|
6
|
-
export declare function assertTunnelGrantContract(grant: ChannelInitGrant, expectedRole: ControlRole): void;
|
|
7
|
-
export declare function assertDirectConnectContract(info: DirectConnectInfo): void;
|
|
8
|
-
export declare function assertValidPSK(raw: string, path: Exclude<FlowersecPath, "auto">): string;
|