@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.
Files changed (270) hide show
  1. package/README.md +91 -22
  2. package/THIRD_PARTY_NOTICES.md +141 -0
  3. package/dist/browser/connectSession.d.ts +13 -0
  4. package/dist/browser/connectSession.js +40 -0
  5. package/dist/browser/index.d.ts +3 -10
  6. package/dist/browser/index.js +2 -5
  7. package/dist/browser/runtimeCapability.d.ts +13 -0
  8. package/dist/browser/runtimeCapability.js +27 -0
  9. package/dist/browser/sessionRuntime.d.ts +2 -0
  10. package/dist/browser/sessionRuntime.js +21 -0
  11. package/dist/browser/webTransportClient.d.ts +8 -0
  12. package/dist/browser/webTransportClient.js +55 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +126 -0
  15. package/dist/cliSpendMarker.d.ts +1 -0
  16. package/dist/cliSpendMarker.js +22 -0
  17. package/dist/connectionController.d.ts +54 -0
  18. package/dist/connectionController.js +438 -0
  19. package/dist/connector/adapters/webSocketCandidate.d.ts +7 -0
  20. package/dist/connector/adapters/webSocketCandidate.js +116 -0
  21. package/dist/connector/adapters/webTransportCandidate.d.ts +5 -0
  22. package/dist/connector/adapters/webTransportCandidate.js +73 -0
  23. package/dist/connector/admissionCommit.d.ts +31 -0
  24. package/dist/connector/admissionCommit.js +239 -0
  25. package/dist/connector/sessionAcceptor.d.ts +16 -0
  26. package/dist/connector/sessionAcceptor.js +114 -0
  27. package/dist/connector/sessionConfig.d.ts +3 -0
  28. package/dist/connector/sessionConfig.js +30 -0
  29. package/dist/connector/sessionConnector.d.ts +45 -0
  30. package/dist/connector/sessionConnector.js +603 -0
  31. package/dist/defaults.d.ts +7 -7
  32. package/dist/defaults.js +7 -7
  33. package/dist/facade.d.ts +12 -30
  34. package/dist/facade.js +6 -30
  35. package/dist/node/connectSession.d.ts +21 -0
  36. package/dist/node/connectSession.js +62 -0
  37. package/dist/node/index.d.ts +3 -11
  38. package/dist/node/index.js +2 -8
  39. package/dist/node/runtimeCapability.d.ts +7 -0
  40. package/dist/node/runtimeCapability.js +10 -0
  41. package/dist/node/sessionRuntime.d.ts +2 -0
  42. package/dist/node/sessionRuntime.js +13 -0
  43. package/dist/node/webTransportClient.d.ts +9 -0
  44. package/dist/node/webTransportClient.js +56 -0
  45. package/dist/node/webTransportServer.d.ts +22 -0
  46. package/dist/node/webTransportServer.js +100 -0
  47. package/dist/node/wsFactory.d.ts +3 -1
  48. package/dist/node/wsFactory.js +4 -3
  49. package/dist/proxy/controllerGuard.d.ts +4 -4
  50. package/dist/proxy/controllerGuard.js +83 -177
  51. package/dist/proxy/disableUpstreamServiceWorkerRegister.d.ts +3 -1
  52. package/dist/proxy/disableUpstreamServiceWorkerRegister.js +19 -17
  53. package/dist/proxy/index.d.ts +16 -15
  54. package/dist/proxy/index.js +8 -14
  55. package/dist/proxy/integration.d.ts +23 -71
  56. package/dist/proxy/integration.js +37 -340
  57. package/dist/proxy/registerServiceWorker.d.ts +1 -10
  58. package/dist/proxy/registerServiceWorker.js +43 -105
  59. package/dist/proxy/runtime.d.ts +3 -63
  60. package/dist/proxy/runtime.js +349 -462
  61. package/dist/proxy/scope.d.ts +6 -0
  62. package/dist/proxy/scope.js +131 -0
  63. package/dist/proxy/serviceWorker.d.ts +5 -16
  64. package/dist/proxy/serviceWorker.js +230 -617
  65. package/dist/proxy/stream.d.ts +9 -0
  66. package/dist/proxy/stream.js +40 -0
  67. package/dist/proxy/types.d.ts +78 -30
  68. package/dist/proxy/windowBridge.d.ts +61 -0
  69. package/dist/proxy/windowBridge.js +344 -0
  70. package/dist/proxy/wsPatch.d.ts +4 -14
  71. package/dist/proxy/wsPatch.js +173 -268
  72. package/dist/public/artifact.d.ts +11 -0
  73. package/dist/public/artifact.js +44 -0
  74. package/dist/public/artifactLease.d.ts +10 -0
  75. package/dist/public/artifactLease.js +48 -0
  76. package/dist/public/connectError.d.ts +6 -0
  77. package/dist/public/connectError.js +9 -0
  78. package/dist/public/contract.d.ts +83 -0
  79. package/dist/public/contract.js +17 -0
  80. package/dist/public/streamMetadata.d.ts +10 -0
  81. package/dist/public/streamMetadata.js +40 -0
  82. package/dist/rpc/caller.d.ts +1 -1
  83. package/dist/rpc/client.d.ts +1 -4
  84. package/dist/rpc/client.js +0 -21
  85. package/dist/rpc/rpcProxy.d.ts +1 -1
  86. package/dist/rpc/server.d.ts +1 -1
  87. package/dist/rpc/validate.d.ts +1 -1
  88. package/dist/rpc/wire.d.ts +15 -0
  89. package/dist/runtime/errors.d.ts +6 -0
  90. package/dist/runtime/errors.js +10 -0
  91. package/dist/transport/webSocketAdapter.d.ts +24 -0
  92. package/dist/transport/webSocketAdapter.js +234 -0
  93. package/dist/transport/webTransportAdapter.d.ts +27 -0
  94. package/dist/transport/webTransportAdapter.js +519 -0
  95. package/dist/utils/errors.d.ts +2 -20
  96. package/dist/utils/errors.js +73 -20
  97. package/dist/v2/admissionError.d.ts +4 -0
  98. package/dist/v2/admissionError.js +8 -0
  99. package/dist/v2/artifact.d.ts +128 -0
  100. package/dist/v2/artifact.js +1126 -0
  101. package/dist/v2/artifactLease.d.ts +1 -0
  102. package/dist/v2/artifactLease.js +7 -0
  103. package/dist/v2/capability.d.ts +29 -0
  104. package/dist/v2/capability.js +190 -0
  105. package/dist/v2/carrier.d.ts +79 -0
  106. package/dist/v2/carrier.js +405 -0
  107. package/dist/v2/contract.d.ts +37 -0
  108. package/dist/v2/contract.js +1 -0
  109. package/dist/v2/handshake.d.ts +80 -0
  110. package/dist/v2/handshake.js +470 -0
  111. package/dist/v2/opaqueArtifact.d.ts +1 -0
  112. package/dist/v2/opaqueArtifact.js +2 -0
  113. package/dist/v2/protocol.d.ts +99 -0
  114. package/dist/v2/protocol.js +739 -0
  115. package/dist/v2/publicSession.d.ts +1 -0
  116. package/dist/v2/publicSession.js +179 -0
  117. package/dist/v2/retryDisposition.d.ts +16 -0
  118. package/dist/v2/retryDisposition.js +30 -0
  119. package/dist/v2/session.d.ts +277 -0
  120. package/dist/v2/session.js +1879 -0
  121. package/dist/v2/streamLifetimeLedger.d.ts +31 -0
  122. package/dist/v2/streamLifetimeLedger.js +131 -0
  123. package/dist/v2/streamMetadata.d.ts +1 -0
  124. package/dist/v2/streamMetadata.js +7 -0
  125. package/dist/v2/unreliableMessage.d.ts +43 -0
  126. package/dist/v2/unreliableMessage.js +252 -0
  127. package/dist/vendor/tr46.d.ts +10 -0
  128. package/dist/vendor/tr46.js +501 -0
  129. package/dist/ws-client/binaryTransport.d.ts +0 -3
  130. package/dist/ws-client/binaryTransport.js +15 -39
  131. package/dist/yamux/errors.d.ts +0 -4
  132. package/dist/yamux/errors.js +0 -9
  133. package/dist/yamux/session.d.ts +0 -5
  134. package/dist/yamux/session.js +4 -70
  135. package/dist/yamux/stream.d.ts +1 -0
  136. package/dist/yamux/stream.js +4 -0
  137. package/package.json +29 -83
  138. package/sbom/cyclonedx.json +2764 -0
  139. package/sbom/spdx.json +1947 -0
  140. package/YAMUX_ALIGNMENT.md +0 -127
  141. package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +0 -12
  142. package/dist/_examples/flowersec/demo/v1.facade.gen.js +0 -15
  143. package/dist/_examples/flowersec/demo/v1.gen.d.ts +0 -16
  144. package/dist/_examples/flowersec/demo/v1.gen.js +0 -86
  145. package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +0 -11
  146. package/dist/_examples/flowersec/demo/v1.rpc.gen.js +0 -22
  147. package/dist/browser/connect.d.ts +0 -12
  148. package/dist/browser/connect.js +0 -31
  149. package/dist/browser/controlplane.d.ts +0 -15
  150. package/dist/browser/controlplane.js +0 -64
  151. package/dist/browser/reconnectConfig.d.ts +0 -15
  152. package/dist/browser/reconnectConfig.js +0 -20
  153. package/dist/client-connect/common.d.ts +0 -26
  154. package/dist/client-connect/common.js +0 -167
  155. package/dist/client-connect/connectCore.d.ts +0 -61
  156. package/dist/client-connect/connectCore.js +0 -572
  157. package/dist/client-connect/contract.d.ts +0 -8
  158. package/dist/client-connect/contract.js +0 -51
  159. package/dist/client-connect/termination.d.ts +0 -6
  160. package/dist/client-connect/termination.js +0 -7
  161. package/dist/client-connect/transportSecurity.d.ts +0 -27
  162. package/dist/client-connect/transportSecurity.js +0 -199
  163. package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
  164. package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
  165. package/dist/client.d.ts +0 -23
  166. package/dist/connect/artifact.d.ts +0 -37
  167. package/dist/connect/artifact.js +0 -217
  168. package/dist/connect/internalNormalize.d.ts +0 -22
  169. package/dist/connect/internalNormalize.js +0 -173
  170. package/dist/controlplane/channelInit.d.ts +0 -27
  171. package/dist/controlplane/channelInit.js +0 -116
  172. package/dist/controlplane/http.d.ts +0 -16
  173. package/dist/controlplane/http.js +0 -59
  174. package/dist/controlplane/index.d.ts +0 -6
  175. package/dist/controlplane/index.js +0 -5
  176. package/dist/controlplane/issuer.d.ts +0 -18
  177. package/dist/controlplane/issuer.js +0 -113
  178. package/dist/controlplane/request.d.ts +0 -51
  179. package/dist/controlplane/request.js +0 -273
  180. package/dist/controlplane/token.d.ts +0 -33
  181. package/dist/controlplane/token.js +0 -119
  182. package/dist/direct-client/connect.d.ts +0 -9
  183. package/dist/direct-client/connect.js +0 -90
  184. package/dist/direct-client/index.d.ts +0 -1
  185. package/dist/direct-client/index.js +0 -1
  186. package/dist/e2ee/constants.d.ts +0 -9
  187. package/dist/e2ee/constants.js +0 -18
  188. package/dist/e2ee/errors.d.ts +0 -5
  189. package/dist/e2ee/errors.js +0 -8
  190. package/dist/e2ee/framing.d.ts +0 -12
  191. package/dist/e2ee/framing.js +0 -57
  192. package/dist/e2ee/handshake.d.ts +0 -88
  193. package/dist/e2ee/handshake.js +0 -353
  194. package/dist/e2ee/index.d.ts +0 -7
  195. package/dist/e2ee/index.js +0 -7
  196. package/dist/e2ee/kdf.d.ts +0 -15
  197. package/dist/e2ee/kdf.js +0 -39
  198. package/dist/e2ee/record.d.ts +0 -11
  199. package/dist/e2ee/record.js +0 -76
  200. package/dist/e2ee/secureChannel.d.ts +0 -92
  201. package/dist/e2ee/secureChannel.js +0 -328
  202. package/dist/e2ee/transcript.d.ts +0 -23
  203. package/dist/e2ee/transcript.js +0 -31
  204. package/dist/endpoint/index.d.ts +0 -84
  205. package/dist/endpoint/index.js +0 -557
  206. package/dist/endpoint/node.d.ts +0 -9
  207. package/dist/endpoint/node.js +0 -51
  208. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
  209. package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
  210. package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
  211. package/dist/gen/flowersec/direct/v1.gen.js +0 -101
  212. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
  213. package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
  214. package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
  215. package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
  216. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
  217. package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
  218. package/dist/index.d.ts +0 -22
  219. package/dist/index.js +0 -22
  220. package/dist/node/connect.d.ts +0 -12
  221. package/dist/node/connect.js +0 -29
  222. package/dist/node/reconnectConfig.d.ts +0 -16
  223. package/dist/node/reconnectConfig.js +0 -20
  224. package/dist/observability/index.d.ts +0 -1
  225. package/dist/observability/index.js +0 -1
  226. package/dist/observability/observer.d.ts +0 -52
  227. package/dist/observability/observer.js +0 -314
  228. package/dist/proxy/appWindow.d.ts +0 -37
  229. package/dist/proxy/appWindow.js +0 -273
  230. package/dist/proxy/bootstrap.d.ts +0 -35
  231. package/dist/proxy/bootstrap.js +0 -119
  232. package/dist/proxy/constants.d.ts +0 -7
  233. package/dist/proxy/constants.js +0 -8
  234. package/dist/proxy/controllerWindow.d.ts +0 -12
  235. package/dist/proxy/controllerWindow.js +0 -384
  236. package/dist/proxy/cookieJar.d.ts +0 -7
  237. package/dist/proxy/cookieJar.js +0 -136
  238. package/dist/proxy/headerPolicy.d.ts +0 -14
  239. package/dist/proxy/headerPolicy.js +0 -134
  240. package/dist/proxy/portStream.d.ts +0 -5
  241. package/dist/proxy/portStream.js +0 -256
  242. package/dist/proxy/preset.d.ts +0 -29
  243. package/dist/proxy/preset.js +0 -111
  244. package/dist/proxy/runtimeScope.d.ts +0 -57
  245. package/dist/proxy/runtimeScope.js +0 -218
  246. package/dist/proxy/server.d.ts +0 -28
  247. package/dist/proxy/server.js +0 -833
  248. package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
  249. package/dist/proxy/windowBridgeProtocol.js +0 -11
  250. package/dist/reconnect/artifactControlplane.d.ts +0 -16
  251. package/dist/reconnect/artifactControlplane.js +0 -35
  252. package/dist/reconnect/index.d.ts +0 -36
  253. package/dist/reconnect/index.js +0 -388
  254. package/dist/rpc-proxy/index.d.ts +0 -1
  255. package/dist/rpc-proxy/index.js +0 -1
  256. package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
  257. package/dist/rpc-proxy/rpcProxy.js +0 -1
  258. package/dist/streamhello/index.d.ts +0 -1
  259. package/dist/streamhello/index.js +0 -1
  260. package/dist/streamhello/streamHello.d.ts +0 -3
  261. package/dist/streamhello/streamHello.js +0 -13
  262. package/dist/streamio/index.d.ts +0 -32
  263. package/dist/streamio/index.js +0 -87
  264. package/dist/tunnel-client/connect.d.ts +0 -9
  265. package/dist/tunnel-client/connect.js +0 -157
  266. package/dist/tunnel-client/index.d.ts +0 -1
  267. package/dist/tunnel-client/index.js +0 -1
  268. package/dist/ws/index.d.ts +0 -1
  269. package/dist/ws/index.js +0 -1
  270. /package/dist/{client.js → rpc/wire.js} +0 -0
@@ -1,51 +0,0 @@
1
- import { base64urlDecode } from "../utils/base64url.js";
2
- import { FlowersecError } from "../utils/errors.js";
3
- const textEncoder = new TextEncoder();
4
- export const CHANNEL_ID_MAX_BYTES = 256;
5
- export function prepareChannelId(raw, path) {
6
- const channelId = typeof raw === "string" ? raw.trim() : "";
7
- if (channelId === "") {
8
- throw new FlowersecError({ path, stage: "validate", code: "missing_channel_id", message: "missing channel_id" });
9
- }
10
- if (textEncoder.encode(channelId).length > CHANNEL_ID_MAX_BYTES) {
11
- throw new FlowersecError({ path, stage: "validate", code: "invalid_input", message: "channel_id too long" });
12
- }
13
- return channelId;
14
- }
15
- export function assertTunnelGrantContract(grant, expectedRole) {
16
- if (grant.role !== expectedRole) {
17
- throw new FlowersecError({ stage: "validate", code: "role_mismatch", path: "tunnel", message: `expected role=${expectedRole === 1 ? "client" : "server"}` });
18
- }
19
- const allowedSuites = Array.isArray(grant.allowed_suites) ? grant.allowed_suites : [];
20
- if (allowedSuites.length === 0) {
21
- throw new FlowersecError({ stage: "validate", code: "invalid_suite", path: "tunnel", message: "allowed_suites must be non-empty" });
22
- }
23
- for (const suite of allowedSuites) {
24
- assertSupportedSuite(suite, "tunnel");
25
- }
26
- assertSupportedSuite(grant.default_suite, "tunnel");
27
- if (!allowedSuites.includes(grant.default_suite)) {
28
- throw new FlowersecError({ stage: "validate", code: "invalid_suite", path: "tunnel", message: "default_suite must be included in allowed_suites" });
29
- }
30
- }
31
- export function assertDirectConnectContract(info) {
32
- assertSupportedSuite(info.default_suite, "direct");
33
- }
34
- export function assertValidPSK(raw, path) {
35
- const pskB64u = typeof raw === "string" ? raw.trim() : "";
36
- try {
37
- const psk = base64urlDecode(pskB64u);
38
- if (psk.length !== 32) {
39
- throw new Error("psk must be 32 bytes");
40
- }
41
- }
42
- catch (e) {
43
- throw new FlowersecError({ stage: "validate", code: "invalid_psk", path, message: "invalid e2ee_psk_b64u", cause: e });
44
- }
45
- return pskB64u;
46
- }
47
- function assertSupportedSuite(suite, path) {
48
- if (suite !== 1 && suite !== 2) {
49
- throw new FlowersecError({ stage: "validate", code: "invalid_suite", path, message: "invalid suite" });
50
- }
51
- }
@@ -1,6 +0,0 @@
1
- import type { Client } from "../client.js";
2
- export type ClientTermination = Readonly<{
3
- error: Error;
4
- }>;
5
- export declare function registerClientTermination(client: Client, termination: Promise<ClientTermination>): void;
6
- export declare function getClientTermination(client: Client): Promise<ClientTermination> | undefined;
@@ -1,7 +0,0 @@
1
- const clientTerminations = new WeakMap();
2
- export function registerClientTermination(client, termination) {
3
- clientTerminations.set(client, termination);
4
- }
5
- export function getClientTermination(client) {
6
- return clientTerminations.get(client);
7
- }
@@ -1,27 +0,0 @@
1
- import type { ClientPath } from "../client.js";
2
- import { type ClientObserverLike } from "../observability/observer.js";
3
- export declare const RequireTLS: "require_tls";
4
- export declare const AllowPlaintextForLoopback: "allow_plaintext_for_loopback";
5
- export declare const PlaintextRiskAcceptance: {
6
- readonly acceptPreE2ECredentialExposure: "accept_pre_e2ee_credential_exposure";
7
- };
8
- export type PlaintextRiskAcceptance = typeof PlaintextRiskAcceptance[keyof typeof PlaintextRiskAcceptance];
9
- export type NetworkPlaintextPolicyOptions = Readonly<{
10
- allowedHosts: readonly string[];
11
- riskAcceptance: PlaintextRiskAcceptance;
12
- }>;
13
- export type TransportSecurityPolicyInput = Readonly<{
14
- path: ClientPath;
15
- scheme: "ws" | "wss";
16
- host: string;
17
- runtime: "browser" | "node" | "other";
18
- }>;
19
- export type TransportSecurityPolicyPreset = typeof RequireTLS | typeof AllowPlaintextForLoopback;
20
- export type TransportSecurityPolicy = TransportSecurityPolicyPreset | ((input: TransportSecurityPolicyInput) => boolean | Promise<boolean>);
21
- export declare function createNetworkPlaintextPolicy(options: NetworkPlaintextPolicyOptions): TransportSecurityPolicy;
22
- export declare function enforceTransportSecurity(args: Readonly<{
23
- rawUrl: string;
24
- path: ClientPath;
25
- policy?: TransportSecurityPolicy;
26
- observer?: ClientObserverLike;
27
- }>): Promise<void>;
@@ -1,199 +0,0 @@
1
- import { FlowersecError } from "../utils/errors.js";
2
- import { emitObserverDiagnostic } from "../observability/observer.js";
3
- export const RequireTLS = "require_tls";
4
- export const AllowPlaintextForLoopback = "allow_plaintext_for_loopback";
5
- export const PlaintextRiskAcceptance = {
6
- acceptPreE2ECredentialExposure: "accept_pre_e2ee_credential_exposure",
7
- };
8
- export function createNetworkPlaintextPolicy(options) {
9
- if (options?.riskAcceptance !== PlaintextRiskAcceptance.acceptPreE2ECredentialExposure) {
10
- throw new Error("network plaintext policy requires explicit pre-E2EE credential exposure acceptance");
11
- }
12
- if (!Array.isArray(options.allowedHosts) || options.allowedHosts.length === 0) {
13
- throw new Error("network plaintext policy requires at least one allowed host");
14
- }
15
- const allowedHosts = new Set(options.allowedHosts.map(canonicalNetworkPlaintextHost));
16
- return (input) => input.scheme === "wss" || (input.scheme === "ws" && allowedHosts.has(input.host));
17
- }
18
- export async function enforceTransportSecurity(args) {
19
- let target;
20
- try {
21
- target = parseWebSocketTarget(args.rawUrl);
22
- }
23
- catch (cause) {
24
- throw denied(args.path, cause);
25
- }
26
- const input = {
27
- path: args.path,
28
- scheme: target.scheme,
29
- host: target.host,
30
- runtime: detectRuntime(),
31
- };
32
- let allowed = false;
33
- const policy = args.policy ?? RequireTLS;
34
- try {
35
- if (typeof policy === "function") {
36
- allowed = await policy(input) === true;
37
- }
38
- else {
39
- allowed = evaluatePreset(policy, target);
40
- }
41
- }
42
- catch (cause) {
43
- throw denied(args.path, cause);
44
- }
45
- if (!allowed)
46
- throw denied(args.path);
47
- if (target.scheme === "ws") {
48
- emitObserverDiagnostic(args.observer, {
49
- path: args.path,
50
- stage: "transport",
51
- code_domain: "event",
52
- code: "plaintext_transport",
53
- result: "skip",
54
- });
55
- }
56
- }
57
- function denied(path, cause) {
58
- return new FlowersecError({
59
- path,
60
- stage: "validate",
61
- code: "transport_policy_denied",
62
- message: "transport security policy denied websocket URL",
63
- ...(cause === undefined ? {} : { cause }),
64
- });
65
- }
66
- function evaluatePreset(policy, target) {
67
- switch (policy) {
68
- case RequireTLS:
69
- return target.scheme === "wss";
70
- case AllowPlaintextForLoopback:
71
- return target.scheme === "wss" || isLiteralLoopbackHost(target.host);
72
- }
73
- }
74
- function parseWebSocketTarget(rawUrl) {
75
- const raw = rawUrl.trim();
76
- const match = /^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^/?#]*)(?:[/?#]|$)/.exec(raw);
77
- const scheme = match?.[1]?.toLowerCase();
78
- const authority = match?.[2] ?? "";
79
- if ((scheme !== "ws" && scheme !== "wss") || authority === "" || authority.includes("@")) {
80
- throw new Error("invalid websocket URL");
81
- }
82
- let host;
83
- if (authority.startsWith("[")) {
84
- const end = authority.indexOf("]");
85
- if (end <= 1 || (authority.slice(end + 1) !== "" && !/^:\d+$/.test(authority.slice(end + 1)))) {
86
- throw new Error("invalid websocket URL");
87
- }
88
- host = authority.slice(1, end).toLowerCase();
89
- }
90
- else {
91
- const pieces = authority.split(":");
92
- if (pieces.length > 2 || (pieces.length === 2 && !/^\d+$/.test(pieces[1] ?? ""))) {
93
- throw new Error("invalid websocket URL");
94
- }
95
- host = (pieces[0] ?? "").toLowerCase();
96
- }
97
- if (host === "")
98
- throw new Error("invalid websocket URL");
99
- return { scheme, host };
100
- }
101
- function isLiteralLoopbackHost(host) {
102
- if (host === "localhost" || host === "::1")
103
- return true;
104
- const parts = host.split(".");
105
- if (parts.length !== 4)
106
- return false;
107
- const octets = [];
108
- for (const part of parts) {
109
- if (!/^(0|[1-9]\d{0,2})$/.test(part))
110
- return false;
111
- const value = Number(part);
112
- if (value > 255)
113
- return false;
114
- octets.push(value);
115
- }
116
- return octets[0] === 127;
117
- }
118
- function canonicalNetworkPlaintextHost(rawHost) {
119
- const host = String(rawHost ?? "").trim();
120
- if (host === "" || host !== host.toLowerCase() || /[@/?#%\[\]]/.test(host)) {
121
- throw new Error(`invalid network plaintext allowed host ${JSON.stringify(rawHost)}`);
122
- }
123
- if (host.includes(":")) {
124
- return canonicalNetworkIPv6Host(host);
125
- }
126
- const octets = canonicalIPv4Octets(host);
127
- const firstOctet = octets[0];
128
- const secondOctet = octets[1];
129
- if (firstOctet == null || secondOctet == null) {
130
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(rawHost)}`);
131
- }
132
- if (firstOctet === 127 || (firstOctet === 169 && secondOctet === 254) || firstOctet >= 224 || octets.every((value) => value === 0)) {
133
- throw new Error(`network plaintext allowed host must be a non-loopback unicast IP literal: ${JSON.stringify(rawHost)}`);
134
- }
135
- return host;
136
- }
137
- function canonicalIPv4Octets(host) {
138
- const parts = host.split(".");
139
- if (parts.length !== 4) {
140
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
141
- }
142
- const octets = parts.map((part) => {
143
- if (!/^(0|[1-9]\d{0,2})$/.test(part)) {
144
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
145
- }
146
- const value = Number(part);
147
- if (value > 255) {
148
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
149
- }
150
- return value;
151
- });
152
- return octets;
153
- }
154
- function canonicalNetworkIPv6Host(host) {
155
- let parsed;
156
- try {
157
- parsed = new URL(`http://[${host}]/`);
158
- }
159
- catch {
160
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
161
- }
162
- const canonical = parsed.hostname.replace(/^\[|\]$/g, "").toLowerCase();
163
- if (canonical !== host) {
164
- throw new Error(`network plaintext allowed host must be a canonical IP literal: ${JSON.stringify(host)}`);
165
- }
166
- const words = expandIPv6Words(canonical);
167
- const unspecified = words.every((word) => word === 0);
168
- const loopback = words.slice(0, 7).every((word) => word === 0) && words[7] === 1;
169
- const mappedIPv4 = words.slice(0, 5).every((word) => word === 0) && words[5] === 0xffff;
170
- const multicast = (words[0] & 0xff00) === 0xff00;
171
- const linkLocal = (words[0] & 0xffc0) === 0xfe80;
172
- if (unspecified || loopback || mappedIPv4 || multicast || linkLocal) {
173
- throw new Error(`network plaintext allowed host must be a non-loopback unicast IP literal: ${JSON.stringify(host)}`);
174
- }
175
- return canonical;
176
- }
177
- function expandIPv6Words(host) {
178
- const halves = host.split("::");
179
- if (halves.length > 2)
180
- throw new Error("invalid IPv6 host");
181
- const left = halves[0] === "" ? [] : halves[0].split(":");
182
- const right = halves.length === 1 || halves[1] === "" ? [] : halves[1].split(":");
183
- const missing = 8 - left.length - right.length;
184
- if ((halves.length === 1 && missing !== 0) || (halves.length === 2 && missing < 1)) {
185
- throw new Error("invalid IPv6 host");
186
- }
187
- const words = [...left, ...Array.from({ length: missing }, () => "0"), ...right].map((part) => Number.parseInt(part, 16));
188
- if (words.length !== 8 || words.some((word) => !Number.isInteger(word) || word < 0 || word > 0xffff)) {
189
- throw new Error("invalid IPv6 host");
190
- }
191
- return words;
192
- }
193
- function detectRuntime() {
194
- if (typeof window !== "undefined" && typeof window.document !== "undefined")
195
- return "browser";
196
- if (typeof process !== "undefined" && process.versions?.node != null)
197
- return "node";
198
- return "other";
199
- }
@@ -1,3 +0,0 @@
1
- export declare const tunnelAttachCloseReasons: readonly ["too_many_connections", "expected_attach", "invalid_attach", "invalid_token", "channel_mismatch", "init_exp_mismatch", "idle_timeout_mismatch", "role_mismatch", "token_replay", "tenant_mismatch", "policy_denied", "policy_error", "replace_rate_limited", "attach_failed", "timeout", "canceled"];
2
- export type TunnelAttachCloseReason = (typeof tunnelAttachCloseReasons)[number];
3
- export declare function isTunnelAttachCloseReason(v: string | undefined): v is TunnelAttachCloseReason;
@@ -1,23 +0,0 @@
1
- export const tunnelAttachCloseReasons = [
2
- "too_many_connections",
3
- "expected_attach",
4
- "invalid_attach",
5
- "invalid_token",
6
- "channel_mismatch",
7
- "init_exp_mismatch",
8
- "idle_timeout_mismatch",
9
- "role_mismatch",
10
- "token_replay",
11
- "tenant_mismatch",
12
- "policy_denied",
13
- "policy_error",
14
- "replace_rate_limited",
15
- "attach_failed",
16
- "timeout",
17
- "canceled",
18
- ];
19
- export function isTunnelAttachCloseReason(v) {
20
- if (v == null)
21
- return false;
22
- return tunnelAttachCloseReasons.includes(v);
23
- }
package/dist/client.d.ts DELETED
@@ -1,23 +0,0 @@
1
- import type { SecureChannel } from "./e2ee/secureChannel.js";
2
- import type { RpcClient } from "./rpc/client.js";
3
- import type { YamuxSession } from "./yamux/session.js";
4
- import type { YamuxStream } from "./yamux/stream.js";
5
- export type ClientPath = "tunnel" | "direct";
6
- export type Client = Readonly<{
7
- path: ClientPath;
8
- endpointInstanceId?: string;
9
- rpc: RpcClient;
10
- openStream: (kind: string, opts?: Readonly<{
11
- signal?: AbortSignal;
12
- }>) => Promise<YamuxStream>;
13
- ping: () => Promise<void>;
14
- /** Emits an authenticated rekey record and advances the E2EE send key. */
15
- rekey: () => Promise<void>;
16
- /** Performs an acknowledged Yamux round trip and returns RTT in milliseconds. */
17
- probeLiveness: () => Promise<number>;
18
- close: () => void;
19
- }>;
20
- export type ClientInternal = Client & Readonly<{
21
- secure: SecureChannel;
22
- mux: YamuxSession;
23
- }>;
@@ -1,37 +0,0 @@
1
- import { type ChannelInitGrant } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { type DirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
3
- export type ConnectArtifactTransport = "tunnel" | "direct";
4
- export type CorrelationKV = Readonly<{
5
- key: string;
6
- value: string;
7
- }>;
8
- export type CorrelationContext = Readonly<{
9
- v: 1;
10
- trace_id?: string;
11
- session_id?: string;
12
- tags: readonly CorrelationKV[];
13
- }>;
14
- export type ScopePayload = Record<string, unknown>;
15
- export type ScopeMetadataEntry = Readonly<{
16
- scope: string;
17
- scope_version: number;
18
- critical: boolean;
19
- payload: ScopePayload;
20
- }>;
21
- export type TunnelClientConnectArtifact = Readonly<{
22
- v: 1;
23
- transport: "tunnel";
24
- tunnel_grant: ChannelInitGrant;
25
- scoped?: readonly ScopeMetadataEntry[];
26
- correlation?: CorrelationContext;
27
- }>;
28
- export type DirectClientConnectArtifact = Readonly<{
29
- v: 1;
30
- transport: "direct";
31
- direct_info: DirectConnectInfo;
32
- scoped?: readonly ScopeMetadataEntry[];
33
- correlation?: CorrelationContext;
34
- }>;
35
- export type ConnectArtifact = TunnelClientConnectArtifact | DirectClientConnectArtifact;
36
- export declare function hasArtifactOnlyFields(value: Record<string, unknown>): boolean;
37
- export declare function assertConnectArtifact(value: unknown): ConnectArtifact;
@@ -1,217 +0,0 @@
1
- import { assertChannelInitGrant, Role as ControlRole } from "../gen/flowersec/controlplane/v1.gen.js";
2
- import { assertDirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
3
- const SCOPE_NAME_RE = /^[a-z][a-z0-9._-]{0,63}$/;
4
- const TAG_KEY_RE = /^[a-z][a-z0-9._-]{0,31}$/;
5
- const CORRELATION_ID_RE = /^[A-Za-z0-9._~-]{8,128}$/;
6
- const encoder = new TextEncoder();
7
- const TUNNEL_ARTIFACT_KEYS = new Set(["v", "transport", "tunnel_grant", "scoped", "correlation"]);
8
- const DIRECT_ARTIFACT_KEYS = new Set(["v", "transport", "direct_info", "scoped", "correlation"]);
9
- const ARTIFACT_ONLY_KEYS = new Set(["v", "transport", "tunnel_grant", "direct_info", "scoped", "correlation"]);
10
- function isRecord(v) {
11
- return typeof v === "object" && v != null && !Array.isArray(v);
12
- }
13
- function hasOwn(o, key) {
14
- return Object.prototype.hasOwnProperty.call(o, key);
15
- }
16
- function assertNoUnknownFields(kind, obj, allowed) {
17
- for (const key of Object.keys(obj)) {
18
- if (!allowed.has(key))
19
- throw new Error(`bad ${kind}.${key}`);
20
- }
21
- }
22
- function assertPositiveInt(name, value, min, max) {
23
- if (typeof value !== "number" || !Number.isSafeInteger(value))
24
- throw new Error(`bad ${name}`);
25
- if (value < min || value > max)
26
- throw new Error(`bad ${name}`);
27
- return value;
28
- }
29
- function utf8Len(value) {
30
- return encoder.encode(value).length;
31
- }
32
- function normalizedJSONForSize(value) {
33
- return serializeNormalizedJSON(normalizeJSONValue(value));
34
- }
35
- function normalizeJSONValue(value) {
36
- if (value === null)
37
- return null;
38
- switch (typeof value) {
39
- case "string":
40
- case "boolean":
41
- return value;
42
- case "number":
43
- if (!Number.isFinite(value))
44
- throw new Error("bad payload.number");
45
- return value;
46
- case "object": {
47
- if (Array.isArray(value)) {
48
- return value.map((entry) => normalizeJSONValue(entry));
49
- }
50
- if (!isRecord(value))
51
- throw new Error("bad payload.object");
52
- const out = {};
53
- for (const key of Object.keys(value).sort()) {
54
- out[key] = normalizeJSONValue(value[key]);
55
- }
56
- return out;
57
- }
58
- default:
59
- throw new Error("bad payload.value");
60
- }
61
- }
62
- function serializeNormalizedJSON(value) {
63
- return JSON.stringify(value);
64
- }
65
- function maxContainerDepth(value) {
66
- if (Array.isArray(value)) {
67
- let best = 1;
68
- for (const entry of value)
69
- best = Math.max(best, 1 + maxContainerDepth(entry));
70
- return best;
71
- }
72
- if (isRecord(value)) {
73
- let best = 1;
74
- for (const entry of Object.values(value))
75
- best = Math.max(best, 1 + maxContainerDepth(entry));
76
- return best;
77
- }
78
- return 0;
79
- }
80
- function sanitizeCorrelationID(value) {
81
- if (typeof value !== "string")
82
- return undefined;
83
- const trimmed = value.trim();
84
- if (!CORRELATION_ID_RE.test(trimmed))
85
- return undefined;
86
- return trimmed;
87
- }
88
- function assertCorrelationKV(value) {
89
- if (!isRecord(value))
90
- throw new Error("bad CorrelationKV");
91
- assertNoUnknownFields("CorrelationKV", value, new Set(["key", "value"]));
92
- if (typeof value.key !== "string" || !TAG_KEY_RE.test(value.key))
93
- throw new Error("bad CorrelationKV.key");
94
- if (typeof value.value !== "string")
95
- throw new Error("bad CorrelationKV.value");
96
- if (utf8Len(value.key) > 32)
97
- throw new Error("bad CorrelationKV.key");
98
- if (utf8Len(value.value) > 128)
99
- throw new Error("bad CorrelationKV.value");
100
- return Object.freeze({ key: value.key, value: value.value });
101
- }
102
- function assertCorrelationContext(value) {
103
- if (!isRecord(value))
104
- throw new Error("bad CorrelationContext");
105
- assertNoUnknownFields("CorrelationContext", value, new Set(["v", "trace_id", "session_id", "tags"]));
106
- if (value.v !== 1)
107
- throw new Error("bad CorrelationContext.v");
108
- const rawTags = value.tags;
109
- if (rawTags !== undefined && !Array.isArray(rawTags))
110
- throw new Error("bad CorrelationContext.tags");
111
- const tags = (rawTags ?? []).map((entry) => assertCorrelationKV(entry));
112
- const seen = new Set();
113
- for (const tag of tags) {
114
- if (seen.has(tag.key))
115
- throw new Error("bad CorrelationContext.tags");
116
- seen.add(tag.key);
117
- }
118
- if (tags.length > 8)
119
- throw new Error("bad CorrelationContext.tags");
120
- const traceId = sanitizeCorrelationID(value.trace_id);
121
- const sessionId = sanitizeCorrelationID(value.session_id);
122
- return Object.freeze({
123
- v: 1,
124
- ...(traceId === undefined ? {} : { trace_id: traceId }),
125
- ...(sessionId === undefined ? {} : { session_id: sessionId }),
126
- tags: Object.freeze(tags),
127
- });
128
- }
129
- function assertPayloadObject(value) {
130
- if (!isRecord(value))
131
- throw new Error("bad ScopeMetadataEntry.payload");
132
- const normalized = normalizedJSONForSize(value);
133
- if (utf8Len(normalized) > 8192)
134
- throw new Error("bad ScopeMetadataEntry.payload");
135
- if (maxContainerDepth(value) > 8)
136
- throw new Error("bad ScopeMetadataEntry.payload");
137
- return value;
138
- }
139
- function assertScopeMetadataEntry(value) {
140
- if (!isRecord(value))
141
- throw new Error("bad ScopeMetadataEntry");
142
- assertNoUnknownFields("ScopeMetadataEntry", value, new Set(["scope", "scope_version", "critical", "payload"]));
143
- if (typeof value.scope !== "string" || !SCOPE_NAME_RE.test(value.scope))
144
- throw new Error("bad ScopeMetadataEntry.scope");
145
- if (typeof value.critical !== "boolean")
146
- throw new Error("bad ScopeMetadataEntry.critical");
147
- const scopeVersion = assertPositiveInt("ScopeMetadataEntry.scope_version", value.scope_version, 1, 65535);
148
- const payload = assertPayloadObject(value.payload);
149
- return Object.freeze({
150
- scope: value.scope,
151
- scope_version: scopeVersion,
152
- critical: value.critical,
153
- payload,
154
- });
155
- }
156
- function assertScopedEntries(value) {
157
- if (!Array.isArray(value))
158
- throw new Error("bad ConnectArtifact.scoped");
159
- if (value.length > 8)
160
- throw new Error("bad ConnectArtifact.scoped");
161
- const out = value.map((entry) => assertScopeMetadataEntry(entry));
162
- const seen = new Set();
163
- for (const entry of out) {
164
- if (seen.has(entry.scope))
165
- throw new Error("bad ConnectArtifact.scoped");
166
- seen.add(entry.scope);
167
- }
168
- return Object.freeze(out);
169
- }
170
- function assertArtifactObject(value) {
171
- if (!isRecord(value))
172
- throw new Error("bad ConnectArtifact");
173
- return value;
174
- }
175
- function assertArtifactTransport(value) {
176
- if (value !== "tunnel" && value !== "direct")
177
- throw new Error("bad ConnectArtifact.transport");
178
- return value;
179
- }
180
- export function hasArtifactOnlyFields(value) {
181
- return Object.keys(value).some((key) => ARTIFACT_ONLY_KEYS.has(key));
182
- }
183
- export function assertConnectArtifact(value) {
184
- const record = assertArtifactObject(value);
185
- if (record.v !== 1)
186
- throw new Error("bad ConnectArtifact.v");
187
- const transport = assertArtifactTransport(record.transport);
188
- const scoped = record.scoped === undefined ? undefined : assertScopedEntries(record.scoped);
189
- const correlation = record.correlation === undefined ? undefined : assertCorrelationContext(record.correlation);
190
- if (transport === "tunnel") {
191
- assertNoUnknownFields("TunnelClientConnectArtifact", record, TUNNEL_ARTIFACT_KEYS);
192
- if (!hasOwn(record, "tunnel_grant"))
193
- throw new Error("bad TunnelClientConnectArtifact.tunnel_grant");
194
- const tunnelGrant = assertChannelInitGrant(record.tunnel_grant);
195
- if (tunnelGrant.role !== ControlRole.Role_client) {
196
- throw new Error("bad TunnelClientConnectArtifact.tunnel_grant.role");
197
- }
198
- return Object.freeze({
199
- v: 1,
200
- transport,
201
- tunnel_grant: tunnelGrant,
202
- ...(scoped === undefined ? {} : { scoped }),
203
- ...(correlation === undefined ? {} : { correlation }),
204
- });
205
- }
206
- assertNoUnknownFields("DirectClientConnectArtifact", record, DIRECT_ARTIFACT_KEYS);
207
- if (!hasOwn(record, "direct_info"))
208
- throw new Error("bad DirectClientConnectArtifact.direct_info");
209
- const directInfo = assertDirectConnectInfo(record.direct_info);
210
- return Object.freeze({
211
- v: 1,
212
- transport,
213
- direct_info: directInfo,
214
- ...(scoped === undefined ? {} : { scoped }),
215
- ...(correlation === undefined ? {} : { correlation }),
216
- });
217
- }
@@ -1,22 +0,0 @@
1
- import { type ClientObserverLike } from "../observability/observer.js";
2
- import { type CorrelationContext, type ScopeMetadataEntry } from "./artifact.js";
3
- export type ConnectScopeResolver = (entry: ScopeMetadataEntry) => void | Promise<void>;
4
- export type ConnectScopeResolverMap = Readonly<Record<string, ConnectScopeResolver>>;
5
- type NormalizeOptions = Readonly<{
6
- observer?: ClientObserverLike;
7
- scopeResolvers?: ConnectScopeResolverMap;
8
- relaxedOptionalScopeValidation?: boolean;
9
- }>;
10
- export type NormalizedConnectInput = Readonly<{
11
- kind: "tunnel";
12
- input: unknown;
13
- correlation?: CorrelationContext;
14
- observer?: ClientObserverLike;
15
- }> | Readonly<{
16
- kind: "direct";
17
- input: unknown;
18
- correlation?: CorrelationContext;
19
- observer?: ClientObserverLike;
20
- }>;
21
- export declare function normalizeConnectInput(input: unknown, opts?: NormalizeOptions): Promise<NormalizedConnectInput>;
22
- export {};