@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,273 +0,0 @@
1
- import { assertConnectArtifact } from "../connect/artifact.js";
2
- import { SDK_DEFAULTS } from "../defaults.js";
3
- export class ControlplaneRequestError extends Error {
4
- status;
5
- code;
6
- responseBody;
7
- constructor(args) {
8
- super(args.message);
9
- this.name = "ControlplaneRequestError";
10
- this.status = args.status;
11
- this.code = String(args.code ?? "").trim();
12
- this.responseBody = args.responseBody;
13
- }
14
- }
15
- export const DEFAULT_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact";
16
- export const DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH = "/v1/connect/artifact/entry";
17
- const DEFAULT_MAX_CONTROLPLANE_RESPONSE_BYTES = SDK_DEFAULTS.controlplane.maxResponseBodyBytes;
18
- class ControlplaneResponseTooLargeError extends Error {
19
- constructor(maxBytes) {
20
- super(`controlplane response exceeded ${maxBytes} bytes`);
21
- this.name = "ControlplaneResponseTooLargeError";
22
- }
23
- }
24
- function resolveFetch(fetchImpl) {
25
- if (fetchImpl)
26
- return fetchImpl;
27
- if (typeof globalThis.fetch === "function")
28
- return globalThis.fetch.bind(globalThis);
29
- throw new Error("global fetch is not available");
30
- }
31
- export function buildControlplaneURL(baseUrl, path) {
32
- const base = String(baseUrl ?? "").trim();
33
- if (base === "")
34
- return path;
35
- return `${base.replace(/\/+$/, "")}${path}`;
36
- }
37
- function transportPolicyDenied() {
38
- return new ControlplaneRequestError({
39
- status: 0,
40
- code: "transport_policy_denied",
41
- message: "controlplane transport security policy denied URL",
42
- });
43
- }
44
- function validateArtifactURL(rawUrl, allowLoopbackHTTP) {
45
- let target;
46
- try {
47
- if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(rawUrl)) {
48
- target = new URL(rawUrl);
49
- }
50
- else {
51
- const base = globalThis.location?.href;
52
- if (!base)
53
- throw new Error("relative URL requires a browser location");
54
- target = new URL(rawUrl, base);
55
- }
56
- }
57
- catch {
58
- throw transportPolicyDenied();
59
- }
60
- if (target.username !== "" || target.password !== "" || target.hash !== "") {
61
- throw transportPolicyDenied();
62
- }
63
- if (target.protocol === "https:")
64
- return;
65
- if (target.protocol === "http:"
66
- && allowLoopbackHTTP === true
67
- && isLiteralLoopbackHost(sourceHostname(rawUrl, target)))
68
- return;
69
- throw transportPolicyDenied();
70
- }
71
- function sourceHostname(rawUrl, target) {
72
- const authority = /^(?:[A-Za-z][A-Za-z0-9+.-]*:)?\/\/([^/?#]*)/.exec(rawUrl)?.[1];
73
- if (authority === undefined)
74
- return target.hostname;
75
- const hostPort = authority.slice(authority.lastIndexOf("@") + 1);
76
- if (hostPort.startsWith("[")) {
77
- const end = hostPort.indexOf("]");
78
- if (end < 0 || (hostPort.length > end + 1 && !/^:\d+$/.test(hostPort.slice(end + 1)))) {
79
- return hostPort;
80
- }
81
- return hostPort.slice(1, end);
82
- }
83
- const colon = hostPort.lastIndexOf(":");
84
- if (colon < 0)
85
- return hostPort;
86
- if (hostPort.indexOf(":") !== colon || !/^\d+$/.test(hostPort.slice(colon + 1)))
87
- return hostPort;
88
- return hostPort.slice(0, colon);
89
- }
90
- function isLiteralLoopbackHost(rawHost) {
91
- const host = rawHost.startsWith("[") && rawHost.endsWith("]")
92
- ? rawHost.slice(1, -1).toLowerCase()
93
- : rawHost.toLowerCase();
94
- if (host === "localhost" || host === "::1")
95
- return true;
96
- const parts = host.split(".");
97
- if (parts.length !== 4)
98
- return false;
99
- const octets = parts.map((part) => {
100
- if (!/^(0|[1-9]\d{0,2})$/.test(part))
101
- return -1;
102
- const value = Number(part);
103
- return value <= 255 ? value : -1;
104
- });
105
- return octets.every((value) => value >= 0) && octets[0] === 127;
106
- }
107
- function parseMaybeJSON(bodyText) {
108
- const trimmed = String(bodyText ?? "").trim();
109
- if (trimmed === "")
110
- return "";
111
- try {
112
- return JSON.parse(trimmed);
113
- }
114
- catch {
115
- return trimmed;
116
- }
117
- }
118
- function parseContentLength(headerValue) {
119
- const raw = String(headerValue ?? "").trim();
120
- if (raw === "")
121
- return null;
122
- if (!/^[0-9]+$/.test(raw))
123
- return null;
124
- const parsed = Number(raw);
125
- if (!Number.isSafeInteger(parsed) || parsed < 0)
126
- return null;
127
- return parsed;
128
- }
129
- async function readControlplaneText(response, maxBytes) {
130
- const contentLength = parseContentLength(response.headers.get("Content-Length"));
131
- if (contentLength !== null && contentLength > maxBytes) {
132
- throw new ControlplaneResponseTooLargeError(maxBytes);
133
- }
134
- if (!response.body) {
135
- return "";
136
- }
137
- const reader = response.body.getReader();
138
- const decoder = new TextDecoder();
139
- let totalBytes = 0;
140
- let text = "";
141
- while (true) {
142
- const chunk = await reader.read();
143
- if (chunk.done)
144
- break;
145
- totalBytes += chunk.value.byteLength;
146
- if (totalBytes > maxBytes) {
147
- try {
148
- await reader.cancel();
149
- }
150
- catch {
151
- // The size violation is authoritative; cancellation is secondary cleanup.
152
- }
153
- throw new ControlplaneResponseTooLargeError(maxBytes);
154
- }
155
- text += decoder.decode(chunk.value, { stream: true });
156
- }
157
- text += decoder.decode();
158
- return text;
159
- }
160
- function decodeErrorMessage(status, responseBody) {
161
- let message = `controlplane request failed: ${status}`;
162
- let code = "";
163
- if (responseBody && typeof responseBody === "object") {
164
- const error = responseBody.error;
165
- if (error && typeof error === "object") {
166
- const nextMessage = String(error.message ?? "").trim();
167
- if (nextMessage !== "") {
168
- message = nextMessage;
169
- }
170
- code = String(error.code ?? "").trim();
171
- }
172
- }
173
- else if (typeof responseBody === "string" && responseBody !== "") {
174
- message = responseBody;
175
- }
176
- return { code, message };
177
- }
178
- export async function requestControlplaneJSON(url, init) {
179
- const runFetch = resolveFetch(init.fetch);
180
- const response = await runFetch(url, {
181
- ...init,
182
- cache: "no-store",
183
- redirect: "error",
184
- });
185
- let rawBody = "";
186
- try {
187
- rawBody = await readControlplaneText(response, DEFAULT_MAX_CONTROLPLANE_RESPONSE_BYTES);
188
- }
189
- catch (err) {
190
- if (err instanceof ControlplaneResponseTooLargeError && !response.ok) {
191
- throw new ControlplaneRequestError({
192
- status: response.status,
193
- message: err.message,
194
- });
195
- }
196
- throw err;
197
- }
198
- const responseBody = parseMaybeJSON(rawBody);
199
- if (!response.ok) {
200
- const error = decodeErrorMessage(response.status, responseBody);
201
- throw new ControlplaneRequestError({
202
- status: response.status,
203
- message: error.message,
204
- code: error.code,
205
- responseBody,
206
- });
207
- }
208
- if (typeof responseBody === "string") {
209
- if (responseBody === "") {
210
- throw new Error("Invalid controlplane response: expected JSON body");
211
- }
212
- throw new Error("Invalid controlplane response: expected JSON body");
213
- }
214
- return responseBody;
215
- }
216
- function buildConnectArtifactPayload(config) {
217
- const body = {
218
- endpoint_id: String(config.endpointId ?? "").trim(),
219
- };
220
- if (body.endpoint_id === "")
221
- throw new Error("endpointId is required");
222
- if (config.payload !== undefined)
223
- body.payload = { ...config.payload };
224
- const traceId = String(config.correlation?.traceId ?? "").trim();
225
- if (traceId !== "") {
226
- body.correlation = { trace_id: traceId };
227
- }
228
- return body;
229
- }
230
- function createJSONHeaders(input) {
231
- const headers = new Headers(input);
232
- if (!headers.has("Content-Type")) {
233
- headers.set("Content-Type", "application/json");
234
- }
235
- return headers;
236
- }
237
- export async function requestConnectArtifact(config) {
238
- const url = buildControlplaneURL(config.baseUrl, config.path ?? DEFAULT_CONNECT_ARTIFACT_PATH);
239
- validateArtifactURL(url, config.allowLoopbackHTTP);
240
- const data = (await requestControlplaneJSON(url, {
241
- ...(config.fetch === undefined ? {} : { fetch: config.fetch }),
242
- method: "POST",
243
- credentials: config.credentials ?? "omit",
244
- headers: createJSONHeaders(config.headers),
245
- body: JSON.stringify(buildConnectArtifactPayload(config)),
246
- ...(config.signal === undefined ? {} : { signal: config.signal }),
247
- }));
248
- if (!data?.connect_artifact) {
249
- throw new Error("Invalid controlplane response: missing `connect_artifact`");
250
- }
251
- return assertConnectArtifact(data.connect_artifact);
252
- }
253
- export async function requestEntryConnectArtifact(config) {
254
- const entryTicket = String(config.entryTicket ?? "").trim();
255
- if (entryTicket === "")
256
- throw new Error("entryTicket is required");
257
- const headers = createJSONHeaders(config.headers);
258
- headers.set("Authorization", `Bearer ${entryTicket}`);
259
- const url = buildControlplaneURL(config.baseUrl, config.path ?? DEFAULT_ENTRY_CONNECT_ARTIFACT_PATH);
260
- validateArtifactURL(url, config.allowLoopbackHTTP);
261
- const data = (await requestControlplaneJSON(url, {
262
- ...(config.fetch === undefined ? {} : { fetch: config.fetch }),
263
- method: "POST",
264
- credentials: config.credentials ?? "omit",
265
- headers,
266
- body: JSON.stringify(buildConnectArtifactPayload(config)),
267
- ...(config.signal === undefined ? {} : { signal: config.signal }),
268
- }));
269
- if (!data?.connect_artifact) {
270
- throw new Error("Invalid controlplane response: missing `connect_artifact`");
271
- }
272
- return assertConnectArtifact(data.connect_artifact);
273
- }
@@ -1,33 +0,0 @@
1
- export declare const TOKEN_PREFIX = "FST2";
2
- export type TokenPayload = Readonly<{
3
- kid: string;
4
- aud: string;
5
- iss?: string;
6
- channel_id: string;
7
- role: 1 | 2;
8
- token_id: string;
9
- init_exp: number;
10
- idle_timeout_seconds: number;
11
- iat: number;
12
- exp: number;
13
- }>;
14
- export type ParsedToken = Readonly<{
15
- payload: TokenPayload;
16
- signed: Uint8Array;
17
- signature: Uint8Array;
18
- }>;
19
- export type TokenVerifyOptions = Readonly<{
20
- nowUnixS?: number;
21
- audience?: string;
22
- issuer?: string;
23
- clockSkewMs?: number;
24
- }>;
25
- export type TokenKeyLookup = ReadonlyMap<string, Uint8Array> | ((kid: string) => Uint8Array | undefined);
26
- export declare class TokenError extends Error {
27
- readonly code: string;
28
- constructor(code: string, message?: string);
29
- }
30
- export declare function signToken(signingSeed: Uint8Array, payload: TokenPayload): string;
31
- export declare function parseToken(token: string): ParsedToken;
32
- export declare function verifyToken(token: string, keys: TokenKeyLookup, options?: TokenVerifyOptions): TokenPayload;
33
- export declare function equalSignedTokenPart(left: string, right: string): boolean;
@@ -1,119 +0,0 @@
1
- import { ed25519 } from "@noble/curves/ed25519";
2
- import { base64urlDecode, base64urlEncode } from "../utils/base64url.js";
3
- export const TOKEN_PREFIX = "FST2";
4
- export class TokenError extends Error {
5
- code;
6
- constructor(code, message = code) {
7
- super(message);
8
- this.code = code;
9
- this.name = "TokenError";
10
- }
11
- }
12
- export function signToken(signingSeed, payload) {
13
- if (signingSeed.length !== 32)
14
- throw new TokenError("invalid_signing_key");
15
- const normalized = normalizePayload(payload, true);
16
- const payloadJSON = new TextEncoder().encode(JSON.stringify(normalized));
17
- const signedText = `${TOKEN_PREFIX}.${base64urlEncode(payloadJSON)}`;
18
- const signature = ed25519.sign(new TextEncoder().encode(signedText), signingSeed);
19
- return `${signedText}.${base64urlEncode(signature)}`;
20
- }
21
- export function parseToken(token) {
22
- const parts = token.split(".");
23
- if (parts.length !== 3 || parts[0] !== TOKEN_PREFIX)
24
- throw new TokenError("invalid_format");
25
- try {
26
- const payload = JSON.parse(new TextDecoder().decode(base64urlDecode(parts[1])));
27
- return {
28
- payload,
29
- signed: new TextEncoder().encode(`${TOKEN_PREFIX}.${parts[1]}`),
30
- signature: base64urlDecode(parts[2]),
31
- };
32
- }
33
- catch (error) {
34
- if (error instanceof SyntaxError)
35
- throw new TokenError("invalid_json");
36
- throw new TokenError("invalid_base64url");
37
- }
38
- }
39
- export function verifyToken(token, keys, options = {}) {
40
- const parsed = parseToken(token);
41
- const payload = normalizePayload(parsed.payload, false);
42
- const publicKey = typeof keys === "function" ? keys(payload.kid) : keys.get(payload.kid);
43
- if (publicKey == null)
44
- throw new TokenError("unknown_kid");
45
- if (publicKey.length !== 32 || parsed.signature.length !== 64 || !ed25519.verify(parsed.signature, parsed.signed, publicKey)) {
46
- throw new TokenError("invalid_signature");
47
- }
48
- if (options.audience !== undefined && !constantTimeTextEqual(payload.aud, options.audience))
49
- throw new TokenError("invalid_audience");
50
- if (options.issuer !== undefined && !constantTimeTextEqual(payload.iss ?? "", options.issuer))
51
- throw new TokenError("invalid_issuer");
52
- const skewMs = options.clockSkewMs ?? 0;
53
- if (!Number.isFinite(skewMs) || skewMs < 0)
54
- throw new TokenError("invalid_clock_skew");
55
- const skewSeconds = Math.ceil(skewMs / 1000);
56
- const now = options.nowUnixS ?? Math.floor(Date.now() / 1000);
57
- if (!Number.isSafeInteger(now))
58
- throw new TokenError("invalid_time");
59
- if (payload.iat > now + skewSeconds)
60
- throw new TokenError("iat_in_future");
61
- if (payload.init_exp < now - skewSeconds)
62
- throw new TokenError("init_expired");
63
- if (payload.exp < now - skewSeconds)
64
- throw new TokenError("expired");
65
- return payload;
66
- }
67
- export function equalSignedTokenPart(left, right) {
68
- const leftPart = left.lastIndexOf(".");
69
- const rightPart = right.lastIndexOf(".");
70
- if (leftPart <= 0 || rightPart <= 0)
71
- return false;
72
- return constantTimeTextEqual(left.slice(0, leftPart), right.slice(0, rightPart));
73
- }
74
- function normalizePayload(input, signing) {
75
- const kid = String(input?.kid ?? "").trim();
76
- const aud = String(input?.aud ?? "").trim();
77
- const iss = String(input?.iss ?? "").trim();
78
- const channelId = String(input?.channel_id ?? "").trim();
79
- const tokenId = String(input?.token_id ?? "").trim();
80
- if (kid === "" || tokenId === "" || (signing && aud === ""))
81
- throw new TokenError("invalid_format");
82
- if (channelId === "" || new TextEncoder().encode(channelId).length > 256)
83
- throw new TokenError("invalid_format");
84
- if (input.role !== 1 && input.role !== 2)
85
- throw new TokenError("invalid_format");
86
- for (const [name, value] of [["init_exp", input.init_exp], ["iat", input.iat], ["exp", input.exp], ["idle_timeout_seconds", input.idle_timeout_seconds]]) {
87
- if (!Number.isSafeInteger(value))
88
- throw new TokenError("invalid_format", `invalid ${name}`);
89
- }
90
- if (input.idle_timeout_seconds <= 0)
91
- throw new TokenError("invalid_idle_timeout");
92
- if (signing && (input.init_exp <= 0 || input.iat <= 0 || input.exp <= 0))
93
- throw new TokenError("invalid_format");
94
- if (input.exp > input.init_exp)
95
- throw new TokenError("exp_after_init");
96
- if (input.iat > input.exp)
97
- throw new TokenError("invalid_format");
98
- return {
99
- kid,
100
- aud,
101
- ...(iss === "" ? {} : { iss }),
102
- channel_id: channelId,
103
- role: input.role,
104
- token_id: tokenId,
105
- init_exp: input.init_exp,
106
- idle_timeout_seconds: input.idle_timeout_seconds,
107
- iat: input.iat,
108
- exp: input.exp,
109
- };
110
- }
111
- function constantTimeTextEqual(left, right) {
112
- const a = new TextEncoder().encode(left);
113
- const b = new TextEncoder().encode(right);
114
- let diff = a.length ^ b.length;
115
- const length = Math.max(a.length, b.length);
116
- for (let index = 0; index < length; index++)
117
- diff |= (a[index] ?? 0) ^ (b[index] ?? 0);
118
- return diff === 0;
119
- }
@@ -1,9 +0,0 @@
1
- import { type ConnectOptionsBase } from "../client-connect/connectCore.js";
2
- import type { ClientInternal } from "../client.js";
3
- export type DirectConnectOptions = ConnectOptionsBase & Readonly<{
4
- /** Type-only marker to prevent mixing direct and tunnel option types. */
5
- __mode?: "direct";
6
- /** Reserved for tunnel connects; forbidden for direct connects. */
7
- endpointInstanceId?: never;
8
- }>;
9
- export declare function connectDirect(info: unknown, opts: DirectConnectOptions): Promise<ClientInternal>;
@@ -1,90 +0,0 @@
1
- import { assertDirectConnectInfo } from "../gen/flowersec/direct/v1.gen.js";
2
- import { FlowersecError } from "../utils/errors.js";
3
- import { connectCore } from "../client-connect/connectCore.js";
4
- import { assertDirectConnectContract, assertValidPSK, prepareChannelId } from "../client-connect/contract.js";
5
- function isRecord(v) {
6
- return typeof v === "object" && v != null && !Array.isArray(v);
7
- }
8
- function hasOwn(o, key) {
9
- return Object.prototype.hasOwnProperty.call(o, key);
10
- }
11
- // connectDirect connects to a direct websocket endpoint and returns an RPC-ready session.
12
- export async function connectDirect(info, opts) {
13
- const endpointInstanceId = opts?.endpointInstanceId;
14
- if (endpointInstanceId != null) {
15
- throw new FlowersecError({
16
- stage: "validate",
17
- code: "invalid_option",
18
- path: "direct",
19
- message: "endpointInstanceId is only valid for tunnel connects",
20
- });
21
- }
22
- if (info == null) {
23
- throw new FlowersecError({ stage: "validate", code: "missing_connect_info", path: "direct", message: "missing connect info" });
24
- }
25
- if (isRecord(info)) {
26
- // Align missing/invalid field codes with Go: missing fields map to specific stable codes.
27
- const okTypes = (!hasOwn(info, "ws_url") || typeof info["ws_url"] === "string") &&
28
- (!hasOwn(info, "channel_id") || typeof info["channel_id"] === "string") &&
29
- (!hasOwn(info, "channel_init_expire_at_unix_s") ||
30
- (typeof info["channel_init_expire_at_unix_s"] === "number" && Number.isSafeInteger(info["channel_init_expire_at_unix_s"]))) &&
31
- (!hasOwn(info, "e2ee_psk_b64u") || typeof info["e2ee_psk_b64u"] === "string") &&
32
- (!hasOwn(info, "default_suite") || (typeof info["default_suite"] === "number" && Number.isSafeInteger(info["default_suite"])));
33
- if (okTypes) {
34
- if (!hasOwn(info, "ws_url")) {
35
- throw new FlowersecError({ stage: "validate", code: "missing_ws_url", path: "direct", message: "missing ws_url" });
36
- }
37
- if (!hasOwn(info, "channel_id")) {
38
- throw new FlowersecError({ stage: "validate", code: "missing_channel_id", path: "direct", message: "missing channel_id" });
39
- }
40
- if (!hasOwn(info, "channel_init_expire_at_unix_s")) {
41
- throw new FlowersecError({
42
- stage: "validate",
43
- code: "missing_init_exp",
44
- path: "direct",
45
- message: "missing channel_init_expire_at_unix_s",
46
- });
47
- }
48
- if (!hasOwn(info, "e2ee_psk_b64u")) {
49
- throw new FlowersecError({ stage: "validate", code: "invalid_psk", path: "direct", message: "missing e2ee_psk_b64u" });
50
- }
51
- if (!hasOwn(info, "default_suite")) {
52
- throw new FlowersecError({ stage: "validate", code: "invalid_suite", path: "direct", message: "missing default_suite" });
53
- }
54
- }
55
- const suite = info["default_suite"];
56
- // Keep "invalid_suite" as the stable error code even when the IDL validator rejects the enum value.
57
- if (typeof suite === "number" && Number.isSafeInteger(suite) && suite !== 1 && suite !== 2) {
58
- throw new FlowersecError({ stage: "validate", code: "invalid_suite", path: "direct", message: "invalid suite" });
59
- }
60
- }
61
- let ready;
62
- try {
63
- ready = assertDirectConnectInfo(info);
64
- }
65
- catch (e) {
66
- throw new FlowersecError({ stage: "validate", code: "invalid_input", path: "direct", message: "invalid DirectConnectInfo", cause: e });
67
- }
68
- if (ready.ws_url === "") {
69
- throw new FlowersecError({ stage: "validate", code: "missing_ws_url", path: "direct", message: "missing ws_url" });
70
- }
71
- const channelId = prepareChannelId(ready.channel_id, "direct");
72
- if (ready.channel_init_expire_at_unix_s <= 0) {
73
- throw new FlowersecError({
74
- stage: "validate",
75
- code: "missing_init_exp",
76
- path: "direct",
77
- message: "missing channel_init_expire_at_unix_s",
78
- });
79
- }
80
- assertDirectConnectContract(ready);
81
- assertValidPSK(ready.e2ee_psk_b64u, "direct");
82
- return await connectCore({
83
- path: "direct",
84
- wsUrl: ready.ws_url,
85
- channelId,
86
- e2eePskB64u: ready.e2ee_psk_b64u,
87
- defaultSuite: ready.default_suite,
88
- opts,
89
- });
90
- }
@@ -1 +0,0 @@
1
- export * from "./connect.js";
@@ -1 +0,0 @@
1
- export * from "./connect.js";
@@ -1,9 +0,0 @@
1
- export declare const HANDSHAKE_MAGIC = "FSEH";
2
- export declare const RECORD_MAGIC = "FSEC";
3
- export declare const PROTOCOL_VERSION: 1;
4
- export declare const HANDSHAKE_TYPE_INIT: 1;
5
- export declare const HANDSHAKE_TYPE_RESP: 2;
6
- export declare const HANDSHAKE_TYPE_ACK: 3;
7
- export declare const RECORD_FLAG_APP: 0;
8
- export declare const RECORD_FLAG_PING: 1;
9
- export declare const RECORD_FLAG_REKEY: 2;
@@ -1,18 +0,0 @@
1
- // Handshake header prefix.
2
- export const HANDSHAKE_MAGIC = "FSEH";
3
- // Record header prefix.
4
- export const RECORD_MAGIC = "FSEC";
5
- // Wire-format version for E2EE framing.
6
- export const PROTOCOL_VERSION = 1;
7
- // Client->server init.
8
- export const HANDSHAKE_TYPE_INIT = 1;
9
- // Server response with ephemeral key and nonce.
10
- export const HANDSHAKE_TYPE_RESP = 2;
11
- // Client ack with auth tag.
12
- export const HANDSHAKE_TYPE_ACK = 3;
13
- // Application payload record.
14
- export const RECORD_FLAG_APP = 0;
15
- // Keepalive record.
16
- export const RECORD_FLAG_PING = 1;
17
- // Rekey record.
18
- export const RECORD_FLAG_REKEY = 2;
@@ -1,5 +0,0 @@
1
- export type E2EEHandshakeErrorCode = "auth_tag_mismatch" | "invalid_suite" | "invalid_version" | "timestamp_after_init_exp" | "timestamp_out_of_skew";
2
- export declare class E2EEHandshakeError extends Error {
3
- readonly code: E2EEHandshakeErrorCode;
4
- constructor(code: E2EEHandshakeErrorCode, message: string);
5
- }
@@ -1,8 +0,0 @@
1
- export class E2EEHandshakeError extends Error {
2
- code;
3
- constructor(code, message) {
4
- super(message);
5
- this.name = "E2EEHandshakeError";
6
- this.code = code;
7
- }
8
- }
@@ -1,12 +0,0 @@
1
- export declare const HANDSHAKE_HEADER_LEN: number;
2
- export declare const RECORD_HEADER_LEN: number;
3
- export declare class FramingError extends Error {
4
- }
5
- export declare function encodeHandshakeFrame(handshakeType: number, payloadJsonUtf8: Uint8Array): Uint8Array;
6
- export declare function decodeHandshakeFrame(frame: Uint8Array, maxPayloadBytes: number): {
7
- handshakeType: number;
8
- payloadJsonUtf8: Uint8Array;
9
- };
10
- export declare function looksLikeRecordFrame(frame: Uint8Array, maxCiphertextBytes: number): boolean;
11
- export declare function encodeU64beBigint(n: bigint): Uint8Array;
12
- export declare function decodeU64beBigint(buf: Uint8Array, off: number): bigint;
@@ -1,57 +0,0 @@
1
- import { concatBytes, readU32be, readU64be, u32be, u64be } from "../utils/bin.js";
2
- import { HANDSHAKE_MAGIC, PROTOCOL_VERSION, RECORD_MAGIC } from "./constants.js";
3
- const te = new TextEncoder();
4
- const td = new TextDecoder();
5
- // Handshake header byte size: magic + version + type + length.
6
- export const HANDSHAKE_HEADER_LEN = 4 + 1 + 1 + 4;
7
- // Record header byte size: magic + version + flags + seq + length.
8
- export const RECORD_HEADER_LEN = 4 + 1 + 1 + 8 + 4;
9
- // FramingError marks malformed or truncated frames.
10
- export class FramingError extends Error {
11
- }
12
- // encodeHandshakeFrame builds the handshake header plus JSON payload.
13
- export function encodeHandshakeFrame(handshakeType, payloadJsonUtf8) {
14
- const header = new Uint8Array(HANDSHAKE_HEADER_LEN);
15
- header.set(te.encode(HANDSHAKE_MAGIC), 0);
16
- header[4] = PROTOCOL_VERSION;
17
- header[5] = handshakeType & 0xff;
18
- header.set(u32be(payloadJsonUtf8.length), 6);
19
- return concatBytes([header, payloadJsonUtf8]);
20
- }
21
- // decodeHandshakeFrame validates the handshake header and extracts the payload.
22
- export function decodeHandshakeFrame(frame, maxPayloadBytes) {
23
- if (frame.length < HANDSHAKE_HEADER_LEN)
24
- throw new FramingError("handshake frame too short");
25
- if (td.decode(frame.slice(0, 4)) !== HANDSHAKE_MAGIC)
26
- throw new FramingError("bad handshake magic");
27
- if (frame[4] !== PROTOCOL_VERSION)
28
- throw new FramingError("bad handshake version");
29
- const handshakeType = frame[5];
30
- const n = readU32be(frame, 6);
31
- if (maxPayloadBytes > 0 && n > maxPayloadBytes)
32
- throw new FramingError("handshake payload too large");
33
- if (HANDSHAKE_HEADER_LEN + n !== frame.length)
34
- throw new FramingError("handshake length mismatch");
35
- return { handshakeType, payloadJsonUtf8: frame.slice(HANDSHAKE_HEADER_LEN) };
36
- }
37
- // looksLikeRecordFrame checks whether a frame matches the record header shape.
38
- export function looksLikeRecordFrame(frame, maxCiphertextBytes) {
39
- if (frame.length < RECORD_HEADER_LEN)
40
- return false;
41
- if (td.decode(frame.slice(0, 4)) !== RECORD_MAGIC)
42
- return false;
43
- if (frame[4] !== PROTOCOL_VERSION)
44
- return false;
45
- const n = readU32be(frame, 14);
46
- if (maxCiphertextBytes > 0 && n > maxCiphertextBytes)
47
- return false;
48
- return RECORD_HEADER_LEN + n === frame.length;
49
- }
50
- // encodeU64beBigint converts a bigint into an 8-byte big-endian buffer.
51
- export function encodeU64beBigint(n) {
52
- return u64be(n);
53
- }
54
- // decodeU64beBigint reads a bigint from an 8-byte big-endian buffer.
55
- export function decodeU64beBigint(buf, off) {
56
- return readU64be(buf, off);
57
- }