@floegence/flowersec-core 0.26.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/README.md +91 -22
  2. package/THIRD_PARTY_NOTICES.md +141 -0
  3. package/dist/browser/connectSession.d.ts +13 -0
  4. package/dist/browser/connectSession.js +40 -0
  5. package/dist/browser/index.d.ts +3 -10
  6. package/dist/browser/index.js +2 -5
  7. package/dist/browser/runtimeCapability.d.ts +13 -0
  8. package/dist/browser/runtimeCapability.js +27 -0
  9. package/dist/browser/sessionRuntime.d.ts +2 -0
  10. package/dist/browser/sessionRuntime.js +21 -0
  11. package/dist/browser/webTransportClient.d.ts +8 -0
  12. package/dist/browser/webTransportClient.js +55 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +126 -0
  15. package/dist/cliSpendMarker.d.ts +1 -0
  16. package/dist/cliSpendMarker.js +22 -0
  17. package/dist/connectionController.d.ts +54 -0
  18. package/dist/connectionController.js +438 -0
  19. package/dist/connector/adapters/webSocketCandidate.d.ts +7 -0
  20. package/dist/connector/adapters/webSocketCandidate.js +116 -0
  21. package/dist/connector/adapters/webTransportCandidate.d.ts +5 -0
  22. package/dist/connector/adapters/webTransportCandidate.js +73 -0
  23. package/dist/connector/admissionCommit.d.ts +31 -0
  24. package/dist/connector/admissionCommit.js +239 -0
  25. package/dist/connector/sessionAcceptor.d.ts +16 -0
  26. package/dist/connector/sessionAcceptor.js +114 -0
  27. package/dist/connector/sessionConfig.d.ts +3 -0
  28. package/dist/connector/sessionConfig.js +30 -0
  29. package/dist/connector/sessionConnector.d.ts +45 -0
  30. package/dist/connector/sessionConnector.js +603 -0
  31. package/dist/defaults.d.ts +7 -7
  32. package/dist/defaults.js +7 -7
  33. package/dist/facade.d.ts +12 -30
  34. package/dist/facade.js +6 -30
  35. package/dist/node/connectSession.d.ts +21 -0
  36. package/dist/node/connectSession.js +62 -0
  37. package/dist/node/index.d.ts +3 -11
  38. package/dist/node/index.js +2 -8
  39. package/dist/node/runtimeCapability.d.ts +7 -0
  40. package/dist/node/runtimeCapability.js +10 -0
  41. package/dist/node/sessionRuntime.d.ts +2 -0
  42. package/dist/node/sessionRuntime.js +13 -0
  43. package/dist/node/webTransportClient.d.ts +9 -0
  44. package/dist/node/webTransportClient.js +56 -0
  45. package/dist/node/webTransportServer.d.ts +22 -0
  46. package/dist/node/webTransportServer.js +100 -0
  47. package/dist/node/wsFactory.d.ts +3 -1
  48. package/dist/node/wsFactory.js +4 -3
  49. package/dist/proxy/controllerGuard.d.ts +4 -4
  50. package/dist/proxy/controllerGuard.js +83 -177
  51. package/dist/proxy/disableUpstreamServiceWorkerRegister.d.ts +3 -1
  52. package/dist/proxy/disableUpstreamServiceWorkerRegister.js +19 -17
  53. package/dist/proxy/index.d.ts +16 -15
  54. package/dist/proxy/index.js +8 -14
  55. package/dist/proxy/integration.d.ts +23 -71
  56. package/dist/proxy/integration.js +37 -340
  57. package/dist/proxy/registerServiceWorker.d.ts +1 -10
  58. package/dist/proxy/registerServiceWorker.js +43 -105
  59. package/dist/proxy/runtime.d.ts +3 -63
  60. package/dist/proxy/runtime.js +349 -462
  61. package/dist/proxy/scope.d.ts +6 -0
  62. package/dist/proxy/scope.js +131 -0
  63. package/dist/proxy/serviceWorker.d.ts +5 -16
  64. package/dist/proxy/serviceWorker.js +230 -617
  65. package/dist/proxy/stream.d.ts +9 -0
  66. package/dist/proxy/stream.js +40 -0
  67. package/dist/proxy/types.d.ts +78 -30
  68. package/dist/proxy/windowBridge.d.ts +61 -0
  69. package/dist/proxy/windowBridge.js +344 -0
  70. package/dist/proxy/wsPatch.d.ts +4 -14
  71. package/dist/proxy/wsPatch.js +173 -268
  72. package/dist/public/artifact.d.ts +11 -0
  73. package/dist/public/artifact.js +44 -0
  74. package/dist/public/artifactLease.d.ts +10 -0
  75. package/dist/public/artifactLease.js +48 -0
  76. package/dist/public/connectError.d.ts +6 -0
  77. package/dist/public/connectError.js +9 -0
  78. package/dist/public/contract.d.ts +83 -0
  79. package/dist/public/contract.js +17 -0
  80. package/dist/public/streamMetadata.d.ts +10 -0
  81. package/dist/public/streamMetadata.js +40 -0
  82. package/dist/rpc/caller.d.ts +1 -1
  83. package/dist/rpc/client.d.ts +1 -4
  84. package/dist/rpc/client.js +0 -21
  85. package/dist/rpc/rpcProxy.d.ts +1 -1
  86. package/dist/rpc/server.d.ts +1 -1
  87. package/dist/rpc/validate.d.ts +1 -1
  88. package/dist/rpc/wire.d.ts +15 -0
  89. package/dist/runtime/errors.d.ts +6 -0
  90. package/dist/runtime/errors.js +10 -0
  91. package/dist/transport/webSocketAdapter.d.ts +24 -0
  92. package/dist/transport/webSocketAdapter.js +234 -0
  93. package/dist/transport/webTransportAdapter.d.ts +27 -0
  94. package/dist/transport/webTransportAdapter.js +519 -0
  95. package/dist/utils/errors.d.ts +2 -20
  96. package/dist/utils/errors.js +73 -20
  97. package/dist/v2/admissionError.d.ts +4 -0
  98. package/dist/v2/admissionError.js +8 -0
  99. package/dist/v2/artifact.d.ts +128 -0
  100. package/dist/v2/artifact.js +1126 -0
  101. package/dist/v2/artifactLease.d.ts +1 -0
  102. package/dist/v2/artifactLease.js +7 -0
  103. package/dist/v2/capability.d.ts +29 -0
  104. package/dist/v2/capability.js +190 -0
  105. package/dist/v2/carrier.d.ts +79 -0
  106. package/dist/v2/carrier.js +405 -0
  107. package/dist/v2/contract.d.ts +37 -0
  108. package/dist/v2/contract.js +1 -0
  109. package/dist/v2/handshake.d.ts +80 -0
  110. package/dist/v2/handshake.js +470 -0
  111. package/dist/v2/opaqueArtifact.d.ts +1 -0
  112. package/dist/v2/opaqueArtifact.js +2 -0
  113. package/dist/v2/protocol.d.ts +99 -0
  114. package/dist/v2/protocol.js +739 -0
  115. package/dist/v2/publicSession.d.ts +1 -0
  116. package/dist/v2/publicSession.js +179 -0
  117. package/dist/v2/retryDisposition.d.ts +16 -0
  118. package/dist/v2/retryDisposition.js +30 -0
  119. package/dist/v2/session.d.ts +277 -0
  120. package/dist/v2/session.js +1879 -0
  121. package/dist/v2/streamLifetimeLedger.d.ts +31 -0
  122. package/dist/v2/streamLifetimeLedger.js +131 -0
  123. package/dist/v2/streamMetadata.d.ts +1 -0
  124. package/dist/v2/streamMetadata.js +7 -0
  125. package/dist/v2/unreliableMessage.d.ts +43 -0
  126. package/dist/v2/unreliableMessage.js +252 -0
  127. package/dist/vendor/tr46.d.ts +10 -0
  128. package/dist/vendor/tr46.js +501 -0
  129. package/dist/ws-client/binaryTransport.d.ts +0 -3
  130. package/dist/ws-client/binaryTransport.js +15 -39
  131. package/dist/yamux/errors.d.ts +0 -4
  132. package/dist/yamux/errors.js +0 -9
  133. package/dist/yamux/session.d.ts +0 -5
  134. package/dist/yamux/session.js +4 -70
  135. package/dist/yamux/stream.d.ts +1 -0
  136. package/dist/yamux/stream.js +4 -0
  137. package/package.json +29 -83
  138. package/sbom/cyclonedx.json +2764 -0
  139. package/sbom/spdx.json +1947 -0
  140. package/YAMUX_ALIGNMENT.md +0 -127
  141. package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +0 -12
  142. package/dist/_examples/flowersec/demo/v1.facade.gen.js +0 -15
  143. package/dist/_examples/flowersec/demo/v1.gen.d.ts +0 -16
  144. package/dist/_examples/flowersec/demo/v1.gen.js +0 -86
  145. package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +0 -11
  146. package/dist/_examples/flowersec/demo/v1.rpc.gen.js +0 -22
  147. package/dist/browser/connect.d.ts +0 -12
  148. package/dist/browser/connect.js +0 -31
  149. package/dist/browser/controlplane.d.ts +0 -15
  150. package/dist/browser/controlplane.js +0 -64
  151. package/dist/browser/reconnectConfig.d.ts +0 -15
  152. package/dist/browser/reconnectConfig.js +0 -20
  153. package/dist/client-connect/common.d.ts +0 -26
  154. package/dist/client-connect/common.js +0 -167
  155. package/dist/client-connect/connectCore.d.ts +0 -61
  156. package/dist/client-connect/connectCore.js +0 -572
  157. package/dist/client-connect/contract.d.ts +0 -8
  158. package/dist/client-connect/contract.js +0 -51
  159. package/dist/client-connect/termination.d.ts +0 -6
  160. package/dist/client-connect/termination.js +0 -7
  161. package/dist/client-connect/transportSecurity.d.ts +0 -27
  162. package/dist/client-connect/transportSecurity.js +0 -199
  163. package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
  164. package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
  165. package/dist/client.d.ts +0 -23
  166. package/dist/connect/artifact.d.ts +0 -37
  167. package/dist/connect/artifact.js +0 -217
  168. package/dist/connect/internalNormalize.d.ts +0 -22
  169. package/dist/connect/internalNormalize.js +0 -173
  170. package/dist/controlplane/channelInit.d.ts +0 -27
  171. package/dist/controlplane/channelInit.js +0 -116
  172. package/dist/controlplane/http.d.ts +0 -16
  173. package/dist/controlplane/http.js +0 -59
  174. package/dist/controlplane/index.d.ts +0 -6
  175. package/dist/controlplane/index.js +0 -5
  176. package/dist/controlplane/issuer.d.ts +0 -18
  177. package/dist/controlplane/issuer.js +0 -113
  178. package/dist/controlplane/request.d.ts +0 -51
  179. package/dist/controlplane/request.js +0 -273
  180. package/dist/controlplane/token.d.ts +0 -33
  181. package/dist/controlplane/token.js +0 -119
  182. package/dist/direct-client/connect.d.ts +0 -9
  183. package/dist/direct-client/connect.js +0 -90
  184. package/dist/direct-client/index.d.ts +0 -1
  185. package/dist/direct-client/index.js +0 -1
  186. package/dist/e2ee/constants.d.ts +0 -9
  187. package/dist/e2ee/constants.js +0 -18
  188. package/dist/e2ee/errors.d.ts +0 -5
  189. package/dist/e2ee/errors.js +0 -8
  190. package/dist/e2ee/framing.d.ts +0 -12
  191. package/dist/e2ee/framing.js +0 -57
  192. package/dist/e2ee/handshake.d.ts +0 -88
  193. package/dist/e2ee/handshake.js +0 -353
  194. package/dist/e2ee/index.d.ts +0 -7
  195. package/dist/e2ee/index.js +0 -7
  196. package/dist/e2ee/kdf.d.ts +0 -15
  197. package/dist/e2ee/kdf.js +0 -39
  198. package/dist/e2ee/record.d.ts +0 -11
  199. package/dist/e2ee/record.js +0 -76
  200. package/dist/e2ee/secureChannel.d.ts +0 -92
  201. package/dist/e2ee/secureChannel.js +0 -328
  202. package/dist/e2ee/transcript.d.ts +0 -23
  203. package/dist/e2ee/transcript.js +0 -31
  204. package/dist/endpoint/index.d.ts +0 -84
  205. package/dist/endpoint/index.js +0 -557
  206. package/dist/endpoint/node.d.ts +0 -9
  207. package/dist/endpoint/node.js +0 -51
  208. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
  209. package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
  210. package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
  211. package/dist/gen/flowersec/direct/v1.gen.js +0 -101
  212. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
  213. package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
  214. package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
  215. package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
  216. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
  217. package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
  218. package/dist/index.d.ts +0 -22
  219. package/dist/index.js +0 -22
  220. package/dist/node/connect.d.ts +0 -12
  221. package/dist/node/connect.js +0 -29
  222. package/dist/node/reconnectConfig.d.ts +0 -16
  223. package/dist/node/reconnectConfig.js +0 -20
  224. package/dist/observability/index.d.ts +0 -1
  225. package/dist/observability/index.js +0 -1
  226. package/dist/observability/observer.d.ts +0 -52
  227. package/dist/observability/observer.js +0 -314
  228. package/dist/proxy/appWindow.d.ts +0 -37
  229. package/dist/proxy/appWindow.js +0 -273
  230. package/dist/proxy/bootstrap.d.ts +0 -35
  231. package/dist/proxy/bootstrap.js +0 -119
  232. package/dist/proxy/constants.d.ts +0 -7
  233. package/dist/proxy/constants.js +0 -8
  234. package/dist/proxy/controllerWindow.d.ts +0 -12
  235. package/dist/proxy/controllerWindow.js +0 -384
  236. package/dist/proxy/cookieJar.d.ts +0 -7
  237. package/dist/proxy/cookieJar.js +0 -136
  238. package/dist/proxy/headerPolicy.d.ts +0 -14
  239. package/dist/proxy/headerPolicy.js +0 -134
  240. package/dist/proxy/portStream.d.ts +0 -5
  241. package/dist/proxy/portStream.js +0 -256
  242. package/dist/proxy/preset.d.ts +0 -29
  243. package/dist/proxy/preset.js +0 -111
  244. package/dist/proxy/runtimeScope.d.ts +0 -57
  245. package/dist/proxy/runtimeScope.js +0 -218
  246. package/dist/proxy/server.d.ts +0 -28
  247. package/dist/proxy/server.js +0 -833
  248. package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
  249. package/dist/proxy/windowBridgeProtocol.js +0 -11
  250. package/dist/reconnect/artifactControlplane.d.ts +0 -16
  251. package/dist/reconnect/artifactControlplane.js +0 -35
  252. package/dist/reconnect/index.d.ts +0 -36
  253. package/dist/reconnect/index.js +0 -388
  254. package/dist/rpc-proxy/index.d.ts +0 -1
  255. package/dist/rpc-proxy/index.js +0 -1
  256. package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
  257. package/dist/rpc-proxy/rpcProxy.js +0 -1
  258. package/dist/streamhello/index.d.ts +0 -1
  259. package/dist/streamhello/index.js +0 -1
  260. package/dist/streamhello/streamHello.d.ts +0 -3
  261. package/dist/streamhello/streamHello.js +0 -13
  262. package/dist/streamio/index.d.ts +0 -32
  263. package/dist/streamio/index.js +0 -87
  264. package/dist/tunnel-client/connect.d.ts +0 -9
  265. package/dist/tunnel-client/connect.js +0 -157
  266. package/dist/tunnel-client/index.d.ts +0 -1
  267. package/dist/tunnel-client/index.js +0 -1
  268. package/dist/ws/index.d.ts +0 -1
  269. package/dist/ws/index.js +0 -1
  270. /package/dist/{client.js → rpc/wire.js} +0 -0
@@ -1,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>;