@floegence/flowersec-core 0.1.1
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/LICENSE +22 -0
- package/README.md +42 -0
- package/YAMUX_ALIGNMENT.md +127 -0
- package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +12 -0
- package/dist/_examples/flowersec/demo/v1.facade.gen.js +15 -0
- package/dist/_examples/flowersec/demo/v1.gen.d.ts +16 -0
- package/dist/_examples/flowersec/demo/v1.gen.js +86 -0
- package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +11 -0
- package/dist/_examples/flowersec/demo/v1.rpc.gen.js +22 -0
- package/dist/browser/connect.d.ts +12 -0
- package/dist/browser/connect.js +31 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/client-connect/common.d.ts +26 -0
- package/dist/client-connect/common.js +167 -0
- package/dist/client-connect/connectCore.d.ts +42 -0
- package/dist/client-connect/connectCore.js +302 -0
- package/dist/client-connect/tunnelAttachCloseReason.d.ts +3 -0
- package/dist/client-connect/tunnelAttachCloseReason.js +16 -0
- package/dist/client.d.ts +17 -0
- package/dist/client.js +1 -0
- package/dist/direct-client/connect.d.ts +4 -0
- package/dist/direct-client/connect.js +67 -0
- package/dist/direct-client/index.d.ts +1 -0
- package/dist/direct-client/index.js +1 -0
- package/dist/e2ee/constants.d.ts +9 -0
- package/dist/e2ee/constants.js +18 -0
- package/dist/e2ee/errors.d.ts +5 -0
- package/dist/e2ee/errors.js +8 -0
- package/dist/e2ee/framing.d.ts +12 -0
- package/dist/e2ee/framing.js +57 -0
- package/dist/e2ee/handshake.d.ts +80 -0
- package/dist/e2ee/handshake.js +322 -0
- package/dist/e2ee/index.d.ts +7 -0
- package/dist/e2ee/index.js +7 -0
- package/dist/e2ee/kdf.d.ts +15 -0
- package/dist/e2ee/kdf.js +39 -0
- package/dist/e2ee/record.d.ts +11 -0
- package/dist/e2ee/record.js +69 -0
- package/dist/e2ee/secureChannel.d.ts +82 -0
- package/dist/e2ee/secureChannel.js +265 -0
- package/dist/e2ee/transcript.d.ts +23 -0
- package/dist/e2ee/transcript.js +31 -0
- package/dist/facade.d.ts +21 -0
- package/dist/facade.js +61 -0
- package/dist/gen/flowersec/controlplane/v1.gen.d.ts +36 -0
- package/dist/gen/flowersec/controlplane/v1.gen.js +135 -0
- package/dist/gen/flowersec/direct/v1.gen.d.ts +21 -0
- package/dist/gen/flowersec/direct/v1.gen.js +101 -0
- package/dist/gen/flowersec/e2ee/v1.gen.d.ts +68 -0
- package/dist/gen/flowersec/e2ee/v1.gen.js +194 -0
- package/dist/gen/flowersec/rpc/v1.gen.d.ts +30 -0
- package/dist/gen/flowersec/rpc/v1.gen.js +107 -0
- package/dist/gen/flowersec/tunnel/v1.gen.d.ts +23 -0
- package/dist/gen/flowersec/tunnel/v1.gen.js +104 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.js +19 -0
- package/dist/node/connect.d.ts +9 -0
- package/dist/node/connect.js +13 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.js +2 -0
- package/dist/node/wsFactory.d.ts +2 -0
- package/dist/node/wsFactory.js +69 -0
- package/dist/observability/index.d.ts +1 -0
- package/dist/observability/index.js +1 -0
- package/dist/observability/observer.d.ts +23 -0
- package/dist/observability/observer.js +28 -0
- package/dist/rpc/callError.d.ts +5 -0
- package/dist/rpc/callError.js +11 -0
- package/dist/rpc/caller.d.ts +8 -0
- package/dist/rpc/caller.js +1 -0
- package/dist/rpc/client.d.ts +22 -0
- package/dist/rpc/client.js +170 -0
- package/dist/rpc/framing.d.ts +4 -0
- package/dist/rpc/framing.js +24 -0
- package/dist/rpc/index.d.ts +6 -0
- package/dist/rpc/index.js +6 -0
- package/dist/rpc/server.d.ts +15 -0
- package/dist/rpc/server.js +67 -0
- package/dist/rpc/typed.d.ts +5 -0
- package/dist/rpc/typed.js +9 -0
- package/dist/rpc/validate.d.ts +2 -0
- package/dist/rpc/validate.js +27 -0
- package/dist/rpc-proxy/index.d.ts +1 -0
- package/dist/rpc-proxy/index.js +1 -0
- package/dist/rpc-proxy/rpcProxy.d.ts +13 -0
- package/dist/rpc-proxy/rpcProxy.js +59 -0
- package/dist/streamhello/index.d.ts +1 -0
- package/dist/streamhello/index.js +1 -0
- package/dist/streamhello/streamHello.d.ts +3 -0
- package/dist/streamhello/streamHello.js +13 -0
- package/dist/tunnel-client/connect.d.ts +7 -0
- package/dist/tunnel-client/connect.js +125 -0
- package/dist/tunnel-client/index.d.ts +1 -0
- package/dist/tunnel-client/index.js +1 -0
- package/dist/utils/base64url.d.ts +2 -0
- package/dist/utils/base64url.js +40 -0
- package/dist/utils/bin.d.ts +6 -0
- package/dist/utils/bin.js +55 -0
- package/dist/utils/errors.d.ts +26 -0
- package/dist/utils/errors.js +42 -0
- package/dist/utils/number.d.ts +2 -0
- package/dist/utils/number.js +9 -0
- package/dist/ws/index.d.ts +1 -0
- package/dist/ws/index.js +1 -0
- package/dist/ws-client/binaryTransport.d.ts +49 -0
- package/dist/ws-client/binaryTransport.js +301 -0
- package/dist/yamux/byteReader.d.ts +10 -0
- package/dist/yamux/byteReader.js +50 -0
- package/dist/yamux/constants.d.ts +10 -0
- package/dist/yamux/constants.js +14 -0
- package/dist/yamux/header.d.ts +17 -0
- package/dist/yamux/header.js +26 -0
- package/dist/yamux/index.d.ts +5 -0
- package/dist/yamux/index.js +5 -0
- package/dist/yamux/session.d.ts +44 -0
- package/dist/yamux/session.js +228 -0
- package/dist/yamux/stream.d.ts +30 -0
- package/dist/yamux/stream.js +222 -0
- package/package.json +112 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { readJsonFrame, writeJsonFrame } from "../rpc/framing.js";
|
|
2
|
+
// writeStreamHello sends the initial stream greeting.
|
|
3
|
+
export async function writeStreamHello(write, kind) {
|
|
4
|
+
const h = { kind, v: 1 };
|
|
5
|
+
await writeJsonFrame(write, h);
|
|
6
|
+
}
|
|
7
|
+
// readStreamHello reads and validates the stream greeting.
|
|
8
|
+
export async function readStreamHello(readExactly) {
|
|
9
|
+
const h = (await readJsonFrame(readExactly, 8 * 1024));
|
|
10
|
+
if (h.v !== 1 || typeof h.kind !== "string" || h.kind.length === 0)
|
|
11
|
+
throw new Error("bad StreamHello");
|
|
12
|
+
return h;
|
|
13
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ConnectOptionsBase } from "../client-connect/connectCore.js";
|
|
2
|
+
import type { ClientInternal } from "../client.js";
|
|
3
|
+
export type TunnelConnectOptions = ConnectOptionsBase & Readonly<{
|
|
4
|
+
/** Optional caller-provided endpoint instance ID (base64url). */
|
|
5
|
+
endpointInstanceId?: string;
|
|
6
|
+
}>;
|
|
7
|
+
export declare function connectTunnel(grant: unknown, opts: TunnelConnectOptions): Promise<ClientInternal>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Role as TunnelRole } from "../gen/flowersec/tunnel/v1.gen.js";
|
|
2
|
+
import { assertChannelInitGrant, Role as ControlRole } from "../gen/flowersec/controlplane/v1.gen.js";
|
|
3
|
+
import { base64urlDecode, base64urlEncode } from "../utils/base64url.js";
|
|
4
|
+
import { FlowersecError } from "../utils/errors.js";
|
|
5
|
+
import { randomBytes } from "../client-connect/common.js";
|
|
6
|
+
import { connectCore } from "../client-connect/connectCore.js";
|
|
7
|
+
function isRecord(v) {
|
|
8
|
+
return typeof v === "object" && v != null && !Array.isArray(v);
|
|
9
|
+
}
|
|
10
|
+
function unwrapGrant(v) {
|
|
11
|
+
if (v == null || typeof v !== "object")
|
|
12
|
+
return v;
|
|
13
|
+
const o = v;
|
|
14
|
+
if (o["grant_client"] != null)
|
|
15
|
+
return o["grant_client"];
|
|
16
|
+
if (o["grant_server"] != null)
|
|
17
|
+
return o["grant_server"];
|
|
18
|
+
return v;
|
|
19
|
+
}
|
|
20
|
+
// connectTunnel attaches to a tunnel and returns an RPC-ready session.
|
|
21
|
+
export async function connectTunnel(grant, opts) {
|
|
22
|
+
const input = unwrapGrant(grant);
|
|
23
|
+
if (input == null) {
|
|
24
|
+
throw new FlowersecError({ stage: "validate", code: "missing_grant", path: "tunnel", message: "missing grant" });
|
|
25
|
+
}
|
|
26
|
+
if (isRecord(input)) {
|
|
27
|
+
const suite = input["default_suite"];
|
|
28
|
+
// Keep "invalid_suite" as the stable error code even when the IDL validator rejects the enum value.
|
|
29
|
+
if (typeof suite === "number" && Number.isSafeInteger(suite) && suite !== 1 && suite !== 2) {
|
|
30
|
+
throw new FlowersecError({ stage: "validate", code: "invalid_suite", path: "tunnel", message: "invalid suite" });
|
|
31
|
+
}
|
|
32
|
+
const allowed = input["allowed_suites"];
|
|
33
|
+
if (Array.isArray(allowed)) {
|
|
34
|
+
for (const v of allowed) {
|
|
35
|
+
if (typeof v === "number" && Number.isSafeInteger(v) && v !== 1 && v !== 2) {
|
|
36
|
+
throw new FlowersecError({ stage: "validate", code: "invalid_suite", path: "tunnel", message: "invalid suite" });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
let checkedGrant;
|
|
42
|
+
try {
|
|
43
|
+
checkedGrant = assertChannelInitGrant(input);
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
throw new FlowersecError({ stage: "validate", code: "invalid_input", path: "tunnel", message: "invalid ChannelInitGrant", cause: e });
|
|
47
|
+
}
|
|
48
|
+
if (checkedGrant.tunnel_url === "") {
|
|
49
|
+
throw new FlowersecError({ stage: "validate", code: "missing_tunnel_url", path: "tunnel", message: "missing tunnel_url" });
|
|
50
|
+
}
|
|
51
|
+
if (checkedGrant.channel_id === "") {
|
|
52
|
+
throw new FlowersecError({ stage: "validate", code: "missing_channel_id", path: "tunnel", message: "missing channel_id" });
|
|
53
|
+
}
|
|
54
|
+
if (checkedGrant.token === "") {
|
|
55
|
+
throw new FlowersecError({ stage: "validate", code: "missing_token", path: "tunnel", message: "missing token" });
|
|
56
|
+
}
|
|
57
|
+
if (checkedGrant.channel_init_expire_at_unix_s <= 0) {
|
|
58
|
+
throw new FlowersecError({
|
|
59
|
+
stage: "validate",
|
|
60
|
+
code: "missing_init_exp",
|
|
61
|
+
path: "tunnel",
|
|
62
|
+
message: "missing channel_init_expire_at_unix_s",
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
const psk = base64urlDecode(checkedGrant.e2ee_psk_b64u);
|
|
67
|
+
if (psk.length !== 32) {
|
|
68
|
+
throw new Error("psk must be 32 bytes");
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch (e) {
|
|
72
|
+
throw new FlowersecError({ stage: "validate", code: "invalid_psk", path: "tunnel", message: "invalid e2ee_psk_b64u", cause: e });
|
|
73
|
+
}
|
|
74
|
+
const idleTimeoutSeconds = checkedGrant.idle_timeout_seconds;
|
|
75
|
+
if (checkedGrant.role !== ControlRole.Role_client) {
|
|
76
|
+
throw new FlowersecError({ stage: "validate", code: "role_mismatch", path: "tunnel", message: "expected role=client" });
|
|
77
|
+
}
|
|
78
|
+
const endpointInstanceId = opts.endpointInstanceId ?? base64urlEncode(randomBytes(24));
|
|
79
|
+
let eidBytes;
|
|
80
|
+
try {
|
|
81
|
+
eidBytes = base64urlDecode(endpointInstanceId);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
throw new FlowersecError({
|
|
85
|
+
stage: "validate",
|
|
86
|
+
code: "invalid_endpoint_instance_id",
|
|
87
|
+
path: "tunnel",
|
|
88
|
+
message: "invalid endpointInstanceId",
|
|
89
|
+
cause: e
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (eidBytes.length < 16 || eidBytes.length > 32) {
|
|
93
|
+
throw new FlowersecError({
|
|
94
|
+
stage: "validate",
|
|
95
|
+
code: "invalid_endpoint_instance_id",
|
|
96
|
+
path: "tunnel",
|
|
97
|
+
message: "endpointInstanceId must decode to 16..32 bytes"
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const attach = {
|
|
101
|
+
v: 1,
|
|
102
|
+
channel_id: checkedGrant.channel_id,
|
|
103
|
+
role: TunnelRole.Role_client,
|
|
104
|
+
token: checkedGrant.token,
|
|
105
|
+
endpoint_instance_id: endpointInstanceId
|
|
106
|
+
};
|
|
107
|
+
const attachJson = JSON.stringify(attach);
|
|
108
|
+
const keepaliveIntervalMs = opts.keepaliveIntervalMs !== undefined ? Math.max(0, opts.keepaliveIntervalMs) : defaultKeepaliveIntervalMs(idleTimeoutSeconds);
|
|
109
|
+
return await connectCore({
|
|
110
|
+
path: "tunnel",
|
|
111
|
+
wsUrl: checkedGrant.tunnel_url,
|
|
112
|
+
channelId: checkedGrant.channel_id,
|
|
113
|
+
e2eePskB64u: checkedGrant.e2ee_psk_b64u,
|
|
114
|
+
defaultSuite: checkedGrant.default_suite,
|
|
115
|
+
opts: { ...opts, keepaliveIntervalMs },
|
|
116
|
+
attach: { attachJson, endpointInstanceId }
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
function defaultKeepaliveIntervalMs(idleTimeoutSeconds) {
|
|
120
|
+
if (!Number.isFinite(idleTimeoutSeconds) || idleTimeoutSeconds <= 0)
|
|
121
|
+
return 0;
|
|
122
|
+
const idleMs = idleTimeoutSeconds * 1000;
|
|
123
|
+
const half = Math.floor(idleMs / 2);
|
|
124
|
+
return Math.max(500, half);
|
|
125
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./connect.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./connect.js";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
// base64urlEncode encodes bytes without padding.
|
|
2
|
+
export function base64urlEncode(bytes) {
|
|
3
|
+
let b64;
|
|
4
|
+
if (typeof Buffer !== "undefined") {
|
|
5
|
+
b64 = Buffer.from(bytes).toString("base64");
|
|
6
|
+
}
|
|
7
|
+
else {
|
|
8
|
+
let binary = "";
|
|
9
|
+
for (let i = 0; i < bytes.length; i++)
|
|
10
|
+
binary += String.fromCharCode(bytes[i]);
|
|
11
|
+
b64 = btoa(binary);
|
|
12
|
+
}
|
|
13
|
+
return b64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
|
14
|
+
}
|
|
15
|
+
// base64urlDecode decodes base64url without padding.
|
|
16
|
+
export function base64urlDecode(s) {
|
|
17
|
+
// Be strict: Node's Buffer base64 decoder is permissive (it may ignore invalid characters).
|
|
18
|
+
if (!/^[A-Za-z0-9_-]*$/.test(s))
|
|
19
|
+
throw new Error("invalid base64url");
|
|
20
|
+
// base64/base64url length cannot be 1 mod 4.
|
|
21
|
+
if (s.length % 4 === 1)
|
|
22
|
+
throw new Error("invalid base64url length");
|
|
23
|
+
const normalized = s.replace(/-/g, "+").replace(/_/g, "/");
|
|
24
|
+
const padLen = (4 - (normalized.length % 4)) % 4;
|
|
25
|
+
const padded = normalized + "=".repeat(padLen);
|
|
26
|
+
if (typeof Buffer !== "undefined") {
|
|
27
|
+
const out = new Uint8Array(Buffer.from(padded, "base64"));
|
|
28
|
+
// Roundtrip validation makes invalid inputs fail fast and deterministically.
|
|
29
|
+
if (base64urlEncode(out) !== s)
|
|
30
|
+
throw new Error("invalid base64url");
|
|
31
|
+
return out;
|
|
32
|
+
}
|
|
33
|
+
const binary = atob(padded);
|
|
34
|
+
const out = new Uint8Array(binary.length);
|
|
35
|
+
for (let i = 0; i < binary.length; i++)
|
|
36
|
+
out[i] = binary.charCodeAt(i);
|
|
37
|
+
if (base64urlEncode(out) !== s)
|
|
38
|
+
throw new Error("invalid base64url");
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function u16be(n: number): Uint8Array;
|
|
2
|
+
export declare function u32be(n: number): Uint8Array;
|
|
3
|
+
export declare function u64be(n: bigint): Uint8Array;
|
|
4
|
+
export declare function readU32be(buf: Uint8Array, off: number): number;
|
|
5
|
+
export declare function readU64be(buf: Uint8Array, off: number): bigint;
|
|
6
|
+
export declare function concatBytes(chunks: readonly Uint8Array[]): Uint8Array;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// u16be encodes a number into 2 bytes big-endian.
|
|
2
|
+
export function u16be(n) {
|
|
3
|
+
const b = new Uint8Array(2);
|
|
4
|
+
const v = n >>> 0;
|
|
5
|
+
b[0] = (v >>> 8) & 0xff;
|
|
6
|
+
b[1] = v & 0xff;
|
|
7
|
+
return b;
|
|
8
|
+
}
|
|
9
|
+
// u32be encodes a number into 4 bytes big-endian.
|
|
10
|
+
export function u32be(n) {
|
|
11
|
+
const b = new Uint8Array(4);
|
|
12
|
+
const v = n >>> 0;
|
|
13
|
+
b[0] = (v >>> 24) & 0xff;
|
|
14
|
+
b[1] = (v >>> 16) & 0xff;
|
|
15
|
+
b[2] = (v >>> 8) & 0xff;
|
|
16
|
+
b[3] = v & 0xff;
|
|
17
|
+
return b;
|
|
18
|
+
}
|
|
19
|
+
// u64be encodes a bigint into 8 bytes big-endian.
|
|
20
|
+
export function u64be(n) {
|
|
21
|
+
const b = new Uint8Array(8);
|
|
22
|
+
let v = n;
|
|
23
|
+
for (let i = 7; i >= 0; i--) {
|
|
24
|
+
b[i] = Number(v & 0xffn);
|
|
25
|
+
v >>= 8n;
|
|
26
|
+
}
|
|
27
|
+
return b;
|
|
28
|
+
}
|
|
29
|
+
// readU32be reads a 4-byte big-endian number.
|
|
30
|
+
export function readU32be(buf, off) {
|
|
31
|
+
return ((buf[off] << 24) |
|
|
32
|
+
(buf[off + 1] << 16) |
|
|
33
|
+
(buf[off + 2] << 8) |
|
|
34
|
+
buf[off + 3]) >>> 0;
|
|
35
|
+
}
|
|
36
|
+
// readU64be reads an 8-byte big-endian bigint.
|
|
37
|
+
export function readU64be(buf, off) {
|
|
38
|
+
let v = 0n;
|
|
39
|
+
for (let i = 0; i < 8; i++)
|
|
40
|
+
v = (v << 8n) | BigInt(buf[off + i]);
|
|
41
|
+
return v;
|
|
42
|
+
}
|
|
43
|
+
// concatBytes concatenates buffers into a single Uint8Array.
|
|
44
|
+
export function concatBytes(chunks) {
|
|
45
|
+
let total = 0;
|
|
46
|
+
for (const c of chunks)
|
|
47
|
+
total += c.length;
|
|
48
|
+
const out = new Uint8Array(total);
|
|
49
|
+
let off = 0;
|
|
50
|
+
for (const c of chunks) {
|
|
51
|
+
out.set(c, off);
|
|
52
|
+
off += c.length;
|
|
53
|
+
}
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare class TimeoutError extends Error {
|
|
2
|
+
constructor(message?: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class AbortError extends Error {
|
|
5
|
+
constructor(message?: string);
|
|
6
|
+
}
|
|
7
|
+
export type FlowersecPath = "auto" | "tunnel" | "direct";
|
|
8
|
+
export type FlowersecStage = "validate" | "connect" | "attach" | "handshake" | "secure" | "yamux" | "rpc" | "close";
|
|
9
|
+
export type FlowersecErrorCode = "timeout" | "canceled" | "invalid_version" | "invalid_input" | "invalid_option" | "invalid_endpoint_instance_id" | "invalid_psk" | "invalid_suite" | "missing_grant" | "missing_connect_info" | "missing_conn" | "missing_handler" | "missing_stream_kind" | "role_mismatch" | "missing_tunnel_url" | "missing_ws_url" | "missing_origin" | "missing_channel_id" | "missing_token" | "missing_init_exp" | "timestamp_after_init_exp" | "timestamp_out_of_skew" | "auth_tag_mismatch" | "resolve_failed" | "random_failed" | "upgrade_failed" | "dial_failed" | "attach_failed" | "too_many_connections" | "expected_attach" | "invalid_attach" | "invalid_token" | "channel_mismatch" | "token_replay" | "replace_rate_limited" | "handshake_failed" | "ping_failed" | "mux_failed" | "accept_stream_failed" | "open_stream_failed" | "stream_hello_failed" | "not_connected";
|
|
10
|
+
export declare class FlowersecError extends Error {
|
|
11
|
+
readonly code: FlowersecErrorCode;
|
|
12
|
+
readonly stage: FlowersecStage;
|
|
13
|
+
readonly path: FlowersecPath;
|
|
14
|
+
readonly cause?: unknown;
|
|
15
|
+
constructor(args: Readonly<{
|
|
16
|
+
code: FlowersecErrorCode;
|
|
17
|
+
stage: FlowersecStage;
|
|
18
|
+
path: FlowersecPath;
|
|
19
|
+
message?: string;
|
|
20
|
+
cause?: unknown;
|
|
21
|
+
}>);
|
|
22
|
+
}
|
|
23
|
+
export declare function isTimeoutError(e: unknown): e is TimeoutError;
|
|
24
|
+
export declare function isAbortError(e: unknown): e is AbortError;
|
|
25
|
+
export declare function isFlowersecError(e: unknown): e is FlowersecError;
|
|
26
|
+
export declare function throwIfAborted(signal?: AbortSignal, message?: string): void;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class TimeoutError extends Error {
|
|
2
|
+
constructor(message = "timeout") {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = "TimeoutError";
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class AbortError extends Error {
|
|
8
|
+
constructor(message = "aborted") {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "AbortError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class FlowersecError extends Error {
|
|
14
|
+
code;
|
|
15
|
+
stage;
|
|
16
|
+
path;
|
|
17
|
+
cause;
|
|
18
|
+
constructor(args) {
|
|
19
|
+
const prefix = `${args.path} ${args.stage} (${args.code})`;
|
|
20
|
+
const message = args.message != null && args.message !== "" ? `${prefix}: ${args.message}` : prefix;
|
|
21
|
+
super(message, args.cause !== undefined ? { cause: args.cause } : undefined);
|
|
22
|
+
this.name = "FlowersecError";
|
|
23
|
+
this.code = args.code;
|
|
24
|
+
this.stage = args.stage;
|
|
25
|
+
this.path = args.path;
|
|
26
|
+
if (args.cause !== undefined)
|
|
27
|
+
this.cause = args.cause;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
export function isTimeoutError(e) {
|
|
31
|
+
return e instanceof TimeoutError;
|
|
32
|
+
}
|
|
33
|
+
export function isAbortError(e) {
|
|
34
|
+
return e instanceof AbortError;
|
|
35
|
+
}
|
|
36
|
+
export function isFlowersecError(e) {
|
|
37
|
+
return e instanceof FlowersecError;
|
|
38
|
+
}
|
|
39
|
+
export function throwIfAborted(signal, message) {
|
|
40
|
+
if (signal?.aborted)
|
|
41
|
+
throw new AbortError(message);
|
|
42
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// isSafeU64Number checks whether v is a JSON number that can safely represent a u64 in JS.
|
|
2
|
+
// Values must be integers in [0, Number.MAX_SAFE_INTEGER].
|
|
3
|
+
export function isSafeU64Number(v) {
|
|
4
|
+
return typeof v === "number" && Number.isSafeInteger(v) && v >= 0 && v <= Number.MAX_SAFE_INTEGER;
|
|
5
|
+
}
|
|
6
|
+
// isSafeU32Number checks whether v is a JSON number that can safely represent a u32 in JS.
|
|
7
|
+
export function isSafeU32Number(v) {
|
|
8
|
+
return typeof v === "number" && Number.isSafeInteger(v) && v >= 0 && v <= 0xffffffff;
|
|
9
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../ws-client/binaryTransport.js";
|
package/dist/ws/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "../ws-client/binaryTransport.js";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { type ClientObserverLike } from "../observability/observer.js";
|
|
2
|
+
export declare class WsCloseError extends Error {
|
|
3
|
+
readonly code: number | undefined;
|
|
4
|
+
readonly reason: string | undefined;
|
|
5
|
+
constructor(code?: number, reason?: string);
|
|
6
|
+
}
|
|
7
|
+
export type WebSocketLike = {
|
|
8
|
+
binaryType: string;
|
|
9
|
+
readyState: number;
|
|
10
|
+
send(data: string | ArrayBuffer | Uint8Array): void;
|
|
11
|
+
close(code?: number, reason?: string): void;
|
|
12
|
+
addEventListener(type: "open" | "message" | "error" | "close", listener: (ev: any) => void): void;
|
|
13
|
+
removeEventListener(type: "open" | "message" | "error" | "close", listener: (ev: any) => void): void;
|
|
14
|
+
};
|
|
15
|
+
export declare class WebSocketBinaryTransport {
|
|
16
|
+
private readonly ws;
|
|
17
|
+
private readonly observer;
|
|
18
|
+
private readonly queue;
|
|
19
|
+
private queueHead;
|
|
20
|
+
private queueBytes;
|
|
21
|
+
private readonly maxQueuedBytes;
|
|
22
|
+
private waiters;
|
|
23
|
+
private waitersHead;
|
|
24
|
+
private waitersSettled;
|
|
25
|
+
private messageChain;
|
|
26
|
+
private error;
|
|
27
|
+
private localCloseRequested;
|
|
28
|
+
constructor(ws: WebSocketLike, opts?: Readonly<{
|
|
29
|
+
maxQueuedBytes?: number;
|
|
30
|
+
observer?: ClientObserverLike;
|
|
31
|
+
}>);
|
|
32
|
+
readBinary(opts?: Readonly<{
|
|
33
|
+
signal?: AbortSignal;
|
|
34
|
+
timeoutMs?: number;
|
|
35
|
+
}>): Promise<Uint8Array>;
|
|
36
|
+
writeBinary(frame: Uint8Array, opts?: Readonly<{
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
}>): Promise<void>;
|
|
39
|
+
close(): void;
|
|
40
|
+
private handleMessage;
|
|
41
|
+
private readonly onMessage;
|
|
42
|
+
private readonly onError;
|
|
43
|
+
private readonly onClose;
|
|
44
|
+
private push;
|
|
45
|
+
private shiftQueue;
|
|
46
|
+
private shiftWaiter;
|
|
47
|
+
private compactWaitersMaybe;
|
|
48
|
+
private fail;
|
|
49
|
+
}
|