@floegence/flowersec-core 0.27.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 (268) 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 -27
  34. package/dist/facade.js +6 -24
  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 -81
  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 -13
  148. package/dist/browser/connect.js +0 -31
  149. package/dist/browser/reconnectConfig.d.ts +0 -15
  150. package/dist/browser/reconnectConfig.js +0 -20
  151. package/dist/client-connect/common.d.ts +0 -26
  152. package/dist/client-connect/common.js +0 -167
  153. package/dist/client-connect/connectCore.d.ts +0 -61
  154. package/dist/client-connect/connectCore.js +0 -572
  155. package/dist/client-connect/contract.d.ts +0 -8
  156. package/dist/client-connect/contract.js +0 -51
  157. package/dist/client-connect/termination.d.ts +0 -6
  158. package/dist/client-connect/termination.js +0 -7
  159. package/dist/client-connect/transportSecurity.d.ts +0 -27
  160. package/dist/client-connect/transportSecurity.js +0 -199
  161. package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
  162. package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
  163. package/dist/client.d.ts +0 -23
  164. package/dist/connect/artifact.d.ts +0 -36
  165. package/dist/connect/artifact.js +0 -213
  166. package/dist/connect/resolveArtifact.d.ts +0 -22
  167. package/dist/connect/resolveArtifact.js +0 -87
  168. package/dist/controlplane/channelInit.d.ts +0 -27
  169. package/dist/controlplane/channelInit.js +0 -116
  170. package/dist/controlplane/http.d.ts +0 -16
  171. package/dist/controlplane/http.js +0 -59
  172. package/dist/controlplane/index.d.ts +0 -6
  173. package/dist/controlplane/index.js +0 -5
  174. package/dist/controlplane/issuer.d.ts +0 -18
  175. package/dist/controlplane/issuer.js +0 -113
  176. package/dist/controlplane/request.d.ts +0 -51
  177. package/dist/controlplane/request.js +0 -273
  178. package/dist/controlplane/token.d.ts +0 -33
  179. package/dist/controlplane/token.js +0 -119
  180. package/dist/direct-client/connect.d.ts +0 -9
  181. package/dist/direct-client/connect.js +0 -90
  182. package/dist/direct-client/index.d.ts +0 -1
  183. package/dist/direct-client/index.js +0 -1
  184. package/dist/e2ee/constants.d.ts +0 -9
  185. package/dist/e2ee/constants.js +0 -18
  186. package/dist/e2ee/errors.d.ts +0 -5
  187. package/dist/e2ee/errors.js +0 -8
  188. package/dist/e2ee/framing.d.ts +0 -12
  189. package/dist/e2ee/framing.js +0 -57
  190. package/dist/e2ee/handshake.d.ts +0 -88
  191. package/dist/e2ee/handshake.js +0 -353
  192. package/dist/e2ee/index.d.ts +0 -7
  193. package/dist/e2ee/index.js +0 -7
  194. package/dist/e2ee/kdf.d.ts +0 -15
  195. package/dist/e2ee/kdf.js +0 -39
  196. package/dist/e2ee/record.d.ts +0 -11
  197. package/dist/e2ee/record.js +0 -80
  198. package/dist/e2ee/secureChannel.d.ts +0 -93
  199. package/dist/e2ee/secureChannel.js +0 -342
  200. package/dist/e2ee/transcript.d.ts +0 -23
  201. package/dist/e2ee/transcript.js +0 -31
  202. package/dist/endpoint/index.d.ts +0 -84
  203. package/dist/endpoint/index.js +0 -557
  204. package/dist/endpoint/node.d.ts +0 -9
  205. package/dist/endpoint/node.js +0 -51
  206. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
  207. package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
  208. package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
  209. package/dist/gen/flowersec/direct/v1.gen.js +0 -101
  210. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
  211. package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
  212. package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
  213. package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
  214. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
  215. package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
  216. package/dist/index.d.ts +0 -22
  217. package/dist/index.js +0 -22
  218. package/dist/node/connect.d.ts +0 -10
  219. package/dist/node/connect.js +0 -29
  220. package/dist/node/reconnectConfig.d.ts +0 -16
  221. package/dist/node/reconnectConfig.js +0 -20
  222. package/dist/observability/index.d.ts +0 -1
  223. package/dist/observability/index.js +0 -1
  224. package/dist/observability/observer.d.ts +0 -52
  225. package/dist/observability/observer.js +0 -314
  226. package/dist/proxy/appWindow.d.ts +0 -37
  227. package/dist/proxy/appWindow.js +0 -273
  228. package/dist/proxy/bootstrap.d.ts +0 -35
  229. package/dist/proxy/bootstrap.js +0 -119
  230. package/dist/proxy/constants.d.ts +0 -7
  231. package/dist/proxy/constants.js +0 -8
  232. package/dist/proxy/controllerWindow.d.ts +0 -12
  233. package/dist/proxy/controllerWindow.js +0 -384
  234. package/dist/proxy/cookieJar.d.ts +0 -7
  235. package/dist/proxy/cookieJar.js +0 -136
  236. package/dist/proxy/headerPolicy.d.ts +0 -14
  237. package/dist/proxy/headerPolicy.js +0 -134
  238. package/dist/proxy/portStream.d.ts +0 -5
  239. package/dist/proxy/portStream.js +0 -256
  240. package/dist/proxy/preset.d.ts +0 -29
  241. package/dist/proxy/preset.js +0 -111
  242. package/dist/proxy/runtimeScope.d.ts +0 -57
  243. package/dist/proxy/runtimeScope.js +0 -218
  244. package/dist/proxy/server.d.ts +0 -28
  245. package/dist/proxy/server.js +0 -833
  246. package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
  247. package/dist/proxy/windowBridgeProtocol.js +0 -11
  248. package/dist/reconnect/artifactControlplane.d.ts +0 -16
  249. package/dist/reconnect/artifactControlplane.js +0 -35
  250. package/dist/reconnect/index.d.ts +0 -36
  251. package/dist/reconnect/index.js +0 -388
  252. package/dist/rpc-proxy/index.d.ts +0 -1
  253. package/dist/rpc-proxy/index.js +0 -1
  254. package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
  255. package/dist/rpc-proxy/rpcProxy.js +0 -1
  256. package/dist/streamhello/index.d.ts +0 -1
  257. package/dist/streamhello/index.js +0 -1
  258. package/dist/streamhello/streamHello.d.ts +0 -3
  259. package/dist/streamhello/streamHello.js +0 -13
  260. package/dist/streamio/index.d.ts +0 -32
  261. package/dist/streamio/index.js +0 -87
  262. package/dist/tunnel-client/connect.d.ts +0 -9
  263. package/dist/tunnel-client/connect.js +0 -147
  264. package/dist/tunnel-client/index.d.ts +0 -1
  265. package/dist/tunnel-client/index.js +0 -1
  266. package/dist/ws/index.d.ts +0 -1
  267. package/dist/ws/index.js +0 -1
  268. /package/dist/{client.js → rpc/wire.js} +0 -0
@@ -1,113 +1,88 @@
1
- import { createByteReader } from "../streamio/index.js";
2
1
  import { readU32be, u16be, u32be } from "../utils/bin.js";
3
- import { DEFAULT_MAX_WS_FRAME_BYTES } from "./constants.js";
4
- function readU16be(buf, off) {
5
- return ((buf[off] << 8) | buf[off + 1]) >>> 0;
6
- }
7
- const te = new TextEncoder();
8
- const td = new TextDecoder();
9
- function defaultPortForProtocol(protocol) {
10
- // Protocol strings include the trailing ':' in URL (e.g. 'http:', 'ws:').
2
+ import { ProxyByteReader, writeAll } from "./stream.js";
3
+ const encoder = new TextEncoder();
4
+ const decoder = new TextDecoder();
5
+ function defaultPort(protocol) {
11
6
  if (protocol === "http:" || protocol === "ws:")
12
7
  return "80";
13
8
  if (protocol === "https:" || protocol === "wss:")
14
9
  return "443";
15
10
  return "";
16
11
  }
17
- class ListenerMap {
18
- map = new Map();
19
- on(type, cb) {
20
- let s = this.map.get(type);
21
- if (!s) {
22
- s = new Set();
23
- this.map.set(type, s);
24
- }
25
- s.add(cb);
26
- }
27
- off(type, cb) {
28
- this.map.get(type)?.delete(cb);
12
+ function readU16(input) {
13
+ return ((input[0] << 8) | input[1]) >>> 0;
14
+ }
15
+ async function writeFrame(stream, opcode, payload, maximum) {
16
+ if (payload.length > maximum)
17
+ throw new Error("WebSocket frame exceeds the proxy limit");
18
+ const header = new Uint8Array(5);
19
+ header[0] = opcode;
20
+ header.set(u32be(payload.length), 1);
21
+ await writeAll(stream, header);
22
+ await writeAll(stream, payload);
23
+ }
24
+ async function readFrame(reader, maximum) {
25
+ const header = await reader.readExactly(5);
26
+ const length = readU32be(header, 1);
27
+ if (length > maximum)
28
+ throw new Error("WebSocket frame exceeds the proxy limit");
29
+ return { opcode: header[0], payload: await reader.readExactly(length) };
30
+ }
31
+ class EventListeners {
32
+ values = new Map();
33
+ add(type, listener) {
34
+ const entries = this.values.get(type) ?? new Set();
35
+ entries.add(listener);
36
+ this.values.set(type, entries);
29
37
  }
30
- emit(type, ev) {
31
- for (const cb of this.map.get(type) ?? []) {
38
+ remove(type, listener) { this.values.get(type)?.delete(listener); }
39
+ dispatch(type, event, target) {
40
+ for (const listener of this.values.get(type) ?? []) {
32
41
  try {
33
- cb.call(null, ev);
42
+ if (typeof listener === "function")
43
+ listener.call(target, event);
44
+ else
45
+ listener.handleEvent(event);
34
46
  }
35
47
  catch {
36
- // Best-effort event fanout.
48
+ // Event listener failures do not corrupt the transport state.
37
49
  }
38
50
  }
39
51
  }
40
52
  }
41
- async function writeWSFrame(stream, op, payload, maxPayload) {
42
- if (maxPayload > 0 && payload.length > maxPayload)
43
- throw new Error("ws payload too large");
44
- const hdr = new Uint8Array(5);
45
- hdr[0] = op & 0xff;
46
- hdr.set(u32be(payload.length), 1);
47
- await stream.write(hdr);
48
- if (payload.length > 0)
49
- await stream.write(payload);
53
+ function limit(name, input, fallback) {
54
+ const value = input ?? fallback;
55
+ if (!Number.isSafeInteger(value) || value <= 0)
56
+ throw new TypeError(`${name} must be a positive safe integer`);
57
+ return value;
50
58
  }
51
- async function readWSFrame(reader, maxPayload) {
52
- const hdr = await reader.readExactly(5);
53
- const op = hdr[0];
54
- const n = readU32be(hdr, 1);
55
- if (maxPayload > 0 && n > maxPayload)
56
- throw new Error("ws payload too large");
57
- const payload = n === 0 ? new Uint8Array() : await reader.readExactly(n);
58
- return { op, payload };
59
- }
60
- export function installWebSocketPatch(opts) {
59
+ export function installWebSocketPatch(options) {
61
60
  const Original = globalThis.WebSocket;
62
- if (Original == null) {
63
- return { uninstall: () => { } };
64
- }
65
- const shouldProxy = opts.shouldProxy ??
66
- ((u) => {
67
- const loc = globalThis.location;
68
- const hostname = typeof loc?.hostname === "string" ? loc.hostname : "";
69
- if (hostname === "")
70
- return false;
71
- const locProto = typeof loc?.protocol === "string" ? loc.protocol : "";
72
- const locPortRaw = typeof loc?.port === "string" ? loc.port : "";
73
- const locPort = locPortRaw !== "" ? locPortRaw : defaultPortForProtocol(locProto);
74
- const uPort = u.port !== "" ? u.port : defaultPortForProtocol(u.protocol);
75
- return u.hostname === hostname && uPort === locPort;
76
- });
77
- const runtime = opts.runtime;
78
- const runtimeMaxWsFrameBytes = typeof runtime.limits?.maxWsFrameBytes === "number" && Number.isFinite(runtime.limits.maxWsFrameBytes)
79
- ? runtime.limits.maxWsFrameBytes
80
- : DEFAULT_MAX_WS_FRAME_BYTES;
81
- const maxWsFrameBytesRaw = opts.maxWsFrameBytes ?? runtimeMaxWsFrameBytes;
82
- if (!Number.isFinite(maxWsFrameBytesRaw))
83
- throw new Error("maxWsFrameBytes must be a finite number");
84
- const maxWsFrameBytesFloor = Math.floor(maxWsFrameBytesRaw);
85
- if (maxWsFrameBytesFloor < 0)
86
- throw new Error("maxWsFrameBytes must be >= 0");
87
- const maxWsFrameBytes = maxWsFrameBytesFloor === 0 ? runtimeMaxWsFrameBytes : maxWsFrameBytesFloor;
88
- const defaultMaxWsBufferedAmountBytes = 4 * (1 << 20);
89
- const runtimeMaxWsBufferedAmountBytesRaw = runtime.limits?.maxWsBufferedAmountBytes;
90
- if (runtimeMaxWsBufferedAmountBytesRaw !== undefined &&
91
- (!Number.isSafeInteger(runtimeMaxWsBufferedAmountBytesRaw) || runtimeMaxWsBufferedAmountBytesRaw < 0)) {
92
- throw new Error("runtime maxWsBufferedAmountBytes must be a non-negative safe integer");
93
- }
94
- const runtimeMaxWsBufferedAmountBytes = runtimeMaxWsBufferedAmountBytesRaw == null || runtimeMaxWsBufferedAmountBytesRaw === 0
95
- ? defaultMaxWsBufferedAmountBytes
96
- : runtimeMaxWsBufferedAmountBytesRaw;
97
- const maxWsBufferedAmountBytesRaw = opts.maxWsBufferedAmountBytes ?? runtimeMaxWsBufferedAmountBytes;
98
- if (!Number.isSafeInteger(maxWsBufferedAmountBytesRaw) || maxWsBufferedAmountBytesRaw < 0) {
99
- throw new Error("maxWsBufferedAmountBytes must be a non-negative safe integer");
100
- }
101
- const maxWsBufferedAmountBytes = maxWsBufferedAmountBytesRaw === 0
102
- ? runtimeMaxWsBufferedAmountBytes
103
- : maxWsBufferedAmountBytesRaw;
104
- class PatchedWebSocket {
61
+ if (Original === undefined)
62
+ return Object.freeze({ uninstall: () => undefined });
63
+ const NativeWebSocket = Original;
64
+ const runtimeLimits = options.runtime.limits;
65
+ const maxFrameBytes = limit("maxWsFrameBytes", options.maxWsFrameBytes, runtimeLimits.maxWsFrameBytes ?? 1024 * 1024);
66
+ const maxBufferedBytes = limit("maxWsBufferedAmountBytes", options.maxWsBufferedAmountBytes, runtimeLimits.maxWsBufferedAmountBytes ?? 4 * 1024 * 1024);
67
+ const shouldProxy = options.shouldProxy ?? ((url) => {
68
+ const location = globalThis.location;
69
+ if (location?.hostname === undefined || location.hostname === "")
70
+ return false;
71
+ const localPort = location.port || defaultPort(location.protocol);
72
+ const remotePort = url.port || defaultPort(url.protocol);
73
+ return url.hostname === location.hostname && remotePort === localPort;
74
+ });
75
+ class ProxyWebSocket {
105
76
  static CONNECTING = 0;
106
77
  static OPEN = 1;
107
78
  static CLOSING = 2;
108
79
  static CLOSED = 3;
80
+ CONNECTING = 0;
81
+ OPEN = 1;
82
+ CLOSING = 2;
83
+ CLOSED = 3;
109
84
  url = "";
110
- readyState = PatchedWebSocket.CONNECTING;
85
+ readyState = ProxyWebSocket.CONNECTING;
111
86
  bufferedAmount = 0;
112
87
  extensions = "";
113
88
  protocol = "";
@@ -116,206 +91,136 @@ export function installWebSocketPatch(opts) {
116
91
  onmessage = null;
117
92
  onerror = null;
118
93
  onclose = null;
119
- listeners = new ListenerMap();
120
- ac = new AbortController();
121
- stream = null;
122
- readLoopPromise = null;
123
- writeChain = Promise.resolve();
124
- constructor(url, protocols) {
125
- const u = new URL(String(url), globalThis.location?.href);
126
- if (!shouldProxy(u)) {
127
- return new Original(String(url), protocols);
128
- }
129
- this.url = u.toString();
130
- void this.init(u, protocols);
131
- }
132
- addEventListener(type, listener) {
133
- this.listeners.on(type, listener);
134
- }
135
- removeEventListener(type, listener) {
136
- this.listeners.off(type, listener);
137
- }
138
- queueWriteFrame(stream, op, payload, bufferedBytes = 0) {
139
- this.writeChain = this.writeChain
140
- .then(async () => {
141
- if (this.readyState === PatchedWebSocket.CLOSED)
142
- return;
143
- const resolved = typeof payload === "function" ? await payload() : payload;
144
- await writeWSFrame(stream, op, resolved, maxWsFrameBytes);
145
- })
146
- .catch((e) => this.fail(e))
147
- .finally(() => {
148
- this.bufferedAmount = Math.max(0, this.bufferedAmount - bufferedBytes);
149
- });
150
- }
151
- reserveBufferedAmount(bytes) {
152
- if (this.bufferedAmount + bytes > maxWsBufferedAmountBytes) {
153
- this.fail(new Error("WebSocket bufferedAmount limit exceeded"));
154
- return false;
155
- }
156
- this.bufferedAmount += bytes;
157
- return true;
94
+ listeners = new EventListeners();
95
+ abort = new AbortController();
96
+ stream;
97
+ writes = Promise.resolve();
98
+ constructor(input, protocols) {
99
+ const url = new URL(String(input), globalThis.location?.href);
100
+ if (!shouldProxy(url))
101
+ return new NativeWebSocket(input, protocols);
102
+ this.url = url.toString();
103
+ void this.connect(url, protocols);
158
104
  }
105
+ addEventListener(type, listener) { this.listeners.add(type, listener); }
106
+ removeEventListener(type, listener) { this.listeners.remove(type, listener); }
107
+ dispatchEvent(event) { this.listeners.dispatch(event.type, event, this); return !event.defaultPrevented; }
159
108
  send(data) {
160
- if (this.readyState !== PatchedWebSocket.OPEN || this.stream == null) {
161
- throw new Error("WebSocket is not open");
162
- }
163
- const s = this.stream;
164
- const sendBytes = (op, byteLength, copyPayload) => {
165
- if (!this.reserveBufferedAmount(byteLength))
166
- return;
167
- try {
168
- this.queueWriteFrame(s, op, copyPayload(), byteLength);
169
- }
170
- catch (error) {
171
- this.bufferedAmount = Math.max(0, this.bufferedAmount - byteLength);
172
- throw error;
173
- }
174
- };
109
+ if (this.readyState !== ProxyWebSocket.OPEN || this.stream === undefined)
110
+ throw new DOMException("WebSocket is not open", "InvalidStateError");
111
+ let opcode;
112
+ let bytes;
113
+ let load;
175
114
  if (typeof data === "string") {
176
- const payload = te.encode(data);
177
- sendBytes(1, payload.byteLength, () => payload);
178
- return;
179
- }
180
- if (data instanceof ArrayBuffer) {
181
- sendBytes(2, data.byteLength, () => new Uint8Array(data).slice());
115
+ const encoded = encoder.encode(data);
116
+ opcode = 1;
117
+ bytes = encoded.length;
118
+ load = async () => encoded;
119
+ }
120
+ else if (typeof Blob !== "undefined" && data instanceof Blob) {
121
+ opcode = 2;
122
+ bytes = data.size;
123
+ load = async () => new Uint8Array(await data.arrayBuffer());
124
+ }
125
+ else if (ArrayBuffer.isView(data)) {
126
+ opcode = 2;
127
+ bytes = data.byteLength;
128
+ load = async () => new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice();
129
+ }
130
+ else {
131
+ const buffer = data;
132
+ opcode = 2;
133
+ bytes = buffer.byteLength;
134
+ load = async () => new Uint8Array(buffer).slice();
135
+ }
136
+ if (bytes > maxFrameBytes || this.bufferedAmount + bytes > maxBufferedBytes) {
137
+ this.fail();
182
138
  return;
183
139
  }
184
- if (ArrayBuffer.isView(data)) {
185
- sendBytes(2, data.byteLength, () => new Uint8Array(data.buffer, data.byteOffset, data.byteLength).slice());
140
+ this.bufferedAmount += bytes;
141
+ const stream = this.stream;
142
+ this.writes = this.writes.then(async () => await writeFrame(stream, opcode, await load(), maxFrameBytes))
143
+ .catch(() => this.fail())
144
+ .finally(() => { this.bufferedAmount = Math.max(0, this.bufferedAmount - bytes); });
145
+ }
146
+ close(code, reason = "") {
147
+ if (this.readyState === ProxyWebSocket.CLOSED || this.readyState === ProxyWebSocket.CLOSING)
186
148
  return;
149
+ if (code !== undefined && (code < 1000 || code > 4999 || code === 1005 || code === 1006 || code === 1015)) {
150
+ throw new DOMException("Invalid WebSocket close code", "InvalidAccessError");
151
+ }
152
+ const reasonBytes = encoder.encode(reason);
153
+ if (reasonBytes.length > 123)
154
+ throw new DOMException("WebSocket close reason is too long", "SyntaxError");
155
+ this.readyState = ProxyWebSocket.CLOSING;
156
+ const payload = code === undefined ? new Uint8Array() : new Uint8Array([...u16be(code), ...reasonBytes]);
157
+ this.writes = this.writes.then(async () => {
158
+ if (this.stream !== undefined)
159
+ await writeFrame(this.stream, 8, payload, maxFrameBytes);
160
+ }).catch(() => undefined).finally(() => this.abort.abort());
161
+ }
162
+ async connect(url, protocols) {
163
+ try {
164
+ const opened = await options.runtime.openWebSocketStream(url.pathname + url.search, {
165
+ protocols: typeof protocols === "string" ? [protocols] : protocols ?? [],
166
+ signal: this.abort.signal,
167
+ });
168
+ this.stream = opened.stream;
169
+ this.protocol = opened.protocol;
170
+ this.readyState = ProxyWebSocket.OPEN;
171
+ this.emit("open", new Event("open"));
172
+ await this.readLoop(opened.stream);
187
173
  }
188
- if (typeof Blob !== "undefined" && data instanceof Blob) {
189
- if (!this.reserveBufferedAmount(data.size))
190
- return;
191
- this.queueWriteFrame(s, 2, async () => new Uint8Array(await data.arrayBuffer()), data.size);
192
- return;
174
+ catch {
175
+ this.fail();
193
176
  }
194
- throw new Error("unsupported WebSocket send payload");
195
177
  }
196
- close(code, reason) {
197
- if (this.readyState === PatchedWebSocket.CLOSED)
198
- return;
199
- this.readyState = PatchedWebSocket.CLOSING;
200
- const payloadParts = [];
201
- if (code != null)
202
- payloadParts.push(u16be(code));
203
- if (reason != null && reason !== "")
204
- payloadParts.push(te.encode(reason));
205
- const payload = payloadParts.length === 0 ? new Uint8Array() : payloadParts.reduce((a, b) => {
206
- const out = new Uint8Array(a.length + b.length);
207
- out.set(a, 0);
208
- out.set(b, a.length);
209
- return out;
210
- });
211
- this.writeChain = this.writeChain
212
- .then(() => (this.stream ? writeWSFrame(this.stream, 8, payload, maxWsFrameBytes) : undefined))
213
- .catch(() => undefined)
214
- .finally(() => {
215
- try {
216
- this.ac.abort("closed");
178
+ async readLoop(stream) {
179
+ const reader = new ProxyByteReader(stream, { signal: this.abort.signal });
180
+ while (this.readyState !== ProxyWebSocket.CLOSED) {
181
+ const frame = await readFrame(reader, maxFrameBytes);
182
+ if (frame.opcode === 9) {
183
+ this.writes = this.writes.then(async () => await writeFrame(stream, 10, frame.payload, maxFrameBytes)).catch(() => this.fail());
217
184
  }
218
- catch {
219
- // Best-effort abort.
185
+ else if (frame.opcode === 8) {
186
+ const code = frame.payload.length >= 2 ? readU16(frame.payload) : 1000;
187
+ const reason = frame.payload.length > 2 ? decoder.decode(frame.payload.subarray(2)) : "";
188
+ this.readyState = ProxyWebSocket.CLOSED;
189
+ this.emit("close", new CloseEvent("close", { code, reason, wasClean: true }));
190
+ return;
220
191
  }
221
- });
222
- }
223
- emit(type, ev) {
224
- const prop = this["on" + type];
225
- if (typeof prop === "function") {
226
- try {
227
- prop.call(this, ev);
192
+ else if (frame.opcode === 1) {
193
+ this.emit("message", new MessageEvent("message", { data: decoder.decode(frame.payload) }));
228
194
  }
229
- catch {
230
- // Best-effort.
195
+ else if (frame.opcode === 2) {
196
+ const data = this.binaryType === "arraybuffer"
197
+ ? frame.payload.slice().buffer
198
+ : new Blob([frame.payload.slice()]);
199
+ this.emit("message", new MessageEvent("message", { data }));
231
200
  }
232
201
  }
233
- this.listeners.emit(type, ev);
234
202
  }
235
- async init(u, protocols) {
203
+ emit(type, event) {
204
+ const callback = this[`on${type}`];
236
205
  try {
237
- const list = typeof protocols === "string"
238
- ? [protocols]
239
- : Array.isArray(protocols)
240
- ? protocols
241
- : [];
242
- const { stream, protocol } = await runtime.openWebSocketStream(u.pathname + u.search, {
243
- protocols: list,
244
- signal: this.ac.signal
245
- });
246
- this.stream = stream;
247
- this.protocol = protocol;
248
- this.readyState = PatchedWebSocket.OPEN;
249
- this.emit("open", { type: "open" });
250
- this.readLoopPromise = this.readLoop(stream, this.ac.signal);
251
- }
252
- catch (e) {
253
- this.fail(e);
206
+ callback?.call(this, event);
254
207
  }
208
+ catch { /* User event handlers are isolated. */ }
209
+ this.listeners.dispatch(type, event, this);
255
210
  }
256
- async readLoop(stream, signal) {
257
- const reader = createByteReader(stream, { signal });
258
- try {
259
- while (true) {
260
- const { op, payload } = await readWSFrame(reader, maxWsFrameBytes);
261
- if (op === 9) {
262
- // Ping -> Pong (not exposed to WebSocket JS API).
263
- // Must be serialized with user writes, otherwise concurrent writes can corrupt framing.
264
- this.queueWriteFrame(stream, 10, payload);
265
- continue;
266
- }
267
- if (op === 10)
268
- continue;
269
- if (op === 8) {
270
- this.readyState = PatchedWebSocket.CLOSED;
271
- const code = payload.length >= 2 ? readU16be(payload, 0) : 1000;
272
- const reason = payload.length > 2 ? td.decode(payload.subarray(2)) : "";
273
- this.emit("close", { type: "close", code, reason, wasClean: true });
274
- return;
275
- }
276
- if (op === 1) {
277
- this.emit("message", { type: "message", data: td.decode(payload) });
278
- continue;
279
- }
280
- if (op === 2) {
281
- if (this.binaryType === "arraybuffer") {
282
- const ab = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
283
- this.emit("message", { type: "message", data: ab });
284
- continue;
285
- }
286
- if (typeof Blob !== "undefined") {
287
- this.emit("message", { type: "message", data: new Blob([new Uint8Array(payload)]) });
288
- continue;
289
- }
290
- // Fallback for non-browser contexts.
291
- const ab = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
292
- this.emit("message", { type: "message", data: ab });
293
- continue;
294
- }
295
- }
296
- }
297
- catch (e) {
298
- if (this.readyState !== PatchedWebSocket.CLOSED)
299
- this.fail(e);
300
- }
301
- }
302
- fail(e) {
303
- if (this.readyState === PatchedWebSocket.CLOSED)
211
+ fail() {
212
+ if (this.readyState === ProxyWebSocket.CLOSED)
304
213
  return;
305
- this.readyState = PatchedWebSocket.CLOSED;
306
- const msg = e instanceof Error ? e.message : String(e);
214
+ this.readyState = ProxyWebSocket.CLOSED;
307
215
  this.bufferedAmount = 0;
308
- this.emit("error", { type: "error", message: msg });
309
- this.emit("close", { type: "close", code: 1006, reason: msg, wasClean: false });
310
- this.stream = null;
311
- try {
312
- this.ac.abort(msg);
313
- }
314
- catch {
315
- // Best-effort.
316
- }
216
+ this.emit("error", new Event("error"));
217
+ this.emit("close", new CloseEvent("close", { code: 1006, reason: "proxy WebSocket failed", wasClean: false }));
218
+ this.stream = undefined;
219
+ this.abort.abort();
317
220
  }
318
221
  }
319
- globalThis.WebSocket = PatchedWebSocket;
320
- return { uninstall: () => (globalThis.WebSocket = Original) };
222
+ globalThis.WebSocket = ProxyWebSocket;
223
+ return Object.freeze({
224
+ uninstall: () => { globalThis.WebSocket = NativeWebSocket; },
225
+ });
321
226
  }
@@ -0,0 +1,11 @@
1
+ export declare class Artifact {
2
+ private readonly artifactBrand;
3
+ private constructor();
4
+ }
5
+ export type ArtifactErrorCode = "artifact_too_large" | "invalid_artifact" | "invalid_candidate";
6
+ /** A stable, redacted failure returned while parsing an opaque artifact. */
7
+ export declare class ArtifactError extends Error {
8
+ readonly code: ArtifactErrorCode;
9
+ constructor(code: ArtifactErrorCode);
10
+ }
11
+ export declare function parseArtifact(input: string | Uint8Array): Artifact;
@@ -0,0 +1,44 @@
1
+ import { ArtifactV2Error, decodeArtifactV2JSON } from "../v2/artifact.js";
2
+ const artifactValues = new WeakMap();
3
+ export class Artifact {
4
+ constructor() { }
5
+ }
6
+ /** A stable, redacted failure returned while parsing an opaque artifact. */
7
+ export class ArtifactError extends Error {
8
+ code;
9
+ constructor(code) {
10
+ super(`Flowersec artifact parsing failed (code=${code})`);
11
+ this.code = code;
12
+ this.name = "ArtifactError";
13
+ }
14
+ }
15
+ export function parseArtifact(input) {
16
+ try {
17
+ return wrapArtifact(decodeArtifactV2JSON(input));
18
+ }
19
+ catch (error) {
20
+ if (error instanceof ArtifactV2Error) {
21
+ switch (error.code) {
22
+ case "artifact_too_large":
23
+ case "invalid_candidate":
24
+ throw new ArtifactError(error.code);
25
+ default:
26
+ throw new ArtifactError("invalid_artifact");
27
+ }
28
+ }
29
+ throw new ArtifactError("invalid_artifact");
30
+ }
31
+ }
32
+ /** @internal */
33
+ export function wrapArtifact(value) {
34
+ const artifact = new Artifact();
35
+ artifactValues.set(artifact, value);
36
+ return Object.freeze(artifact);
37
+ }
38
+ /** @internal */
39
+ export function unwrapArtifact(artifact) {
40
+ const value = artifactValues.get(artifact);
41
+ if (value === undefined)
42
+ throw new TypeError("invalid Flowersec artifact handle");
43
+ return value;
44
+ }
@@ -0,0 +1,10 @@
1
+ import { type Artifact } from "./artifact.js";
2
+ export declare class ArtifactLeaseError extends Error {
3
+ readonly code = "already_consumed";
4
+ constructor();
5
+ }
6
+ export declare class ArtifactLease {
7
+ #private;
8
+ private constructor();
9
+ }
10
+ export declare function createArtifactLease(artifact: Artifact, commitSpend: (signal?: AbortSignal) => Promise<void>): ArtifactLease;
@@ -0,0 +1,48 @@
1
+ import { validateArtifactV2 } from "../v2/artifact.js";
2
+ import { unwrapArtifact } from "./artifact.js";
3
+ export class ArtifactLeaseError extends Error {
4
+ code = "already_consumed";
5
+ constructor() {
6
+ super("Flowersec artifact lease has already been consumed");
7
+ this.name = "ArtifactLeaseError";
8
+ }
9
+ }
10
+ export class ArtifactLease {
11
+ #artifactLeaseBrand = undefined;
12
+ constructor() {
13
+ Object.freeze(this);
14
+ }
15
+ /** @internal */
16
+ static create() {
17
+ return new ArtifactLease();
18
+ }
19
+ }
20
+ const artifactLeaseStates = new WeakMap();
21
+ export function createArtifactLease(artifact, commitSpend) {
22
+ validateArtifactV2(unwrapArtifact(artifact));
23
+ const lease = ArtifactLease.create();
24
+ artifactLeaseStates.set(lease, { artifact, state: "idle", commitSpend });
25
+ return lease;
26
+ }
27
+ /** @internal Connector-owned access to validated lease material. */
28
+ export function artifactLeaseArtifact(lease) {
29
+ const leaseState = artifactLeaseStates.get(lease);
30
+ if (leaseState === undefined)
31
+ throw new ArtifactLeaseError();
32
+ return leaseState.artifact;
33
+ }
34
+ /** @internal Connector-owned durable spend transition. */
35
+ export async function commitArtifactLeaseSpend(lease, signal) {
36
+ const leaseState = artifactLeaseStates.get(lease);
37
+ if (leaseState === undefined || leaseState.state !== "idle")
38
+ throw new ArtifactLeaseError();
39
+ leaseState.state = "spending";
40
+ try {
41
+ await leaseState.commitSpend(signal);
42
+ leaseState.state = "consumed";
43
+ }
44
+ catch (error) {
45
+ leaseState.state = "idle";
46
+ throw error;
47
+ }
48
+ }
@@ -0,0 +1,6 @@
1
+ export type ConnectErrorCode = "invalid_input" | "invalid_options" | "expired_artifact" | "resolve_failed" | "credential_spend_failed" | "connection_failed" | "timeout" | "canceled" | "handshake_failed" | "rpc_failed" | "resource_exhausted" | "not_connected";
2
+ /** A stable connection failure that retains no carrier or credential details. */
3
+ export declare class ConnectError extends Error {
4
+ readonly code: ConnectErrorCode;
5
+ constructor(code: ConnectErrorCode);
6
+ }
@@ -0,0 +1,9 @@
1
+ /** A stable connection failure that retains no carrier or credential details. */
2
+ export class ConnectError extends Error {
3
+ code;
4
+ constructor(code) {
5
+ super(`Flowersec connection failed (code=${code})`);
6
+ this.code = code;
7
+ this.name = "ConnectError";
8
+ }
9
+ }