@floegence/flowersec-core 0.19.10 → 0.20.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 -3
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/reconnectConfig.d.ts +8 -36
- package/dist/browser/reconnectConfig.js +13 -70
- package/dist/client-connect/connectCore.d.ts +17 -5
- package/dist/client-connect/connectCore.js +131 -32
- package/dist/client-connect/transportSecurity.d.ts +19 -0
- package/dist/client-connect/transportSecurity.js +114 -0
- package/dist/client.d.ts +2 -0
- package/dist/e2ee/handshake.d.ts +4 -0
- package/dist/e2ee/handshake.js +2 -0
- package/dist/e2ee/secureChannel.d.ts +4 -0
- package/dist/e2ee/secureChannel.js +18 -9
- package/dist/facade.d.ts +5 -0
- package/dist/facade.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +1 -0
- package/dist/node/reconnectConfig.d.ts +8 -34
- package/dist/node/reconnectConfig.js +13 -71
- package/dist/node/wsFactory.js +3 -0
- package/dist/observability/observer.d.ts +5 -2
- package/dist/observability/observer.js +3 -0
- package/dist/reconnect/artifactControlplane.d.ts +9 -6
- package/dist/reconnect/artifactControlplane.js +28 -27
- package/dist/reconnect/index.d.ts +2 -0
- package/dist/reconnect/index.js +7 -0
- package/dist/rpc/server.d.ts +29 -4
- package/dist/rpc/server.js +142 -34
- package/dist/tunnel-client/connect.js +6 -6
- package/dist/utils/errors.d.ts +1 -1
- package/dist/ws-client/binaryTransport.d.ts +20 -5
- package/dist/ws-client/binaryTransport.js +110 -8
- package/dist/yamux/byteReader.d.ts +1 -0
- package/dist/yamux/byteReader.js +26 -0
- package/dist/yamux/errors.d.ts +7 -0
- package/dist/yamux/errors.js +15 -0
- package/dist/yamux/session.d.ts +31 -1
- package/dist/yamux/session.js +191 -12
- package/dist/yamux/stream.d.ts +10 -3
- package/dist/yamux/stream.js +61 -19
- package/package.json +1 -1
package/dist/e2ee/handshake.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type HandshakeClientOptions = Readonly<{
|
|
|
14
14
|
maxHandshakePayload: number;
|
|
15
15
|
/** Maximum record size for encrypted frames after handshake. */
|
|
16
16
|
maxRecordBytes: number;
|
|
17
|
+
/** Preferred plaintext bytes per outbound record. */
|
|
18
|
+
outboundRecordChunkBytes?: number;
|
|
17
19
|
/** Maximum buffered plaintext bytes for the secure channel. */
|
|
18
20
|
maxBufferedBytes?: number;
|
|
19
21
|
/** Optional AbortSignal to cancel the handshake. */
|
|
@@ -38,6 +40,8 @@ export type HandshakeServerOptions = Readonly<{
|
|
|
38
40
|
maxHandshakePayload: number;
|
|
39
41
|
/** Maximum record size for encrypted frames after handshake. */
|
|
40
42
|
maxRecordBytes: number;
|
|
43
|
+
/** Preferred plaintext bytes per outbound record. */
|
|
44
|
+
outboundRecordChunkBytes?: number;
|
|
41
45
|
/** Maximum buffered plaintext bytes for the secure channel. */
|
|
42
46
|
maxBufferedBytes?: number;
|
|
43
47
|
/** Optional AbortSignal to cancel the handshake. */
|
package/dist/e2ee/handshake.js
CHANGED
|
@@ -159,6 +159,7 @@ export async function clientHandshake(transport, opts) {
|
|
|
159
159
|
return new SecureChannel({
|
|
160
160
|
transport,
|
|
161
161
|
maxRecordBytes: opts.maxRecordBytes,
|
|
162
|
+
...(opts.outboundRecordChunkBytes !== undefined ? { outboundRecordChunkBytes: opts.outboundRecordChunkBytes } : {}),
|
|
162
163
|
...(opts.maxBufferedBytes !== undefined ? { maxBufferedBytes: opts.maxBufferedBytes } : {}),
|
|
163
164
|
sendKey: keys.c2sKey,
|
|
164
165
|
recvKey: keys.s2cKey,
|
|
@@ -316,6 +317,7 @@ export async function serverHandshake(transport, cache, opts) {
|
|
|
316
317
|
return new SecureChannel({
|
|
317
318
|
transport,
|
|
318
319
|
maxRecordBytes: opts.maxRecordBytes,
|
|
320
|
+
...(opts.outboundRecordChunkBytes !== undefined ? { outboundRecordChunkBytes: opts.outboundRecordChunkBytes } : {}),
|
|
319
321
|
...(opts.maxBufferedBytes !== undefined ? { maxBufferedBytes: opts.maxBufferedBytes } : {}),
|
|
320
322
|
sendKey: keys.s2cKey,
|
|
321
323
|
recvKey: keys.c2sKey,
|
|
@@ -19,6 +19,8 @@ export type BinaryTransport = {
|
|
|
19
19
|
export type SecureChannelOptions = Readonly<{
|
|
20
20
|
/** Maximum encoded record size (header + ciphertext). */
|
|
21
21
|
maxRecordBytes: number;
|
|
22
|
+
/** Preferred plaintext bytes per outbound record. */
|
|
23
|
+
outboundRecordChunkBytes?: number;
|
|
22
24
|
/** Maximum queued plaintext bytes before backpressure/errors. */
|
|
23
25
|
maxBufferedBytes?: number;
|
|
24
26
|
}>;
|
|
@@ -26,6 +28,7 @@ type Direction = 1 | 2;
|
|
|
26
28
|
export declare class SecureChannel {
|
|
27
29
|
private readonly transport;
|
|
28
30
|
private readonly maxRecordBytes;
|
|
31
|
+
private readonly outboundRecordChunkBytes;
|
|
29
32
|
private readonly maxBufferedBytes;
|
|
30
33
|
private sendKey;
|
|
31
34
|
private recvKey;
|
|
@@ -52,6 +55,7 @@ export declare class SecureChannel {
|
|
|
52
55
|
constructor(args: {
|
|
53
56
|
transport: BinaryTransport;
|
|
54
57
|
maxRecordBytes: number;
|
|
58
|
+
outboundRecordChunkBytes?: number;
|
|
55
59
|
maxBufferedBytes?: number;
|
|
56
60
|
sendKey: Uint8Array;
|
|
57
61
|
recvKey: Uint8Array;
|
|
@@ -14,6 +14,7 @@ export class SecureChannel {
|
|
|
14
14
|
transport;
|
|
15
15
|
// Maximum allowed bytes per record frame.
|
|
16
16
|
maxRecordBytes;
|
|
17
|
+
outboundRecordChunkBytes;
|
|
17
18
|
// Upper bound for buffered plaintext in memory.
|
|
18
19
|
maxBufferedBytes;
|
|
19
20
|
// Active encryption keys and nonce prefixes for the current epoch.
|
|
@@ -48,6 +49,11 @@ export class SecureChannel {
|
|
|
48
49
|
constructor(args) {
|
|
49
50
|
this.transport = args.transport;
|
|
50
51
|
this.maxRecordBytes = args.maxRecordBytes;
|
|
52
|
+
const maxPlain = Math.max(1, maxPlaintextBytes(this.maxRecordBytes));
|
|
53
|
+
this.outboundRecordChunkBytes = args.outboundRecordChunkBytes ?? Math.min(64 * 1024, maxPlain);
|
|
54
|
+
if (!Number.isSafeInteger(this.outboundRecordChunkBytes) || this.outboundRecordChunkBytes <= 0 || this.outboundRecordChunkBytes > maxPlain) {
|
|
55
|
+
throw new RangeError("outboundRecordChunkBytes must be a positive integer within the record plaintext limit");
|
|
56
|
+
}
|
|
51
57
|
this.maxBufferedBytes = Math.max(0, args.maxBufferedBytes ?? 4 * (1 << 20));
|
|
52
58
|
this.sendKey = args.sendKey;
|
|
53
59
|
this.recvKey = args.recvKey;
|
|
@@ -64,13 +70,9 @@ export class SecureChannel {
|
|
|
64
70
|
}
|
|
65
71
|
// write splits payloads into record-sized chunks and queues them for send.
|
|
66
72
|
async write(plaintext) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const chunk = plaintext.slice(off, Math.min(plaintext.length, off + maxPlain));
|
|
71
|
-
await this.enqueueSend("app", chunk);
|
|
72
|
-
off += chunk.length;
|
|
73
|
-
}
|
|
73
|
+
if (plaintext.length === 0)
|
|
74
|
+
return;
|
|
75
|
+
await this.enqueueSend("app", plaintext.slice());
|
|
74
76
|
}
|
|
75
77
|
// read resolves with the next plaintext chunk or throws on errors/close.
|
|
76
78
|
async read() {
|
|
@@ -216,8 +218,15 @@ export class SecureChannel {
|
|
|
216
218
|
try {
|
|
217
219
|
let frame;
|
|
218
220
|
if (req.kind === "app") {
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
+
const payload = req.payload ?? new Uint8Array();
|
|
222
|
+
for (let offset = 0; offset < payload.length; offset += this.outboundRecordChunkBytes) {
|
|
223
|
+
const chunk = payload.subarray(offset, Math.min(payload.length, offset + this.outboundRecordChunkBytes));
|
|
224
|
+
const seq = this.reserveSendSeq();
|
|
225
|
+
frame = encryptRecord(this.sendKey, this.sendNoncePrefix, RECORD_FLAG_APP, seq, chunk, this.maxRecordBytes);
|
|
226
|
+
await this.transport.writeBinary(frame);
|
|
227
|
+
}
|
|
228
|
+
req.resolve();
|
|
229
|
+
continue;
|
|
221
230
|
}
|
|
222
231
|
else if (req.kind === "ping") {
|
|
223
232
|
const seq = this.reserveSendSeq();
|
package/dist/facade.d.ts
CHANGED
|
@@ -12,8 +12,13 @@ export type { ConnectArtifact, CorrelationContext, CorrelationKV, DirectClientCo
|
|
|
12
12
|
export { assertConnectArtifact } from "./connect/artifact.js";
|
|
13
13
|
export type { ClientObserverLike } from "./observability/observer.js";
|
|
14
14
|
export type { Client, ClientPath } from "./client.js";
|
|
15
|
+
export type { LivenessOptions } from "./client-connect/connectCore.js";
|
|
16
|
+
export type { WebSocketLimits } from "./ws-client/binaryTransport.js";
|
|
17
|
+
export type { YamuxLimits } from "./yamux/session.js";
|
|
15
18
|
export type { FlowersecErrorCode, FlowersecPath, FlowersecStage } from "./utils/errors.js";
|
|
16
19
|
export { FlowersecError } from "./utils/errors.js";
|
|
20
|
+
export { AllowPlaintext, AllowPlaintextForLoopback, RequireTLS, } from "./client-connect/transportSecurity.js";
|
|
21
|
+
export type { TransportSecurityPolicy, TransportSecurityPolicyInput, TransportSecurityPolicyPreset, } from "./client-connect/transportSecurity.js";
|
|
17
22
|
export type { TunnelConnectOptions } from "./tunnel-client/connect.js";
|
|
18
23
|
export type { DirectConnectOptions } from "./direct-client/connect.js";
|
|
19
24
|
export type ConnectOptions = TunnelConnectOptions | DirectConnectOptions;
|
package/dist/facade.js
CHANGED
|
@@ -6,6 +6,7 @@ export { assertChannelInitGrant } from "./gen/flowersec/controlplane/v1.gen.js";
|
|
|
6
6
|
export { assertDirectConnectInfo } from "./gen/flowersec/direct/v1.gen.js";
|
|
7
7
|
export { assertConnectArtifact } from "./connect/artifact.js";
|
|
8
8
|
export { FlowersecError } from "./utils/errors.js";
|
|
9
|
+
export { AllowPlaintext, AllowPlaintextForLoopback, RequireTLS, } from "./client-connect/transportSecurity.js";
|
|
9
10
|
export async function connectTunnel(grant, opts) {
|
|
10
11
|
return await connectTunnelInternal(grant, opts);
|
|
11
12
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./e2ee/secureChannel.js";
|
|
|
12
12
|
export * from "./e2ee/handshake.js";
|
|
13
13
|
export * from "./e2ee/errors.js";
|
|
14
14
|
export * from "./client.js";
|
|
15
|
+
export * from "./client-connect/transportSecurity.js";
|
|
15
16
|
export * from "./observability/index.js";
|
|
16
17
|
export * from "./ws-client/binaryTransport.js";
|
|
17
18
|
export * from "./yamux/index.js";
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,7 @@ export * from "./e2ee/secureChannel.js";
|
|
|
12
12
|
export * from "./e2ee/handshake.js";
|
|
13
13
|
export * from "./e2ee/errors.js";
|
|
14
14
|
export * from "./client.js";
|
|
15
|
+
export * from "./client-connect/transportSecurity.js";
|
|
15
16
|
export * from "./observability/index.js";
|
|
16
17
|
export * from "./ws-client/binaryTransport.js";
|
|
17
18
|
export * from "./yamux/index.js";
|
package/dist/node/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { createNodeWsFactory } from "./wsFactory.js";
|
|
2
|
+
export { AllowPlaintext, AllowPlaintextForLoopback, RequireTLS, } from "../client-connect/transportSecurity.js";
|
|
3
|
+
export type { TransportSecurityPolicy, TransportSecurityPolicyInput, TransportSecurityPolicyPreset, } from "../client-connect/transportSecurity.js";
|
|
2
4
|
export { connectDirectNode, connectNode, connectTunnelNode } from "./connect.js";
|
|
3
5
|
export type { DirectNodeReconnectConfig, NodeReconnectConfig, TunnelNodeReconnectConfig, } from "./reconnectConfig.js";
|
|
4
6
|
export { createDirectNodeReconnectConfig, createNodeReconnectConfig, createTunnelNodeReconnectConfig, } from "./reconnectConfig.js";
|
package/dist/node/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { createNodeWsFactory } from "./wsFactory.js";
|
|
2
|
+
export { AllowPlaintext, AllowPlaintextForLoopback, RequireTLS, } from "../client-connect/transportSecurity.js";
|
|
2
3
|
export { connectDirectNode, connectNode, connectTunnelNode } from "./connect.js";
|
|
3
4
|
export { createDirectNodeReconnectConfig, createNodeReconnectConfig, createTunnelNodeReconnectConfig, } from "./reconnectConfig.js";
|
|
4
5
|
export { assertConnectArtifact } from "../connect/artifact.js";
|
|
@@ -1,42 +1,16 @@
|
|
|
1
|
-
import type { ConnectArtifact } from "../connect/artifact.js";
|
|
2
|
-
import type { ChannelInitGrant } from "../gen/flowersec/controlplane/v1.gen.js";
|
|
3
|
-
import type { DirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
|
|
4
1
|
import type { ClientObserverLike } from "../observability/observer.js";
|
|
5
2
|
import type { DirectConnectOptions } from "../direct-client/connect.js";
|
|
6
3
|
import type { TunnelConnectOptions } from "../tunnel-client/connect.js";
|
|
7
4
|
import type { AutoReconnectConfig, ConnectConfig as ReconnectConnectConfig } from "../reconnect/index.js";
|
|
8
|
-
import { type
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
import { type ArtifactSource } from "../reconnect/artifactControlplane.js";
|
|
6
|
+
export type NodeReconnectConfig = Readonly<{
|
|
7
|
+
source: ArtifactSource;
|
|
8
|
+
connect?: Omit<TunnelConnectOptions, "observer" | "signal"> | Omit<DirectConnectOptions, "observer" | "signal">;
|
|
11
9
|
observer?: ClientObserverLike;
|
|
12
10
|
autoReconnect?: AutoReconnectConfig;
|
|
13
11
|
}>;
|
|
14
|
-
type
|
|
15
|
-
type
|
|
16
|
-
type ArtifactAwareTunnelReconnectConfig = ArtifactAwareReconnectConfig & Readonly<{
|
|
17
|
-
artifact?: ConnectArtifact;
|
|
18
|
-
getArtifact?: (args: ArtifactFactoryArgs) => Promise<ConnectArtifact>;
|
|
19
|
-
artifactControlplane?: RequestConnectArtifactInput | RequestEntryConnectArtifactInput;
|
|
20
|
-
}>;
|
|
21
|
-
type ArtifactAwareDirectReconnectConfig = ArtifactAwareReconnectConfig & Readonly<{
|
|
22
|
-
artifact?: ConnectArtifact;
|
|
23
|
-
getArtifact?: (args: ArtifactFactoryArgs) => Promise<ConnectArtifact>;
|
|
24
|
-
artifactControlplane?: RequestConnectArtifactInput | RequestEntryConnectArtifactInput;
|
|
25
|
-
}>;
|
|
26
|
-
export type TunnelNodeReconnectConfig = SharedReconnectOptions & ArtifactAwareTunnelReconnectConfig & Readonly<{
|
|
27
|
-
mode?: "tunnel";
|
|
28
|
-
connect?: TunnelReconnectConnectOptions;
|
|
29
|
-
grant?: ChannelInitGrant;
|
|
30
|
-
getGrant?: () => Promise<ChannelInitGrant>;
|
|
31
|
-
}>;
|
|
32
|
-
export type DirectNodeReconnectConfig = SharedReconnectOptions & ArtifactAwareDirectReconnectConfig & Readonly<{
|
|
33
|
-
mode: "direct";
|
|
34
|
-
connect?: DirectReconnectConnectOptions;
|
|
35
|
-
directInfo?: DirectConnectInfo;
|
|
36
|
-
getDirectInfo?: () => Promise<DirectConnectInfo>;
|
|
37
|
-
}>;
|
|
38
|
-
export type NodeReconnectConfig = TunnelNodeReconnectConfig | DirectNodeReconnectConfig;
|
|
39
|
-
export declare function createTunnelNodeReconnectConfig(config: TunnelNodeReconnectConfig): ReconnectConnectConfig;
|
|
40
|
-
export declare function createDirectNodeReconnectConfig(config: DirectNodeReconnectConfig): ReconnectConnectConfig;
|
|
12
|
+
export type TunnelNodeReconnectConfig = NodeReconnectConfig;
|
|
13
|
+
export type DirectNodeReconnectConfig = NodeReconnectConfig;
|
|
41
14
|
export declare function createNodeReconnectConfig(config: NodeReconnectConfig): ReconnectConnectConfig;
|
|
42
|
-
export
|
|
15
|
+
export declare const createTunnelNodeReconnectConfig: typeof createNodeReconnectConfig;
|
|
16
|
+
export declare const createDirectNodeReconnectConfig: typeof createNodeReconnectConfig;
|
|
@@ -1,78 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
if (config.
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
async function resolveDirectInfo(config) {
|
|
11
|
-
if (config.getDirectInfo)
|
|
12
|
-
return await config.getDirectInfo();
|
|
13
|
-
if (config.directInfo)
|
|
14
|
-
return config.directInfo;
|
|
15
|
-
throw new Error("Direct reconnect config requires `getDirectInfo` or `directInfo`");
|
|
16
|
-
}
|
|
17
|
-
export function createTunnelNodeReconnectConfig(config) {
|
|
18
|
-
let traceId = config.artifact?.correlation?.trace_id;
|
|
19
|
-
return {
|
|
20
|
-
...(config.observer === undefined ? {} : { observer: config.observer }),
|
|
21
|
-
...(config.autoReconnect === undefined ? {} : { autoReconnect: config.autoReconnect }),
|
|
22
|
-
connectOnce: async ({ signal, observer }) => {
|
|
23
|
-
if (config.getArtifact || config.artifact || config.artifactControlplane) {
|
|
24
|
-
const artifact = await resolveConnectArtifact(config, traceId, signal);
|
|
25
|
-
if (artifact.transport !== "tunnel") {
|
|
26
|
-
throw new Error("Tunnel reconnect config requires a tunnel ConnectArtifact");
|
|
27
|
-
}
|
|
28
|
-
traceId = updateTraceId(traceId, artifact);
|
|
29
|
-
const connectOptions = {
|
|
30
|
-
...(config.connect === undefined ? {} : config.connect),
|
|
31
|
-
signal,
|
|
32
|
-
observer,
|
|
33
|
-
};
|
|
34
|
-
return await connectNode(artifact, connectOptions);
|
|
35
|
-
}
|
|
36
|
-
const connectOptions = {
|
|
37
|
-
...(config.connect === undefined ? {} : config.connect),
|
|
38
|
-
signal,
|
|
39
|
-
observer,
|
|
40
|
-
};
|
|
41
|
-
return await connectTunnelNode(await resolveTunnelGrant(config), connectOptions);
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
export function createDirectNodeReconnectConfig(config) {
|
|
46
|
-
let traceId = config.artifact?.correlation?.trace_id;
|
|
1
|
+
import { createArtifactResolver, updateTraceId } from "../reconnect/artifactControlplane.js";
|
|
2
|
+
import { connectNode } from "./connect.js";
|
|
3
|
+
export function createNodeReconnectConfig(config) {
|
|
4
|
+
if (config.source.kind === "once" && config.autoReconnect?.enabled) {
|
|
5
|
+
throw new Error("automatic reconnect requires a refreshable artifact source");
|
|
6
|
+
}
|
|
7
|
+
let traceId = config.source.kind === "once" ? config.source.artifact.correlation?.trace_id : undefined;
|
|
8
|
+
const acquire = createArtifactResolver(config.source);
|
|
47
9
|
return {
|
|
48
10
|
...(config.observer === undefined ? {} : { observer: config.observer }),
|
|
49
11
|
...(config.autoReconnect === undefined ? {} : { autoReconnect: config.autoReconnect }),
|
|
50
12
|
connectOnce: async ({ signal, observer }) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
throw new Error("Direct reconnect config requires a direct ConnectArtifact");
|
|
55
|
-
}
|
|
56
|
-
traceId = updateTraceId(traceId, artifact);
|
|
57
|
-
const connectOptions = {
|
|
58
|
-
...(config.connect === undefined ? {} : config.connect),
|
|
59
|
-
signal,
|
|
60
|
-
observer,
|
|
61
|
-
};
|
|
62
|
-
return await connectNode(artifact, connectOptions);
|
|
63
|
-
}
|
|
64
|
-
const connectOptions = {
|
|
65
|
-
...(config.connect === undefined ? {} : config.connect),
|
|
66
|
-
signal,
|
|
67
|
-
observer,
|
|
68
|
-
};
|
|
69
|
-
return await connectDirectNode(await resolveDirectInfo(config), connectOptions);
|
|
13
|
+
const artifact = await acquire({ ...(traceId === undefined ? {} : { traceId }), signal });
|
|
14
|
+
traceId = updateTraceId(traceId, artifact);
|
|
15
|
+
return await connectNode(artifact, { ...(config.connect ?? {}), signal, observer });
|
|
70
16
|
},
|
|
71
17
|
};
|
|
72
18
|
}
|
|
73
|
-
export
|
|
74
|
-
|
|
75
|
-
return createDirectNodeReconnectConfig(config);
|
|
76
|
-
}
|
|
77
|
-
return createTunnelNodeReconnectConfig(config);
|
|
78
|
-
}
|
|
19
|
+
export const createTunnelNodeReconnectConfig = createNodeReconnectConfig;
|
|
20
|
+
export const createDirectNodeReconnectConfig = createNodeReconnectConfig;
|
package/dist/node/wsFactory.js
CHANGED
|
@@ -6,13 +6,13 @@ export type AttachReason = "send_failed" | "too_many_connections" | "expected_at
|
|
|
6
6
|
export type HandshakeResult = "ok" | "fail";
|
|
7
7
|
export type HandshakeReason = "auth_tag_mismatch" | "handshake_failed" | "invalid_suite" | "invalid_version" | "timestamp_after_init_exp" | "timestamp_out_of_skew" | "timeout" | "canceled";
|
|
8
8
|
export type WsCloseKind = "local" | "peer_or_error";
|
|
9
|
-
export type WsErrorReason = "error" | "recv_buffer_exceeded" | "unexpected_text_frame" | "unexpected_message_type";
|
|
9
|
+
export type WsErrorReason = "error" | "recv_buffer_exceeded" | "send_buffer_exceeded" | "send_buffer_timeout" | "unexpected_text_frame" | "unexpected_message_type";
|
|
10
10
|
export type RpcCallResult = "ok" | "rpc_error" | "handler_not_found" | "transport_error" | "canceled";
|
|
11
11
|
export type DiagnosticEvent = Readonly<{
|
|
12
12
|
v: 1;
|
|
13
13
|
namespace: "connect";
|
|
14
14
|
path: ClientPath | "auto";
|
|
15
|
-
stage: "validate" | "normalize" | "scope" | "connect" | "attach" | "handshake" | "close" | "reconnect";
|
|
15
|
+
stage: "validate" | "normalize" | "scope" | "connect" | "attach" | "handshake" | "transport" | "yamux" | "rpc" | "close" | "reconnect";
|
|
16
16
|
code_domain: "error" | "event";
|
|
17
17
|
code: string;
|
|
18
18
|
result: "ok" | "fail" | "retry" | "skip";
|
|
@@ -20,6 +20,9 @@ export type DiagnosticEvent = Readonly<{
|
|
|
20
20
|
attempt_seq: number;
|
|
21
21
|
trace_id?: string;
|
|
22
22
|
session_id?: string;
|
|
23
|
+
resource?: string;
|
|
24
|
+
current?: number;
|
|
25
|
+
limit?: number;
|
|
23
26
|
}>;
|
|
24
27
|
type ObserverContext = Readonly<{
|
|
25
28
|
path?: ClientPath | "auto";
|
|
@@ -54,6 +54,9 @@ function buildDiagnosticEvent(context, event) {
|
|
|
54
54
|
...(context.sessionId === undefined
|
|
55
55
|
? {}
|
|
56
56
|
: { session_id: context.sessionId }),
|
|
57
|
+
...(event.resource === undefined ? {} : { resource: event.resource }),
|
|
58
|
+
...(event.current === undefined ? {} : { current: event.current }),
|
|
59
|
+
...(event.limit === undefined ? {} : { limit: event.limit }),
|
|
57
60
|
});
|
|
58
61
|
}
|
|
59
62
|
function mapConnectDiagnostic(path, result, reason) {
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import type { ConnectArtifact } from "../connect/artifact.js";
|
|
2
2
|
import { type RequestConnectArtifactInput, type RequestEntryConnectArtifactInput } from "../controlplane/index.js";
|
|
3
|
-
export type
|
|
3
|
+
export type ArtifactAcquireContext = Readonly<{
|
|
4
4
|
traceId?: string;
|
|
5
5
|
signal?: AbortSignal;
|
|
6
6
|
}>;
|
|
7
|
-
export type
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
export type ArtifactSource = Readonly<{
|
|
8
|
+
kind: "once";
|
|
9
|
+
artifact: ConnectArtifact;
|
|
10
|
+
}> | Readonly<{
|
|
11
|
+
kind: "refreshable";
|
|
12
|
+
acquire: (context: ArtifactAcquireContext) => Promise<ConnectArtifact>;
|
|
11
13
|
}>;
|
|
12
|
-
export declare function
|
|
14
|
+
export declare function createControlplaneArtifactSource(input: RequestConnectArtifactInput | RequestEntryConnectArtifactInput): ArtifactSource;
|
|
15
|
+
export declare function createArtifactResolver(source: ArtifactSource): (context: ArtifactAcquireContext) => Promise<ConnectArtifact>;
|
|
13
16
|
export declare function updateTraceId(current: string | undefined, artifact: ConnectArtifact): string | undefined;
|
|
@@ -1,33 +1,34 @@
|
|
|
1
1
|
import { requestConnectArtifact, requestEntryConnectArtifact, } from "../controlplane/index.js";
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const input = {
|
|
17
|
-
...config.artifactControlplane,
|
|
2
|
+
export function createControlplaneArtifactSource(input) {
|
|
3
|
+
return {
|
|
4
|
+
kind: "refreshable",
|
|
5
|
+
acquire: async ({ traceId, signal }) => {
|
|
6
|
+
const correlation = traceId === undefined ? input.correlation : { traceId };
|
|
7
|
+
if ("entryTicket" in input) {
|
|
8
|
+
return await requestEntryConnectArtifact({
|
|
9
|
+
...input,
|
|
10
|
+
...(correlation === undefined ? {} : { correlation }),
|
|
11
|
+
...(signal === undefined ? {} : { signal }),
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
return await requestConnectArtifact({
|
|
15
|
+
...input,
|
|
18
16
|
...(correlation === undefined ? {} : { correlation }),
|
|
19
17
|
...(signal === undefined ? {} : { signal }),
|
|
20
|
-
};
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
});
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function createArtifactResolver(source) {
|
|
23
|
+
let consumed = false;
|
|
24
|
+
return async (context) => {
|
|
25
|
+
if (source.kind === "refreshable")
|
|
26
|
+
return await source.acquire(context);
|
|
27
|
+
if (consumed)
|
|
28
|
+
throw new Error("one-time artifact source has already been consumed");
|
|
29
|
+
consumed = true;
|
|
30
|
+
return source.artifact;
|
|
31
|
+
};
|
|
31
32
|
}
|
|
32
33
|
export function updateTraceId(current, artifact) {
|
|
33
34
|
return artifact.correlation?.trace_id ?? current;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { Client } from "../client.js";
|
|
2
2
|
import { type ClientObserverLike } from "../observability/observer.js";
|
|
3
|
+
export type { ArtifactAcquireContext, ArtifactSource } from "./artifactControlplane.js";
|
|
4
|
+
export { createArtifactResolver, createControlplaneArtifactSource } from "./artifactControlplane.js";
|
|
3
5
|
export type ConnectionStatus = "disconnected" | "connecting" | "connected" | "error";
|
|
4
6
|
export type AutoReconnectConfig = Readonly<{
|
|
5
7
|
enabled?: boolean;
|
package/dist/reconnect/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { emitObserverDiagnostic, withObserverContext } from "../observability/observer.js";
|
|
2
|
+
export { createArtifactResolver, createControlplaneArtifactSource } from "./artifactControlplane.js";
|
|
2
3
|
function normalizeAutoReconnect(cfg) {
|
|
3
4
|
if (!cfg?.enabled) {
|
|
4
5
|
return {
|
|
@@ -159,6 +160,12 @@ export function createReconnectManager() {
|
|
|
159
160
|
},
|
|
160
161
|
onRpcCall: (...args) => user?.onRpcCall?.(...args),
|
|
161
162
|
onRpcNotify: (...args) => user?.onRpcNotify?.(...args),
|
|
163
|
+
onDiagnosticEvent: (event) => {
|
|
164
|
+
user?.onDiagnosticEvent?.(event);
|
|
165
|
+
if (event.code === "liveness_timeout") {
|
|
166
|
+
startReconnect(t, cfg, new Error("liveness timeout"));
|
|
167
|
+
}
|
|
168
|
+
},
|
|
162
169
|
}, {
|
|
163
170
|
attemptSeq: currentAttemptSeq,
|
|
164
171
|
});
|
package/dist/rpc/server.d.ts
CHANGED
|
@@ -3,13 +3,38 @@ export type RpcHandler = (payload: unknown) => Promise<{
|
|
|
3
3
|
payload: unknown;
|
|
4
4
|
error?: RpcError;
|
|
5
5
|
}>;
|
|
6
|
+
export type RpcServerOptions = Readonly<{
|
|
7
|
+
maxConcurrentRequests?: number;
|
|
8
|
+
maxQueuedRequests?: number;
|
|
9
|
+
maxQueuedNotifications?: number;
|
|
10
|
+
}>;
|
|
11
|
+
export type RpcServerTransport = Readonly<{
|
|
12
|
+
readExactly(n: number): Promise<Uint8Array>;
|
|
13
|
+
write(bytes: Uint8Array): Promise<void>;
|
|
14
|
+
close(error: unknown): void;
|
|
15
|
+
}>;
|
|
6
16
|
export declare class RpcServer {
|
|
7
|
-
private readonly
|
|
8
|
-
private readonly write;
|
|
17
|
+
private readonly transport;
|
|
9
18
|
private readonly handlers;
|
|
10
19
|
private closed;
|
|
11
|
-
|
|
20
|
+
private readonly options;
|
|
21
|
+
private readonly requests;
|
|
22
|
+
private readonly notifications;
|
|
23
|
+
private requestWaiters;
|
|
24
|
+
private notificationWaiters;
|
|
25
|
+
private writeChain;
|
|
26
|
+
private terminalError;
|
|
27
|
+
private readonly terminalSignal;
|
|
28
|
+
private signalTerminal;
|
|
29
|
+
private transportClosed;
|
|
30
|
+
constructor(transport: RpcServerTransport, options?: RpcServerOptions);
|
|
12
31
|
register(typeId: number, h: RpcHandler): void;
|
|
13
32
|
serve(signal?: AbortSignal): Promise<void>;
|
|
14
|
-
close(): void;
|
|
33
|
+
close(error?: unknown): void;
|
|
34
|
+
private fail;
|
|
35
|
+
private requestWorker;
|
|
36
|
+
private notificationWorker;
|
|
37
|
+
private nextWork;
|
|
38
|
+
private wakeOne;
|
|
39
|
+
private writeResponse;
|
|
15
40
|
}
|