@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,350 +1,47 @@
1
- import { resolveProxyPreset } from "./preset.js";
1
+ import { connect } from "../browser/connectSession.js";
2
+ import { createProxyRuntime } from "./runtime.js";
2
3
  import { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
3
- import { createProxyRuntime, ensureServiceWorkerRuntimeRegistered } from "./runtime.js";
4
- import { createProxyServiceWorkerScript } from "./serviceWorker.js";
5
- function dedupeStrings(values) {
6
- const out = [];
7
- const seen = new Set();
8
- for (const v of values) {
9
- const n = String(v ?? "").trim();
10
- if (n === "" || seen.has(n))
11
- continue;
12
- seen.add(n);
13
- out.push(n);
14
- }
15
- return out;
16
- }
17
- function parseRepairAttemptFromHref(href, queryKey) {
18
- try {
19
- const u = new URL(href);
20
- const raw = String(u.searchParams.get(queryKey) ?? "").trim();
21
- const n = raw === "" ? 0 : Number(raw);
22
- if (!Number.isFinite(n) || n < 0)
23
- return 0;
24
- return Math.min(9, Math.floor(n));
25
- }
26
- catch {
27
- return 0;
28
- }
29
- }
30
- function buildHrefWithRepairAttempt(href, queryKey, attempt) {
31
- const u = new URL(href);
32
- u.searchParams.set(queryKey, String(Math.max(0, Math.floor(attempt))));
33
- return u.toString();
34
- }
35
- function isServiceWorkerScriptPathSuffix(raw, suffix) {
36
- const script = String(raw ?? "").trim();
37
- if (script === "")
38
- return false;
39
- const wanted = String(suffix ?? "").trim();
40
- if (wanted === "")
41
- return false;
4
+ import { registerProxyControllerWindow, } from "./windowBridge.js";
5
+ export async function connectProxyBrowser(lease, options = {}) {
6
+ const session = await connect(lease, options.connect);
7
+ let runtime;
42
8
  try {
43
- const u = new URL(script);
44
- return u.pathname.endsWith(wanted);
45
- }
46
- catch {
47
- return script.endsWith(wanted);
48
- }
49
- }
50
- async function waitForControllerSuffix(suffix, timeoutMs) {
51
- const sw = globalThis.navigator?.serviceWorker;
52
- if (!sw)
53
- return false;
54
- const isMatch = () => isServiceWorkerScriptPathSuffix(String(sw.controller?.scriptURL ?? ""), suffix);
55
- if (isMatch())
56
- return true;
57
- const ms = Math.max(0, Math.floor(timeoutMs));
58
- return await new Promise((resolve) => {
59
- let done = false;
60
- let timer = null;
61
- const finish = (ok) => {
62
- if (done)
63
- return;
64
- done = true;
65
- if (timer != null)
66
- window.clearTimeout(timer);
67
- sw.removeEventListener("controllerchange", onChange);
68
- resolve(ok);
69
- };
70
- const onChange = () => {
71
- if (isMatch())
72
- finish(true);
73
- };
74
- sw.addEventListener("controllerchange", onChange);
75
- if (isMatch()) {
76
- finish(true);
77
- return;
78
- }
79
- if (ms > 0)
80
- timer = window.setTimeout(() => finish(isMatch()), ms);
81
- });
82
- }
83
- async function uninstallConflictingServiceWorkers(conflicts) {
84
- if (conflicts?.uninstallOnMismatch === false)
85
- return;
86
- const sw = globalThis.navigator?.serviceWorker;
87
- if (!sw || typeof sw.getRegistrations !== "function")
88
- return;
89
- const keep = dedupeStrings(conflicts?.keepScriptPathSuffixes ?? []);
90
- const regs = await sw.getRegistrations();
91
- for (const reg of regs) {
92
- const script = String(reg?.active?.scriptURL ?? reg?.waiting?.scriptURL ?? reg?.installing?.scriptURL ?? "").trim();
93
- if (script === "")
94
- continue;
95
- let shouldKeep = false;
96
- for (const suffix of keep) {
97
- if (isServiceWorkerScriptPathSuffix(script, suffix)) {
98
- shouldKeep = true;
99
- break;
100
- }
101
- }
102
- if (shouldKeep)
103
- continue;
104
- try {
105
- await reg.unregister();
106
- }
107
- catch {
108
- // Best effort cleanup.
109
- }
110
- }
111
- }
112
- async function maybeRepairNavigation(opts) {
113
- const attempt = parseRepairAttemptFromHref(window.location.href, opts.queryKey);
114
- if (attempt >= opts.maxAttempts)
115
- return;
116
- if (opts.strategy === "reload") {
117
- window.location.reload();
118
- await new Promise(() => {
119
- // keep pending on navigation
9
+ if (options.serviceWorker !== undefined)
10
+ await registerServiceWorkerAndEnsureControl(options.serviceWorker);
11
+ runtime = createProxyRuntime({ session, ...options.runtime });
12
+ return Object.freeze({
13
+ session,
14
+ runtime,
15
+ dispose: async () => {
16
+ runtime.dispose();
17
+ await session.close();
18
+ },
120
19
  });
121
- return;
122
- }
123
- const next = buildHrefWithRepairAttempt(window.location.href, opts.queryKey, attempt + 1);
124
- window.location.replace(next);
125
- await new Promise(() => {
126
- // keep pending on navigation
127
- });
128
- }
129
- function applyPluginMutations(opts, plugins) {
130
- let out = opts;
131
- for (const plugin of plugins) {
132
- if (!plugin.mutateOptions)
133
- continue;
134
- out = plugin.mutateOptions(out);
135
- }
136
- return out;
137
- }
138
- function mergeConflictPolicy(base, plugins) {
139
- const keep = dedupeStrings([
140
- ...(base?.keepScriptPathSuffixes ?? []),
141
- ...plugins.flatMap((p) => p.serviceWorkerConflictPolicy?.keepScriptPathSuffixes ?? []),
142
- ]);
143
- let uninstallOnMismatch = base?.uninstallOnMismatch;
144
- for (const p of plugins) {
145
- if (p.serviceWorkerConflictPolicy?.uninstallOnMismatch != null) {
146
- uninstallOnMismatch = p.serviceWorkerConflictPolicy.uninstallOnMismatch;
147
- }
148
20
  }
149
- if (keep.length === 0 && uninstallOnMismatch == null)
150
- return base;
151
- const out = {};
152
- if (keep.length > 0)
153
- out.keepScriptPathSuffixes = keep;
154
- if (uninstallOnMismatch != null)
155
- out.uninstallOnMismatch = uninstallOnMismatch;
156
- return out;
157
- }
158
- function shouldIgnoreMismatch(plugins, ctx) {
159
- for (const plugin of plugins) {
160
- const action = plugin.onControllerMismatch?.(ctx);
161
- if (action === "ignore")
162
- return true;
21
+ catch (error) {
22
+ runtime?.dispose();
23
+ await session.close().catch(() => undefined);
24
+ throw error;
163
25
  }
164
- return false;
165
26
  }
166
- function resolveIntegrationPreset(opts) {
167
- if (opts.preset !== undefined)
168
- return resolveProxyPreset(opts.preset);
169
- return resolveProxyPreset();
170
- }
171
- function buildRuntimeOptions(preset, runtime) {
172
- return {
173
- maxJsonFrameBytes: runtime?.maxJsonFrameBytes ?? preset.limits.max_json_frame_bytes,
174
- maxChunkBytes: runtime?.maxChunkBytes ?? preset.limits.max_chunk_bytes,
175
- maxBodyBytes: runtime?.maxBodyBytes ?? preset.limits.max_body_bytes,
176
- maxWsFrameBytes: runtime?.maxWsFrameBytes ?? preset.limits.max_ws_frame_bytes,
177
- ...(runtime?.maxWsBufferedAmountBytes === undefined
178
- ? {}
179
- : { maxWsBufferedAmountBytes: runtime.maxWsBufferedAmountBytes }),
180
- timeoutMs: runtime?.timeoutMs ?? preset.limits.timeout_ms ?? 0,
181
- ...(runtime?.maxConcurrentHttpStreams === undefined
182
- ? {}
183
- : { maxConcurrentHttpStreams: runtime.maxConcurrentHttpStreams }),
184
- ...(runtime?.maxQueuedHttpRequests === undefined
185
- ? {}
186
- : { maxQueuedHttpRequests: runtime.maxQueuedHttpRequests }),
187
- ...(runtime?.maxQueuedHttpBodyBytes === undefined
188
- ? {}
189
- : { maxQueuedHttpBodyBytes: runtime.maxQueuedHttpBodyBytes }),
190
- ...(runtime?.pathPolicy === undefined ? {} : { pathPolicy: runtime.pathPolicy }),
191
- ...(runtime?.externalOrigin === undefined ? {} : { externalOrigin: runtime.externalOrigin }),
192
- ...(runtime?.runtimeRegistrationToken === undefined ? {} : { runtimeRegistrationToken: runtime.runtimeRegistrationToken }),
193
- };
194
- }
195
- function bindRuntimeGlobal(runtime, key) {
196
- const runtimeGlobalKey = String(key ?? "").trim();
197
- if (runtimeGlobalKey === "")
198
- return null;
199
- const target = globalThis;
200
- target[runtimeGlobalKey] = runtime;
201
- return () => {
202
- if (target[runtimeGlobalKey] === runtime) {
203
- delete target[runtimeGlobalKey];
204
- }
205
- };
206
- }
207
- export function createProxyIntegrationServiceWorkerScript(opts = {}) {
208
- const plugins = opts.plugins ?? [];
209
- let scriptOpts = opts.baseOptions ?? {};
210
- for (const plugin of plugins) {
211
- if (!plugin.extendServiceWorkerScriptOptions)
212
- continue;
213
- scriptOpts = plugin.extendServiceWorkerScriptOptions(scriptOpts);
214
- }
215
- const forwarded = dedupeStrings([
216
- ...(scriptOpts.forwardFetchMessageTypes ?? []),
217
- ...plugins.flatMap((p) => p.forwardFetchMessageTypes ?? []),
218
- ]);
219
- const keepSuffixes = dedupeStrings([
220
- ...(scriptOpts.conflictHints?.keepScriptPathSuffixes ?? []),
221
- ...plugins.flatMap((p) => p.serviceWorkerConflictPolicy?.keepScriptPathSuffixes ?? []),
222
- ]);
223
- const finalOpts = {
224
- ...scriptOpts,
225
- forwardFetchMessageTypes: forwarded,
226
- };
227
- if (keepSuffixes.length > 0) {
228
- return createProxyServiceWorkerScript({
229
- ...finalOpts,
230
- conflictHints: { keepScriptPathSuffixes: keepSuffixes },
231
- });
232
- }
233
- if (scriptOpts.conflictHints != null) {
234
- return createProxyServiceWorkerScript({
235
- ...finalOpts,
236
- conflictHints: scriptOpts.conflictHints,
27
+ export async function connectProxyControllerBrowser(lease, options) {
28
+ const base = await connectProxyBrowser(lease, options);
29
+ let controller;
30
+ try {
31
+ controller = registerProxyControllerWindow({ runtime: base.runtime, ...options.controller });
32
+ return Object.freeze({
33
+ session: base.session,
34
+ runtime: base.runtime,
35
+ controller,
36
+ dispose: async () => {
37
+ controller.dispose();
38
+ await base.dispose();
39
+ },
237
40
  });
238
41
  }
239
- return createProxyServiceWorkerScript(finalOpts);
240
- }
241
- export async function registerProxyIntegration(input) {
242
- const plugins = input.plugins ?? [];
243
- const opts = applyPluginMutations(input, plugins);
244
- const preset = resolveIntegrationPreset(opts);
245
- const runtimeOpts = buildRuntimeOptions(preset, opts.runtime);
246
- const runtime = createProxyRuntime({ ...runtimeOpts, client: opts.client });
247
- const releaseRuntimeGlobal = bindRuntimeGlobal(runtime, opts.runtimeGlobalKey);
248
- const swCfg = opts.serviceWorker;
249
- const repairQueryKey = String(swCfg.repair?.queryKey ?? "_flowersec_sw_repair").trim() || "_flowersec_sw_repair";
250
- const maxRepairAttempts = Math.max(0, Math.floor(swCfg.repair?.maxAttempts ?? 2));
251
- const controllerTimeoutMs = Math.max(0, Math.floor(swCfg.repair?.controllerTimeoutMs ?? 8_000));
252
- const strategy = swCfg.repair?.strategy ?? "replace";
253
- const conflicts = mergeConflictPolicy(swCfg.conflicts, plugins);
254
- const finalScope = String(swCfg.scope ?? "/").trim() || "/";
255
- const expectedScriptPathSuffix = String(swCfg.expectedScriptPathSuffix ?? "").trim();
256
- await uninstallConflictingServiceWorkers(conflicts);
257
- await registerServiceWorkerAndEnsureControl({
258
- scriptUrl: swCfg.scriptUrl,
259
- scope: finalScope,
260
- repairQueryKey,
261
- maxRepairAttempts,
262
- controllerTimeoutMs,
263
- });
264
- await ensureServiceWorkerRuntimeRegistered({
265
- timeoutMs: controllerTimeoutMs,
266
- ...(runtimeOpts.runtimeRegistrationToken === undefined ? {} : { runtimeRegistrationToken: runtimeOpts.runtimeRegistrationToken }),
267
- });
268
- if (expectedScriptPathSuffix !== "") {
269
- const ok = await waitForControllerSuffix(expectedScriptPathSuffix, controllerTimeoutMs);
270
- if (!ok) {
271
- const actual = String(globalThis.navigator?.serviceWorker?.controller?.scriptURL ?? "").trim();
272
- const ctx = {
273
- expectedScriptPathSuffix,
274
- actualScriptURL: actual,
275
- stage: "register",
276
- };
277
- if (!shouldIgnoreMismatch(plugins, ctx)) {
278
- await uninstallConflictingServiceWorkers(conflicts);
279
- await maybeRepairNavigation({ queryKey: repairQueryKey, maxAttempts: maxRepairAttempts, strategy });
280
- throw new Error("Proxy Service Worker is installed but not controlling this page");
281
- }
282
- }
42
+ catch (error) {
43
+ controller?.dispose();
44
+ await base.dispose();
45
+ throw error;
283
46
  }
284
- const monitorEnabled = swCfg.monitor?.enabled ?? true;
285
- const monitorThrottleMs = Math.max(0, Math.floor(swCfg.monitor?.throttleMs ?? 10_000));
286
- const sw = globalThis.navigator?.serviceWorker;
287
- let monitorHandler = null;
288
- let lastMonitorRepairAt = 0;
289
- if (monitorEnabled && sw && expectedScriptPathSuffix !== "") {
290
- monitorHandler = () => {
291
- const actual = String(sw.controller?.scriptURL ?? "").trim();
292
- if (isServiceWorkerScriptPathSuffix(actual, expectedScriptPathSuffix))
293
- return;
294
- const ctx = {
295
- expectedScriptPathSuffix,
296
- actualScriptURL: actual,
297
- stage: "monitor",
298
- };
299
- if (shouldIgnoreMismatch(plugins, ctx))
300
- return;
301
- const now = Date.now();
302
- if (monitorThrottleMs > 0 && now - lastMonitorRepairAt <= monitorThrottleMs)
303
- return;
304
- lastMonitorRepairAt = now;
305
- void maybeRepairNavigation({ queryKey: repairQueryKey, maxAttempts: maxRepairAttempts, strategy });
306
- };
307
- sw.addEventListener("controllerchange", monitorHandler);
308
- }
309
- const ctx = {
310
- runtime,
311
- options: opts,
312
- preset,
313
- };
314
- for (const plugin of plugins) {
315
- await plugin.onRegistered?.(ctx);
316
- }
317
- return {
318
- runtime,
319
- dispose: async () => {
320
- let firstError = null;
321
- if (monitorHandler && sw) {
322
- sw.removeEventListener("controllerchange", monitorHandler);
323
- }
324
- try {
325
- runtime.dispose();
326
- }
327
- catch (error) {
328
- firstError = error;
329
- }
330
- for (const plugin of plugins) {
331
- try {
332
- await plugin.onDisposed?.();
333
- }
334
- catch (error) {
335
- if (firstError == null)
336
- firstError = error;
337
- }
338
- }
339
- try {
340
- releaseRuntimeGlobal?.();
341
- }
342
- catch (error) {
343
- if (firstError == null)
344
- firstError = error;
345
- }
346
- if (firstError != null)
347
- throw firstError;
348
- },
349
- };
350
47
  }
@@ -5,13 +5,4 @@ export type RegisterServiceWorkerOptions = Readonly<{
5
5
  maxRepairAttempts?: number;
6
6
  controllerTimeoutMs?: number;
7
7
  }>;
8
- declare function parseRepairAttemptFromHref(href: string, queryKey: string): number;
9
- declare function buildHrefWithRepairAttempt(href: string, queryKey: string, attempt: number): string;
10
- declare function buildHrefWithoutRepairQueryParam(href: string, queryKey: string): string;
11
- export declare function registerServiceWorkerAndEnsureControl(opts: RegisterServiceWorkerOptions): Promise<void>;
12
- export declare const __testOnly: {
13
- parseRepairAttemptFromHref: typeof parseRepairAttemptFromHref;
14
- buildHrefWithRepairAttempt: typeof buildHrefWithRepairAttempt;
15
- buildHrefWithoutRepairQueryParam: typeof buildHrefWithoutRepairQueryParam;
16
- };
17
- export {};
8
+ export declare function registerServiceWorkerAndEnsureControl(options: RegisterServiceWorkerOptions): Promise<void>;
@@ -1,116 +1,54 @@
1
- function parseRepairAttemptFromHref(href, queryKey) {
1
+ function currentAttempt(key) {
2
2
  try {
3
- const u = new URL(href);
4
- const raw = String(u.searchParams.get(queryKey) ?? "").trim();
5
- const n = raw ? Number(raw) : 0;
6
- if (!Number.isFinite(n) || n < 0)
7
- return 0;
8
- return Math.min(9, Math.floor(n));
3
+ const value = Number(new URL(globalThis.location.href).searchParams.get(key) ?? "0");
4
+ return Number.isSafeInteger(value) && value >= 0 ? value : 0;
9
5
  }
10
6
  catch {
11
7
  return 0;
12
8
  }
13
9
  }
14
- function buildHrefWithRepairAttempt(href, queryKey, attempt) {
15
- const u = new URL(href);
16
- u.searchParams.set(queryKey, String(Math.max(0, Math.floor(attempt))));
17
- return u.toString();
10
+ function repairNavigation(key, attempt) {
11
+ const target = new URL(globalThis.location.href);
12
+ target.searchParams.set(key, String(attempt));
13
+ globalThis.location.replace(target.toString());
14
+ throw new Error("service worker controller navigation started");
18
15
  }
19
- function buildHrefWithoutRepairQueryParam(href, queryKey) {
20
- const u = new URL(href);
21
- u.searchParams.delete(queryKey);
22
- const search = u.searchParams.toString();
23
- return u.pathname + (search ? `?${search}` : "") + u.hash;
24
- }
25
- function waitForController(timeoutMs) {
26
- if (navigator.serviceWorker.controller)
27
- return Promise.resolve(true);
28
- const ms = Math.max(0, Math.floor(timeoutMs));
29
- return new Promise((resolve) => {
30
- let done = false;
31
- let t = null;
32
- function finish(ok) {
33
- if (done)
34
- return;
35
- done = true;
36
- if (t != null)
37
- window.clearTimeout(t);
38
- navigator.serviceWorker.removeEventListener("controllerchange", onChange);
39
- resolve(ok);
40
- }
41
- function onChange() {
42
- finish(true);
43
- }
44
- navigator.serviceWorker.addEventListener("controllerchange", onChange);
45
- // Avoid race: controller can become available before/after the listener registration.
46
- if (navigator.serviceWorker.controller) {
47
- finish(true);
48
- return;
49
- }
50
- if (ms > 0) {
51
- t = window.setTimeout(() => finish(Boolean(navigator.serviceWorker.controller)), ms);
52
- }
53
- });
54
- }
55
- // registerServiceWorkerAndEnsureControl registers a Service Worker and ensures the current page load is controlled.
56
- //
57
- // In DevTools hard reload flows, the SW may be installed but not control the current page load.
58
- // When this happens, we perform a limited "soft navigation" repair to recover control.
59
- export async function registerServiceWorkerAndEnsureControl(opts) {
60
- const scriptUrl = String(opts.scriptUrl ?? "").trim();
61
- if (!scriptUrl)
62
- throw new Error("scriptUrl is required");
63
- if (globalThis.navigator?.serviceWorker == null) {
64
- throw new Error("service worker is not available in this environment");
16
+ export async function registerServiceWorkerAndEnsureControl(options) {
17
+ const serviceWorker = globalThis.navigator?.serviceWorker;
18
+ if (serviceWorker === undefined)
19
+ throw new Error("service workers are unavailable");
20
+ const scriptUrl = options.scriptUrl.trim();
21
+ const scope = options.scope?.trim() || "/";
22
+ if (scriptUrl === "" || scope === "")
23
+ throw new TypeError("service worker script and scope are required");
24
+ const timeoutMs = options.controllerTimeoutMs ?? 8_000;
25
+ const maxAttempts = options.maxRepairAttempts ?? 2;
26
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0 || timeoutMs > 60_000 || !Number.isSafeInteger(maxAttempts) || maxAttempts < 0 || maxAttempts > 5) {
27
+ throw new TypeError("invalid service worker control policy");
65
28
  }
66
- const scope = String(opts.scope ?? "/").trim() || "/";
67
- const queryKey = String(opts.repairQueryKey ?? "_flowersec_sw_repair").trim() || "_flowersec_sw_repair";
68
- const maxRepairAttempts = Math.max(0, Math.floor(opts.maxRepairAttempts ?? 2));
69
- const controllerTimeoutMs = Math.max(0, Math.floor(opts.controllerTimeoutMs ?? 2_000));
70
- await navigator.serviceWorker.register(scriptUrl, { scope });
71
- await navigator.serviceWorker.ready;
72
- const attempt = parseRepairAttemptFromHref(window.location.href, queryKey);
73
- if (navigator.serviceWorker.controller) {
74
- if (attempt > 0) {
75
- try {
76
- const next = buildHrefWithoutRepairQueryParam(window.location.href, queryKey);
77
- history.replaceState(null, document.title, next);
78
- }
79
- catch {
80
- // ignore
81
- }
82
- }
29
+ await serviceWorker.register(scriptUrl, { scope });
30
+ await serviceWorker.ready;
31
+ if (serviceWorker.controller !== null)
83
32
  return;
84
- }
85
- const controlled = await waitForController(controllerTimeoutMs);
86
- if (controlled) {
87
- if (attempt > 0) {
88
- try {
89
- const next = buildHrefWithoutRepairQueryParam(window.location.href, queryKey);
90
- history.replaceState(null, document.title, next);
91
- }
92
- catch {
93
- // ignore
94
- }
95
- }
33
+ const controlled = await new Promise((resolve) => {
34
+ let settled = false;
35
+ const finish = (value) => {
36
+ if (settled)
37
+ return;
38
+ settled = true;
39
+ clearTimeout(timer);
40
+ serviceWorker.removeEventListener("controllerchange", onChange);
41
+ resolve(value);
42
+ };
43
+ const onChange = () => finish(serviceWorker.controller !== null);
44
+ serviceWorker.addEventListener("controllerchange", onChange);
45
+ const timer = setTimeout(() => finish(serviceWorker.controller !== null), timeoutMs);
46
+ });
47
+ if (controlled)
96
48
  return;
97
- }
98
- if (attempt < maxRepairAttempts) {
99
- try {
100
- const next = buildHrefWithRepairAttempt(window.location.href, queryKey, attempt + 1);
101
- window.location.replace(next);
102
- }
103
- catch {
104
- window.location.reload();
105
- }
106
- // Navigation will interrupt JS; keep pending so callers don't proceed with dependent work.
107
- await new Promise(() => { });
108
- }
109
- throw new Error("Service Worker is installed but not controlling this page");
49
+ const key = options.repairQueryKey?.trim() || "_flowersec_sw_repair";
50
+ const attempt = currentAttempt(key);
51
+ if (attempt >= maxAttempts)
52
+ throw new Error("service worker did not take control");
53
+ repairNavigation(key, attempt + 1);
110
54
  }
111
- // Test-only exports (not re-exported from the public proxy entrypoint).
112
- export const __testOnly = {
113
- parseRepairAttemptFromHref,
114
- buildHrefWithRepairAttempt,
115
- buildHrefWithoutRepairQueryParam,
116
- };
@@ -1,67 +1,7 @@
1
- import type { Client } from "../client.js";
2
- import type { YamuxStream } from "../yamux/stream.js";
3
- import { CookieJar } from "./cookieJar.js";
4
- import type { Header } from "./types.js";
5
- type ProxyFetchReq = Readonly<{
6
- id: string;
7
- method: string;
8
- path: string;
9
- headers: readonly Header[];
10
- external_origin?: string;
11
- response_flow_control?: "chunk_credit_v1";
12
- body?: ArrayBuffer;
13
- }>;
14
- export type ProxyRuntimeLimits = Readonly<{
15
- maxJsonFrameBytes: number;
16
- maxChunkBytes: number;
17
- maxBodyBytes: number;
18
- maxWsFrameBytes: number;
19
- maxWsBufferedAmountBytes: number;
20
- maxConcurrentHttpStreams: number;
21
- maxQueuedHttpRequests: number;
22
- maxQueuedHttpBodyBytes: number;
23
- }>;
24
- export type ProxyRuntime = Readonly<{
25
- limits: ProxyRuntimeLimits;
26
- dispose: () => void;
27
- dispatchFetch: (req: ProxyFetchReq, port: MessagePort) => void;
28
- openWebSocketStream: (path: string, opts?: Readonly<{
29
- protocols?: readonly string[];
30
- signal?: AbortSignal;
31
- }>) => Promise<Readonly<{
32
- stream: YamuxStream;
33
- protocol: string;
34
- }>>;
35
- }>;
36
- export type ProxyRuntimePathPolicy = Readonly<{
37
- allowedPathPrefixes?: readonly string[];
38
- deniedPathPrefixes?: readonly string[];
39
- allowedWebSocketPathPrefixes?: readonly string[];
40
- deniedWebSocketPathPrefixes?: readonly string[];
41
- }>;
42
- export type ProxyRuntimeOptions = Readonly<{
43
- client: Client;
44
- maxJsonFrameBytes?: number;
45
- maxChunkBytes?: number;
46
- maxBodyBytes?: number;
47
- maxWsFrameBytes?: number;
48
- maxWsBufferedAmountBytes?: number;
49
- maxConcurrentHttpStreams?: number;
50
- maxQueuedHttpRequests?: number;
51
- maxQueuedHttpBodyBytes?: number;
52
- timeoutMs?: number;
53
- extraRequestHeaders?: readonly string[];
54
- extraResponseHeaders?: readonly string[];
55
- extraWsHeaders?: readonly string[];
56
- cookieJar?: CookieJar;
57
- pathPolicy?: ProxyRuntimePathPolicy;
58
- externalOrigin?: string;
59
- runtimeRegistrationToken?: string;
60
- }>;
1
+ import type { ProxyRuntime, ProxyRuntimeOptions } from "./types.js";
2
+ export declare function createProxyRuntime(options: ProxyRuntimeOptions): ProxyRuntime;
61
3
  export type EnsureServiceWorkerRuntimeRegisteredOptions = Readonly<{
62
4
  timeoutMs?: number;
63
5
  runtimeRegistrationToken?: string;
64
6
  }>;
65
- export declare function createProxyRuntime(opts: ProxyRuntimeOptions): ProxyRuntime;
66
- export declare function ensureServiceWorkerRuntimeRegistered(opts?: EnsureServiceWorkerRuntimeRegisteredOptions): Promise<void>;
67
- export {};
7
+ export declare function ensureServiceWorkerRuntimeRegistered(options?: EnsureServiceWorkerRuntimeRegisteredOptions): Promise<void>;