@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
@@ -0,0 +1,9 @@
1
+ import type { ByteStream, OperationOptions } from "../public/contract.js";
2
+ export declare class ProxyByteReader {
3
+ private readonly stream;
4
+ private readonly options;
5
+ private buffered;
6
+ constructor(stream: ByteStream, options?: OperationOptions);
7
+ readExactly(length: number): Promise<Uint8Array>;
8
+ }
9
+ export declare function writeAll(stream: ByteStream, input: Uint8Array, options?: OperationOptions): Promise<void>;
@@ -0,0 +1,40 @@
1
+ export class ProxyByteReader {
2
+ stream;
3
+ options;
4
+ buffered = new Uint8Array();
5
+ constructor(stream, options = {}) {
6
+ this.stream = stream;
7
+ this.options = options;
8
+ }
9
+ async readExactly(length) {
10
+ if (!Number.isSafeInteger(length) || length < 0)
11
+ throw new TypeError("invalid proxy read length");
12
+ const output = new Uint8Array(length);
13
+ let offset = 0;
14
+ while (offset < length) {
15
+ if (this.buffered.length === 0) {
16
+ const next = await this.stream.read(this.options);
17
+ if (next === null)
18
+ throw new Error("proxy stream ended unexpectedly");
19
+ if (next.length === 0)
20
+ continue;
21
+ this.buffered = next;
22
+ }
23
+ const take = Math.min(length - offset, this.buffered.length);
24
+ output.set(this.buffered.subarray(0, take), offset);
25
+ offset += take;
26
+ this.buffered = this.buffered.subarray(take);
27
+ }
28
+ return output;
29
+ }
30
+ }
31
+ export async function writeAll(stream, input, options = {}) {
32
+ let offset = 0;
33
+ while (offset < input.length) {
34
+ const written = await stream.write(input.subarray(offset), options);
35
+ if (!Number.isSafeInteger(written) || written <= 0 || written > input.length - offset) {
36
+ throw new Error("proxy stream write made no progress");
37
+ }
38
+ offset += written;
39
+ }
40
+ }
@@ -1,38 +1,86 @@
1
- export type Header = Readonly<{
1
+ import type { ByteStream, Session } from "../public/contract.js";
2
+ export type ProxyHeader = Readonly<{
2
3
  name: string;
3
4
  value: string;
4
5
  }>;
5
- export type ProxyError = Readonly<{
6
- code: string;
7
- message: string;
6
+ export type ProxyRuntimeLimits = Readonly<{
7
+ maxJsonFrameBytes: number;
8
+ maxChunkBytes: number;
9
+ maxBodyBytes: number;
10
+ maxWsFrameBytes: number;
11
+ maxWsBufferedAmountBytes: number;
12
+ maxConcurrentHttpStreams: number;
13
+ maxQueuedHttpRequests: number;
14
+ maxQueuedHttpBodyBytes: number;
8
15
  }>;
9
- export type HttpRequestMetaV1 = Readonly<{
10
- v: 1;
11
- request_id: string;
16
+ export type ProxyRuntimePathPolicy = Readonly<{
17
+ allowedPathPrefixes?: readonly string[];
18
+ deniedPathPrefixes?: readonly string[];
19
+ allowedWebSocketPathPrefixes?: readonly string[];
20
+ deniedWebSocketPathPrefixes?: readonly string[];
21
+ }>;
22
+ export type ProxyRuntimeOptions = Readonly<{
23
+ session: Session;
24
+ maxJsonFrameBytes?: number;
25
+ maxChunkBytes?: number;
26
+ maxBodyBytes?: number;
27
+ maxWsFrameBytes?: number;
28
+ maxWsBufferedAmountBytes?: number;
29
+ maxConcurrentHttpStreams?: number;
30
+ maxQueuedHttpRequests?: number;
31
+ maxQueuedHttpBodyBytes?: number;
32
+ timeoutMs?: number;
33
+ extraRequestHeaders?: readonly string[];
34
+ extraResponseHeaders?: readonly string[];
35
+ extraWebSocketHeaders?: readonly string[];
36
+ pathPolicy?: ProxyRuntimePathPolicy;
37
+ externalOrigin?: string;
38
+ runtimeRegistrationToken?: string;
39
+ }>;
40
+ export type ProxyFetchRequest = Readonly<{
41
+ id: string;
12
42
  method: string;
13
43
  path: string;
14
- headers: Header[];
15
- external_origin?: string;
16
- timeout_ms?: number;
17
- }>;
18
- export type HttpResponseMetaV1 = Readonly<{
19
- v: 1;
20
- request_id: string;
21
- ok: boolean;
22
- status?: number;
23
- headers?: Header[];
24
- error?: ProxyError;
25
- }>;
26
- export type WsOpenMetaV1 = Readonly<{
27
- v: 1;
28
- conn_id: string;
29
- path: string;
30
- headers: Header[];
44
+ headers: readonly ProxyHeader[];
45
+ externalOrigin?: string;
46
+ responseFlowControl?: "chunk_credit_v2";
47
+ body?: ArrayBuffer;
48
+ }>;
49
+ export type ProxyRuntime = Readonly<{
50
+ limits: ProxyRuntimeLimits;
51
+ dispatchFetch(request: ProxyFetchRequest, port: MessagePort): void;
52
+ openWebSocketStream(path: string, options?: Readonly<{
53
+ protocols?: readonly string[];
54
+ signal?: AbortSignal;
55
+ }>): Promise<Readonly<{
56
+ stream: ByteStream;
57
+ protocol: string;
58
+ }>>;
59
+ dispose(): void;
60
+ }>;
61
+ export type ProxyRuntimeScopeLimits = Readonly<{
62
+ timeoutMs?: number;
63
+ maxJsonFrameBytes?: number;
64
+ maxChunkBytes?: number;
65
+ maxBodyBytes?: number;
66
+ maxWsFrameBytes?: number;
67
+ }>;
68
+ type ProxyRuntimeScopeBase = Readonly<{
69
+ appBasePath?: string;
70
+ limits?: ProxyRuntimeScopeLimits;
71
+ }>;
72
+ export type ProxyRuntimeServiceWorkerScope = ProxyRuntimeScopeBase & Readonly<{
73
+ mode: "service_worker";
74
+ serviceWorker: Readonly<{
75
+ scriptUrl: string;
76
+ scope: string;
77
+ }>;
31
78
  }>;
32
- export type WsOpenRespV1 = Readonly<{
33
- v: 1;
34
- conn_id: string;
35
- ok: boolean;
36
- protocol?: string;
37
- error?: ProxyError;
79
+ export type ProxyRuntimeControllerBridgeScope = ProxyRuntimeScopeBase & Readonly<{
80
+ mode: "controller_bridge";
81
+ controllerBridge: Readonly<{
82
+ allowedOrigins: readonly string[];
83
+ }>;
38
84
  }>;
85
+ export type ProxyRuntimeScope = ProxyRuntimeServiceWorkerScope | ProxyRuntimeControllerBridgeScope;
86
+ export {};
@@ -0,0 +1,61 @@
1
+ import { SessionError, type ByteStream, type OperationOptions } from "../public/contract.js";
2
+ import { type ServiceWorkerControllerGuardConflictPolicy, type ServiceWorkerControllerGuardMonitorOptions, type ServiceWorkerControllerGuardRepairOptions } from "./controllerGuard.js";
3
+ import type { ProxyRuntime } from "./types.js";
4
+ export declare class MessagePortByteStream implements ByteStream {
5
+ private readonly port;
6
+ readonly kind = "flowersec.proxy.window.v2";
7
+ terminalError: SessionError | undefined;
8
+ private readonly reads;
9
+ private readonly readWaiters;
10
+ private readonly writeWaiters;
11
+ private nextWrite;
12
+ private buffered;
13
+ private readBuffered;
14
+ private ended;
15
+ private closed;
16
+ constructor(port: MessagePort);
17
+ read(options?: OperationOptions): Promise<Uint8Array | null>;
18
+ write(data: Uint8Array, options?: OperationOptions): Promise<number>;
19
+ closeWrite(): Promise<void>;
20
+ reset(): Promise<void>;
21
+ close(): Promise<void>;
22
+ private handle;
23
+ private finish;
24
+ private fail;
25
+ }
26
+ export type RegisterProxyAppWindowOptions = Readonly<{
27
+ controllerOrigin: string;
28
+ controllerWindow?: Window | null;
29
+ targetWindow?: Window;
30
+ maxWsFrameBytes?: number;
31
+ maxWsBufferedAmountBytes?: number;
32
+ capabilityNonce?: string;
33
+ }>;
34
+ export type ProxyAppWindowHandle = Readonly<{
35
+ runtime: ProxyRuntime;
36
+ dispose(): void;
37
+ }>;
38
+ export declare function registerProxyAppWindow(options: RegisterProxyAppWindowOptions): ProxyAppWindowHandle;
39
+ export type RegisterProxyControllerWindowOptions = Readonly<{
40
+ runtime: ProxyRuntime;
41
+ allowedOrigins: readonly string[];
42
+ targetWindow?: Window;
43
+ expectedSource?: Window | null;
44
+ capabilityNonce?: string;
45
+ }>;
46
+ export type ProxyControllerWindowHandle = Readonly<{
47
+ dispose(): void;
48
+ }>;
49
+ export declare function registerProxyControllerWindow(options: RegisterProxyControllerWindowOptions): ProxyControllerWindowHandle;
50
+ export type ProxyAppServiceWorkerControlOptions = Readonly<{
51
+ scriptUrl: string;
52
+ scope?: string;
53
+ expectedScriptPathSuffix: string;
54
+ repair?: ServiceWorkerControllerGuardRepairOptions;
55
+ monitor?: ServiceWorkerControllerGuardMonitorOptions;
56
+ conflicts?: ServiceWorkerControllerGuardConflictPolicy;
57
+ }>;
58
+ export type RegisterProxyAppWindowWithServiceWorkerControlOptions = RegisterProxyAppWindowOptions & Readonly<{
59
+ serviceWorker: ProxyAppServiceWorkerControlOptions;
60
+ }>;
61
+ export declare function registerProxyAppWindowWithServiceWorkerControl(options: RegisterProxyAppWindowWithServiceWorkerControlOptions): Promise<ProxyAppWindowHandle>;
@@ -0,0 +1,344 @@
1
+ import { SDK_DEFAULTS } from "../defaults.js";
2
+ import { SessionError } from "../public/contract.js";
3
+ import { createServiceWorkerControllerGuard, } from "./controllerGuard.js";
4
+ import { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
5
+ const FETCH_MESSAGE = "flowersec-proxy:window_fetch_v2";
6
+ const WEBSOCKET_OPEN_MESSAGE = "flowersec-proxy:window_ws_open_v2";
7
+ const WEBSOCKET_ACK_MESSAGE = "flowersec-proxy:window_ws_open_ack_v2";
8
+ const MAX_BRIDGE_CHUNK_BYTES = 1024 * 1024;
9
+ const MAX_BRIDGE_BUFFER_BYTES = 4 * 1024 * 1024;
10
+ export class MessagePortByteStream {
11
+ port;
12
+ kind = "flowersec.proxy.window.v2";
13
+ terminalError;
14
+ reads = [];
15
+ readWaiters = [];
16
+ writeWaiters = new Map();
17
+ nextWrite = 1;
18
+ buffered = 0;
19
+ readBuffered = 0;
20
+ ended = false;
21
+ closed = false;
22
+ constructor(port) {
23
+ this.port = port;
24
+ port.onmessage = (event) => this.handle(event.data);
25
+ port.onmessageerror = () => this.fail(new SessionError("operation_failed"));
26
+ port.start();
27
+ }
28
+ async read(options = {}) {
29
+ if (options.signal?.aborted === true)
30
+ throw new SessionError("canceled");
31
+ const queued = this.reads.shift();
32
+ if (queued !== undefined) {
33
+ this.readBuffered -= queued.byteLength;
34
+ return queued;
35
+ }
36
+ if (this.ended)
37
+ return null;
38
+ if (this.terminalError !== undefined)
39
+ throw this.terminalError;
40
+ return await new Promise((resolve, reject) => {
41
+ const waiter = { resolve, reject };
42
+ const onAbort = () => {
43
+ const index = this.readWaiters.indexOf(waiter);
44
+ if (index >= 0)
45
+ this.readWaiters.splice(index, 1);
46
+ reject(new SessionError("canceled"));
47
+ };
48
+ options.signal?.addEventListener("abort", onAbort, { once: true });
49
+ this.readWaiters.push(waiter);
50
+ });
51
+ }
52
+ async write(data, options = {}) {
53
+ if (this.closed || this.terminalError !== undefined)
54
+ throw this.terminalError ?? new SessionError("closed");
55
+ if (options.signal?.aborted === true)
56
+ throw new SessionError("canceled");
57
+ if (data.length === 0)
58
+ return 0;
59
+ if (data.length > MAX_BRIDGE_CHUNK_BYTES || this.buffered + data.length > MAX_BRIDGE_BUFFER_BYTES) {
60
+ throw new SessionError("resource_exhausted");
61
+ }
62
+ const id = this.nextWrite++;
63
+ const copy = data.slice();
64
+ this.buffered += copy.length;
65
+ await new Promise((resolve, reject) => {
66
+ this.writeWaiters.set(id, {
67
+ resolve: () => { this.buffered -= copy.length; resolve(); },
68
+ reject: (error) => { this.buffered -= copy.length; reject(error); },
69
+ });
70
+ try {
71
+ this.port.postMessage({ type: "chunk", id, data: copy.buffer }, [copy.buffer]);
72
+ }
73
+ catch {
74
+ this.writeWaiters.delete(id);
75
+ this.buffered -= copy.length;
76
+ reject(new SessionError("operation_failed"));
77
+ }
78
+ });
79
+ return data.length;
80
+ }
81
+ async closeWrite() {
82
+ if (this.closed)
83
+ return;
84
+ this.port.postMessage({ type: "end" });
85
+ }
86
+ async reset() {
87
+ if (this.closed)
88
+ return;
89
+ this.port.postMessage({ type: "reset" });
90
+ this.fail(new SessionError("stream_reset"));
91
+ }
92
+ async close() {
93
+ if (this.closed)
94
+ return;
95
+ this.port.postMessage({ type: "close" });
96
+ this.finish();
97
+ }
98
+ handle(value) {
99
+ if (value === null || typeof value !== "object" || !("type" in value))
100
+ return;
101
+ const message = value;
102
+ if (message.type === "chunk") {
103
+ if (!Number.isSafeInteger(message.id) || !(message.data instanceof ArrayBuffer) || message.data.byteLength > MAX_BRIDGE_CHUNK_BYTES) {
104
+ this.fail(new SessionError("operation_failed"));
105
+ return;
106
+ }
107
+ const chunk = new Uint8Array(message.data);
108
+ const id = message.id;
109
+ const waiter = this.readWaiters.shift();
110
+ if (waiter === undefined) {
111
+ if (this.readBuffered + chunk.byteLength > MAX_BRIDGE_BUFFER_BYTES) {
112
+ this.fail(new SessionError("resource_exhausted"));
113
+ return;
114
+ }
115
+ this.reads.push(chunk);
116
+ this.readBuffered += chunk.byteLength;
117
+ }
118
+ else {
119
+ waiter.resolve(chunk);
120
+ }
121
+ this.port.postMessage({ type: "ack", id });
122
+ return;
123
+ }
124
+ if (message.type === "ack" && Number.isSafeInteger(message.id)) {
125
+ const waiter = this.writeWaiters.get(message.id);
126
+ if (waiter !== undefined) {
127
+ this.writeWaiters.delete(message.id);
128
+ waiter.resolve();
129
+ }
130
+ return;
131
+ }
132
+ if (message.type === "end") {
133
+ this.ended = true;
134
+ for (const waiter of this.readWaiters.splice(0))
135
+ waiter.resolve(null);
136
+ return;
137
+ }
138
+ if (message.type === "reset")
139
+ this.fail(new SessionError("stream_reset"));
140
+ else if (message.type === "close")
141
+ this.finish();
142
+ }
143
+ finish() {
144
+ if (this.closed)
145
+ return;
146
+ this.closed = true;
147
+ this.ended = true;
148
+ this.reads.length = 0;
149
+ this.readBuffered = 0;
150
+ for (const waiter of this.readWaiters.splice(0))
151
+ waiter.resolve(null);
152
+ for (const waiter of this.writeWaiters.values())
153
+ waiter.reject(new SessionError("closed"));
154
+ this.writeWaiters.clear();
155
+ this.port.close();
156
+ }
157
+ fail(error) {
158
+ if (this.terminalError !== undefined)
159
+ return;
160
+ this.terminalError = error;
161
+ this.closed = true;
162
+ this.reads.length = 0;
163
+ this.readBuffered = 0;
164
+ for (const waiter of this.readWaiters.splice(0))
165
+ waiter.reject(error);
166
+ for (const waiter of this.writeWaiters.values())
167
+ waiter.reject(error);
168
+ this.writeWaiters.clear();
169
+ this.port.close();
170
+ }
171
+ }
172
+ function bridgeLimits(maxWsFrameBytes, maxWsBufferedAmountBytes) {
173
+ const wsFrame = maxWsFrameBytes ?? SDK_DEFAULTS.proxy.maxWsFrameBytes;
174
+ const buffered = maxWsBufferedAmountBytes ?? MAX_BRIDGE_BUFFER_BYTES;
175
+ if (!Number.isSafeInteger(wsFrame) || wsFrame <= 0 || !Number.isSafeInteger(buffered) || buffered <= 0) {
176
+ throw new TypeError("invalid proxy window bridge limits");
177
+ }
178
+ return Object.freeze({
179
+ maxJsonFrameBytes: SDK_DEFAULTS.proxy.maxJsonFrameBytes,
180
+ maxChunkBytes: SDK_DEFAULTS.proxy.maxChunkBytes,
181
+ maxBodyBytes: SDK_DEFAULTS.proxy.maxBodyBytes,
182
+ maxWsFrameBytes: wsFrame,
183
+ maxWsBufferedAmountBytes: buffered,
184
+ maxConcurrentHttpStreams: 24,
185
+ maxQueuedHttpRequests: 128,
186
+ maxQueuedHttpBodyBytes: 64 * 1024 * 1024,
187
+ });
188
+ }
189
+ function capability(input) {
190
+ if (input === undefined)
191
+ return undefined;
192
+ if (input === "" || input !== input.trim() || /[\u0000-\u0020\u007f]/u.test(input) || input.length > 256) {
193
+ throw new TypeError("invalid proxy window capability nonce");
194
+ }
195
+ return input;
196
+ }
197
+ export function registerProxyAppWindow(options) {
198
+ const target = options.targetWindow ?? globalThis.window;
199
+ const controller = options.controllerWindow ?? target.opener ?? (target.parent === target ? null : target.parent);
200
+ if (controller === null)
201
+ throw new Error("proxy controller window is unavailable");
202
+ const origin = new URL(options.controllerOrigin).origin;
203
+ if (origin !== options.controllerOrigin)
204
+ throw new TypeError("controllerOrigin must be an origin");
205
+ const nonce = capability(options.capabilityNonce);
206
+ const limits = bridgeLimits(options.maxWsFrameBytes, options.maxWsBufferedAmountBytes);
207
+ let disposed = false;
208
+ const dispatchFetch = (request, port) => {
209
+ if (disposed) {
210
+ port.postMessage({ type: "flowersec-proxy:response_error", status: 503, code: "closed", message: "proxy service unavailable" });
211
+ port.close();
212
+ return;
213
+ }
214
+ controller.postMessage({ type: FETCH_MESSAGE, version: 2, request, ...(nonce === undefined ? {} : { capabilityNonce: nonce }) }, origin, [port]);
215
+ };
216
+ const runtime = Object.freeze({
217
+ limits,
218
+ dispatchFetch,
219
+ openWebSocketStream: async (path, openOptions = {}) => {
220
+ if (disposed)
221
+ throw new SessionError("closed");
222
+ const channel = new MessageChannel();
223
+ const response = new Promise((resolve, reject) => {
224
+ const timer = setTimeout(() => { channel.port1.close(); reject(new SessionError("timeout")); }, 10_000);
225
+ channel.port1.onmessage = (event) => {
226
+ if (event.data?.type !== WEBSOCKET_ACK_MESSAGE)
227
+ return;
228
+ clearTimeout(timer);
229
+ if (event.data.ok !== true) {
230
+ channel.port1.close();
231
+ reject(new SessionError("operation_failed"));
232
+ return;
233
+ }
234
+ resolve(Object.freeze({ stream: new MessagePortByteStream(channel.port1), protocol: typeof event.data.protocol === "string" ? event.data.protocol : "" }));
235
+ };
236
+ });
237
+ controller.postMessage({
238
+ type: WEBSOCKET_OPEN_MESSAGE,
239
+ version: 2,
240
+ path,
241
+ protocols: openOptions.protocols ?? [],
242
+ ...(nonce === undefined ? {} : { capabilityNonce: nonce }),
243
+ }, origin, [channel.port2]);
244
+ if (openOptions.signal !== undefined) {
245
+ openOptions.signal.addEventListener("abort", () => channel.port1.close(), { once: true });
246
+ }
247
+ return await response;
248
+ },
249
+ dispose: () => { disposed = true; },
250
+ });
251
+ return Object.freeze({
252
+ runtime,
253
+ dispose: () => { disposed = true; },
254
+ });
255
+ }
256
+ async function bridgeStreams(runtimeStream, port) {
257
+ const bridge = new MessagePortByteStream(port);
258
+ const left = (async () => {
259
+ while (true) {
260
+ const chunk = await runtimeStream.read();
261
+ if (chunk === null) {
262
+ await bridge.closeWrite();
263
+ return;
264
+ }
265
+ await bridge.write(chunk);
266
+ }
267
+ })();
268
+ const right = (async () => {
269
+ while (true) {
270
+ const chunk = await bridge.read();
271
+ if (chunk === null) {
272
+ await runtimeStream.closeWrite();
273
+ return;
274
+ }
275
+ let offset = 0;
276
+ while (offset < chunk.length)
277
+ offset += await runtimeStream.write(chunk.subarray(offset));
278
+ }
279
+ })();
280
+ await Promise.all([left, right]);
281
+ await runtimeStream.close();
282
+ await bridge.close();
283
+ }
284
+ export function registerProxyControllerWindow(options) {
285
+ const target = options.targetWindow ?? globalThis.window;
286
+ const allowed = new Set(options.allowedOrigins.map((origin) => new URL(origin).origin));
287
+ if (allowed.size === 0 || [...allowed].some((origin) => !options.allowedOrigins.includes(origin))) {
288
+ throw new TypeError("allowedOrigins must contain exact origins");
289
+ }
290
+ const nonce = capability(options.capabilityNonce);
291
+ let disposed = false;
292
+ const onMessage = (event) => {
293
+ if (disposed || !allowed.has(event.origin) || (options.expectedSource !== undefined && event.source !== options.expectedSource))
294
+ return;
295
+ if (nonce !== undefined && event.data?.capabilityNonce !== nonce)
296
+ return;
297
+ const port = event.ports?.[0];
298
+ if (port === undefined)
299
+ return;
300
+ if (event.data?.type === FETCH_MESSAGE && event.data.version === 2) {
301
+ options.runtime.dispatchFetch(event.data.request, port);
302
+ return;
303
+ }
304
+ if (event.data?.type === WEBSOCKET_OPEN_MESSAGE && event.data.version === 2) {
305
+ void (async () => {
306
+ try {
307
+ const opened = await options.runtime.openWebSocketStream(String(event.data.path ?? ""), {
308
+ protocols: Array.isArray(event.data.protocols) ? event.data.protocols.map(String) : [],
309
+ });
310
+ port.postMessage({ type: WEBSOCKET_ACK_MESSAGE, ok: true, protocol: opened.protocol });
311
+ await bridgeStreams(opened.stream, port);
312
+ }
313
+ catch {
314
+ port.postMessage({ type: WEBSOCKET_ACK_MESSAGE, ok: false });
315
+ port.close();
316
+ }
317
+ })();
318
+ }
319
+ };
320
+ target.addEventListener("message", onMessage);
321
+ return Object.freeze({ dispose: () => { disposed = true; target.removeEventListener("message", onMessage); } });
322
+ }
323
+ export async function registerProxyAppWindowWithServiceWorkerControl(options) {
324
+ await registerServiceWorkerAndEnsureControl({
325
+ scriptUrl: options.serviceWorker.scriptUrl,
326
+ ...(options.serviceWorker.scope === undefined ? {} : { scope: options.serviceWorker.scope }),
327
+ ...(options.serviceWorker.repair?.queryKey === undefined ? {} : { repairQueryKey: options.serviceWorker.repair.queryKey }),
328
+ ...(options.serviceWorker.repair?.maxAttempts === undefined ? {} : { maxRepairAttempts: options.serviceWorker.repair.maxAttempts }),
329
+ ...(options.serviceWorker.repair?.controllerTimeoutMs === undefined ? {} : { controllerTimeoutMs: options.serviceWorker.repair.controllerTimeoutMs }),
330
+ });
331
+ const guard = createServiceWorkerControllerGuard({
332
+ ...(options.targetWindow === undefined ? {} : { targetWindow: options.targetWindow, navigationWindow: options.targetWindow }),
333
+ expectedScriptPathSuffix: options.serviceWorker.expectedScriptPathSuffix,
334
+ ...(options.serviceWorker.repair === undefined ? {} : { repair: options.serviceWorker.repair }),
335
+ ...(options.serviceWorker.monitor === undefined ? {} : { monitor: options.serviceWorker.monitor }),
336
+ ...(options.serviceWorker.conflicts === undefined ? {} : { conflicts: options.serviceWorker.conflicts }),
337
+ });
338
+ await guard.ensure();
339
+ const app = registerProxyAppWindow(options);
340
+ return Object.freeze({
341
+ runtime: app.runtime,
342
+ dispose: () => { app.dispose(); guard.dispose(); },
343
+ });
344
+ }
@@ -1,20 +1,10 @@
1
- import type { YamuxStream } from "../yamux/stream.js";
2
- import type { ProxyRuntimeLimits } from "./runtime.js";
1
+ import type { ProxyRuntime } from "./types.js";
3
2
  export type WebSocketPatchOptions = Readonly<{
4
- runtime: Readonly<{
5
- limits: Partial<ProxyRuntimeLimits>;
6
- openWebSocketStream: (path: string, opts?: Readonly<{
7
- protocols?: readonly string[];
8
- signal?: AbortSignal;
9
- }>) => Promise<Readonly<{
10
- stream: YamuxStream;
11
- protocol: string;
12
- }>>;
13
- }>;
3
+ runtime: Pick<ProxyRuntime, "limits" | "openWebSocketStream">;
14
4
  shouldProxy?: (url: URL) => boolean;
15
5
  maxWsFrameBytes?: number;
16
6
  maxWsBufferedAmountBytes?: number;
17
7
  }>;
18
- export declare function installWebSocketPatch(opts: WebSocketPatchOptions): Readonly<{
19
- uninstall: () => void;
8
+ export declare function installWebSocketPatch(options: WebSocketPatchOptions): Readonly<{
9
+ uninstall(): void;
20
10
  }>;