@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
@@ -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
  }>;