@floegence/flowersec-core 3.2.1 → 5.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 (145) hide show
  1. package/README.md +57 -200
  2. package/THIRD_PARTY_NOTICES.md +11 -11
  3. package/dist/browser/index.d.ts +0 -5
  4. package/dist/browser/index.js +0 -3
  5. package/dist/cli.js +93 -64
  6. package/dist/facade.d.ts +0 -17
  7. package/dist/facade.js +0 -11
  8. package/dist/generated/unicode151.d.ts +2 -0
  9. package/dist/generated/unicode151.js +83 -0
  10. package/dist/interop/proxyServerPeer.js +34 -20
  11. package/dist/interop/serverParityPeer.js +11 -252
  12. package/dist/node/acceptor.d.ts +1 -57
  13. package/dist/node/acceptor.js +3 -420
  14. package/dist/node/acceptorV3.js +2 -2
  15. package/dist/node/connectSessionV3.js +2 -2
  16. package/dist/node/index.d.ts +2 -17
  17. package/dist/node/index.js +1 -9
  18. package/dist/node/nativeTransportAddon.d.ts +7 -36
  19. package/dist/node/nativeTransportAddon.js +7 -27
  20. package/dist/node/proxyServer.d.ts +2 -2
  21. package/dist/node/proxyServer.js +2 -2
  22. package/dist/node/rawQuicAdapterV3.d.ts +2 -2
  23. package/dist/node/rawQuicAdapterV3.js +1 -1
  24. package/dist/node/rawQuicServerV3.d.ts +2 -2
  25. package/dist/node/rawQuicServerV3.js +1 -1
  26. package/dist/node/rawQuicTls.d.ts +2 -0
  27. package/dist/node/{rawQuicAdapter.js → rawQuicTls.js} +30 -45
  28. package/dist/node/tunnelRuntimeV3.js +2 -2
  29. package/dist/proxy/index.d.ts +1 -1
  30. package/dist/proxy/index.js +1 -1
  31. package/dist/proxy/integration.d.ts +5 -5
  32. package/dist/proxy/integration.js +2 -2
  33. package/dist/proxy/runtime.js +11 -26
  34. package/dist/proxy/serviceWorkerRuntime.d.ts +7 -0
  35. package/dist/proxy/serviceWorkerRuntime.js +75 -0
  36. package/dist/proxy/types.d.ts +0 -1
  37. package/dist/proxy/windowBridge.d.ts +2 -0
  38. package/dist/proxy/windowBridge.js +53 -6
  39. package/dist/public/contract.d.ts +1 -3
  40. package/dist/public/streamHandlers.d.ts +0 -3
  41. package/dist/public/streamHandlers.js +2 -4
  42. package/dist/public/streamMetadata.js +2 -2
  43. package/dist/runtime/errors.d.ts +2 -2
  44. package/dist/testSupport/tlsFixture.d.ts +2 -0
  45. package/dist/testSupport/tlsFixture.js +8 -0
  46. package/dist/utils/errors.d.ts +0 -2
  47. package/dist/utils/errors.js +0 -80
  48. package/dist/v3/artifact.js +17 -3
  49. package/dist/v3/carrier.d.ts +2 -2
  50. package/dist/v3/controller.js +2 -6
  51. package/dist/v3/protocol.js +2 -103
  52. package/dist/v3/retryDisposition.d.ts +2 -2
  53. package/dist/v3/retryDisposition.js +0 -7
  54. package/dist/v3/security.d.ts +1 -10
  55. package/dist/v3/security.js +2 -9
  56. package/dist/vendor/tr46.d.ts +4 -0
  57. package/dist/vendor/tr46.js +37 -32
  58. package/package.json +19 -17
  59. package/sbom/cyclonedx.json +62 -62
  60. package/sbom/spdx.json +65 -65
  61. package/dist/browser/connectSession.d.ts +0 -13
  62. package/dist/browser/connectSession.js +0 -40
  63. package/dist/browser/runtimeCapability.d.ts +0 -13
  64. package/dist/browser/runtimeCapability.js +0 -27
  65. package/dist/browser/sessionRuntime.d.ts +0 -2
  66. package/dist/browser/sessionRuntime.js +0 -21
  67. package/dist/browser/v2.d.ts +0 -3
  68. package/dist/browser/v2.js +0 -2
  69. package/dist/browser/webTransportClient.d.ts +0 -8
  70. package/dist/browser/webTransportClient.js +0 -55
  71. package/dist/connectionController.d.ts +0 -55
  72. package/dist/connectionController.js +0 -441
  73. package/dist/connector/adapters/rawQuicCandidate.d.ts +0 -5
  74. package/dist/connector/adapters/rawQuicCandidate.js +0 -71
  75. package/dist/connector/adapters/webSocketCandidate.d.ts +0 -7
  76. package/dist/connector/adapters/webSocketCandidate.js +0 -116
  77. package/dist/connector/adapters/webTransportCandidate.d.ts +0 -5
  78. package/dist/connector/adapters/webTransportCandidate.js +0 -73
  79. package/dist/connector/admissionCommit.d.ts +0 -31
  80. package/dist/connector/admissionCommit.js +0 -239
  81. package/dist/connector/sessionAcceptor.d.ts +0 -38
  82. package/dist/connector/sessionAcceptor.js +0 -158
  83. package/dist/connector/sessionConfig.d.ts +0 -3
  84. package/dist/connector/sessionConfig.js +0 -30
  85. package/dist/connector/sessionConnector.d.ts +0 -47
  86. package/dist/connector/sessionConnector.js +0 -606
  87. package/dist/node/connectSession.d.ts +0 -23
  88. package/dist/node/connectSession.js +0 -93
  89. package/dist/node/controlplane.d.ts +0 -137
  90. package/dist/node/controlplane.js +0 -443
  91. package/dist/node/rawQuicAdapter.d.ts +0 -9
  92. package/dist/node/rawQuicServer.d.ts +0 -24
  93. package/dist/node/rawQuicServer.js +0 -37
  94. package/dist/node/runtimeCapability.d.ts +0 -14
  95. package/dist/node/runtimeCapability.js +0 -24
  96. package/dist/node/sessionRuntime.d.ts +0 -2
  97. package/dist/node/sessionRuntime.js +0 -13
  98. package/dist/node/tunnelRuntime.d.ts +0 -42
  99. package/dist/node/tunnelRuntime.js +0 -559
  100. package/dist/node/v2.d.ts +0 -11
  101. package/dist/node/v2.js +0 -6
  102. package/dist/node/webSocketServer.d.ts +0 -24
  103. package/dist/node/webSocketServer.js +0 -135
  104. package/dist/public/artifact.d.ts +0 -11
  105. package/dist/public/artifact.js +0 -44
  106. package/dist/public/artifactLease.d.ts +0 -10
  107. package/dist/public/artifactLease.js +0 -41
  108. package/dist/public/connectError.d.ts +0 -6
  109. package/dist/public/connectError.js +0 -9
  110. package/dist/transport/webSocketAdapter.d.ts +0 -31
  111. package/dist/transport/webSocketAdapter.js +0 -370
  112. package/dist/transport/webTransportAdapter.d.ts +0 -27
  113. package/dist/transport/webTransportAdapter.js +0 -534
  114. package/dist/v2/admissionError.d.ts +0 -4
  115. package/dist/v2/admissionError.js +0 -8
  116. package/dist/v2/artifact.d.ts +0 -128
  117. package/dist/v2/artifact.js +0 -1131
  118. package/dist/v2/artifactLease.d.ts +0 -1
  119. package/dist/v2/artifactLease.js +0 -7
  120. package/dist/v2/capability.d.ts +0 -29
  121. package/dist/v2/capability.js +0 -190
  122. package/dist/v2/carrier.d.ts +0 -84
  123. package/dist/v2/carrier.js +0 -411
  124. package/dist/v2/contract.d.ts +0 -46
  125. package/dist/v2/contract.js +0 -1
  126. package/dist/v2/handshake.d.ts +0 -80
  127. package/dist/v2/handshake.js +0 -470
  128. package/dist/v2/index.d.ts +0 -14
  129. package/dist/v2/index.js +0 -7
  130. package/dist/v2/opaqueArtifact.d.ts +0 -1
  131. package/dist/v2/opaqueArtifact.js +0 -2
  132. package/dist/v2/protocol.d.ts +0 -99
  133. package/dist/v2/protocol.js +0 -737
  134. package/dist/v2/publicSession.d.ts +0 -1
  135. package/dist/v2/publicSession.js +0 -281
  136. package/dist/v2/retryDisposition.d.ts +0 -16
  137. package/dist/v2/retryDisposition.js +0 -30
  138. package/dist/v2/session.d.ts +0 -310
  139. package/dist/v2/session.js +0 -2127
  140. package/dist/v2/streamLifetimeLedger.d.ts +0 -31
  141. package/dist/v2/streamLifetimeLedger.js +0 -131
  142. package/dist/v2/streamMetadata.d.ts +0 -1
  143. package/dist/v2/streamMetadata.js +0 -7
  144. package/dist/v2/unreliableMessage.d.ts +0 -43
  145. package/dist/v2/unreliableMessage.js +0 -254
@@ -1,6 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
- import { CarrierError, } from "../v2/carrier.js";
3
- export const NATIVE_TRANSPORT_CONTRACT_VERSION = 2;
2
+ import { CarrierError } from "../v3/carrier.js";
3
+ export const NATIVE_TRANSPORT_CONTRACT_VERSION = 3;
4
4
  const NATIVE_PACKAGE = "@floegence/flowersec-node-native";
5
5
  const SERVER_PARITY_NATIVE_ADDON = "FLOWERSEC_SERVER_PARITY_NATIVE_ADDON";
6
6
  export class NativeTransportUnavailableError extends Error {
@@ -45,31 +45,13 @@ export function createNativeRawQuicDriver(addon = loadNativeTransportAddon()) {
45
45
  return Object.freeze({
46
46
  async connectRawQuic(options, operation = {}) {
47
47
  const native = await settle(addon.connectRawQuic(options), operation.signal);
48
- return wrapSession(native, 2);
48
+ return wrapSession(native);
49
49
  },
50
50
  async bindRawQuic(options) {
51
51
  const native = await addon.bindRawQuic(options);
52
52
  return Object.freeze({
53
53
  address: () => native.address(),
54
- accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal), 2),
55
- async close() {
56
- await native.close();
57
- },
58
- });
59
- },
60
- });
61
- }
62
- export function createNativeRawQuicDriverV3(addon = loadNativeTransportAddon()) {
63
- return Object.freeze({
64
- async connectRawQuic(options, operation = {}) {
65
- const native = await settle(addon.connectRawQuicV3(options), operation.signal);
66
- return wrapSession(native, 3);
67
- },
68
- async bindRawQuic(options) {
69
- const native = await addon.bindRawQuicV3(options);
70
- return Object.freeze({
71
- address: () => native.address(),
72
- accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal), 3),
54
+ accept: async (operation = {}) => wrapSession(await settle(native.accept(), operation.signal)),
73
55
  async close() {
74
56
  await native.close();
75
57
  },
@@ -92,12 +74,10 @@ function isNativeTransportAddon(value) {
92
74
  }
93
75
  return contractVersion === NATIVE_TRANSPORT_CONTRACT_VERSION &&
94
76
  typeof candidate.connectRawQuic === "function" &&
95
- typeof candidate.bindRawQuic === "function" &&
96
- typeof candidate.connectRawQuicV3 === "function" &&
97
- typeof candidate.bindRawQuicV3 === "function";
77
+ typeof candidate.bindRawQuic === "function";
98
78
  }
99
- function wrapSession(native, expectedWireVersion) {
100
- if (native.wireVersion !== expectedWireVersion) {
79
+ function wrapSession(native) {
80
+ if (native.wireVersion !== 3) {
101
81
  native.abort();
102
82
  throw new NativeTransportUnavailableError();
103
83
  }
@@ -1,6 +1,6 @@
1
1
  import { StreamHandlers } from "../public/streamHandlers.js";
2
- import { SessionHandlers } from "./acceptor.js";
3
- export type StreamHandlerRegistrar = StreamHandlers | SessionHandlers;
2
+ import { SessionHandlersV3 } from "./acceptor.js";
3
+ export type StreamHandlerRegistrar = StreamHandlers | SessionHandlersV3;
4
4
  export type ProxyServerOptions = Readonly<{
5
5
  upstream: string;
6
6
  upstreamOrigin: string;
@@ -3,7 +3,7 @@ import { SessionError } from "../public/contract.js";
3
3
  import { writeJsonFrame } from "../framing/jsonframe.js";
4
4
  import { ProxyByteReader, writeAll } from "../proxy/stream.js";
5
5
  import { StreamHandlers, registerStreamHandlersAtomically, } from "../public/streamHandlers.js";
6
- import { SessionHandlers, registerSessionStreamHandlersAtomically, } from "./acceptor.js";
6
+ import { SessionHandlersV3, registerSessionStreamHandlersAtomically, } from "./acceptor.js";
7
7
  const HTTP_KIND = "flowersec-proxy/http1";
8
8
  const WS_KIND = "flowersec-proxy/ws";
9
9
  const WIRE_VERSION = 1;
@@ -47,7 +47,7 @@ export class ProxyServer {
47
47
  if (handlers instanceof StreamHandlers) {
48
48
  registerStreamHandlersAtomically(handlers, registrations);
49
49
  }
50
- else if (handlers instanceof SessionHandlers) {
50
+ else if (handlers instanceof SessionHandlersV3) {
51
51
  registerSessionStreamHandlersAtomically(handlers, registrations);
52
52
  }
53
53
  else {
@@ -1,8 +1,8 @@
1
1
  import type { ArtifactV3, CanonicalArtifactCandidateV3 } from "../v3/artifact.js";
2
2
  import type { NativeCarrierSessionV3 } from "../v3/carrier.js";
3
- import type { NativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
3
+ import type { NativeRawQuicDriver } from "./nativeTransportAddon.js";
4
4
  type NodeTLSRootsV3 = string | Uint8Array | readonly (string | Uint8Array)[];
5
- export declare function createNodeRawQuicClientV3(driver: NativeRawQuicDriverV3, candidate: CanonicalArtifactCandidateV3, artifact: ArtifactV3, attemptNowUnixSeconds: number, options?: Readonly<{
5
+ export declare function createNodeRawQuicClientV3(driver: NativeRawQuicDriver, candidate: CanonicalArtifactCandidateV3, artifact: ArtifactV3, attemptNowUnixSeconds: number, options?: Readonly<{
6
6
  roots?: NodeTLSRootsV3;
7
7
  signal?: AbortSignal;
8
8
  handshakeTimeoutMs?: number;
@@ -1,6 +1,6 @@
1
1
  import { rootCertificates } from "node:tls";
2
2
  import { snapshotTransportSecurityPolicyV3, TransportFailureV3, } from "../v3/security.js";
3
- import { normalizeCertificateChain } from "./rawQuicAdapter.js";
3
+ import { normalizeCertificateChain } from "./rawQuicTls.js";
4
4
  import { alpnForPathV3 } from "../v3/transportConstants.js";
5
5
  const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
6
6
  export async function createNodeRawQuicClientV3(driver, candidate, artifact, attemptNowUnixSeconds, options = {}) {
@@ -1,6 +1,6 @@
1
1
  import { type CarrierSessionV3 } from "../v3/carrier.js";
2
2
  import type { PathKind } from "../v3/contract.js";
3
- import type { NativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
3
+ import type { NativeRawQuicDriver } from "./nativeTransportAddon.js";
4
4
  export type NodeRawQuicListenerOptionsV3 = Readonly<{
5
5
  host: string;
6
6
  port: number;
@@ -21,4 +21,4 @@ export type NodeRawQuicListenerV3 = Readonly<{
21
21
  }>): Promise<CarrierSessionV3>;
22
22
  close(): Promise<void>;
23
23
  }>;
24
- export declare function startNodeRawQuicListenerV3(driver: NativeRawQuicDriverV3, options: NodeRawQuicListenerOptionsV3): Promise<NodeRawQuicListenerV3>;
24
+ export declare function startNodeRawQuicListenerV3(driver: NativeRawQuicDriver, options: NodeRawQuicListenerOptionsV3): Promise<NodeRawQuicListenerV3>;
@@ -1,5 +1,5 @@
1
1
  import { adaptNativeCarrierSessionV3 } from "../v3/carrier.js";
2
- import { normalizeCertificateChain, normalizePrivateKey } from "./rawQuicAdapter.js";
2
+ import { normalizeCertificateChain, normalizePrivateKey } from "./rawQuicTls.js";
3
3
  export async function startNodeRawQuicListenerV3(driver, options) {
4
4
  validateOptions(options);
5
5
  let listener;
@@ -0,0 +1,2 @@
1
+ export declare function normalizeCertificateChain(input: string | Uint8Array | readonly (string | Uint8Array)[]): readonly Uint8Array[];
2
+ export declare function normalizePrivateKey(input: string | Uint8Array): Uint8Array;
@@ -1,25 +1,8 @@
1
1
  import { X509Certificate, createPrivateKey } from "node:crypto";
2
- const DEFAULT_HANDSHAKE_TIMEOUT_MS = 10_000;
3
2
  const CERTIFICATE_PEM_BEGIN = "-----BEGIN CERTIFICATE-----";
4
3
  const CERTIFICATE_PEM_END = "-----END CERTIFICATE-----";
5
4
  const MAX_CERTIFICATE_PEM_BYTES = 256 * 1024;
6
5
  const MAX_CERTIFICATE_CHAIN_LENGTH = 32;
7
- export async function createNodeRawQuicClientV2(driver, candidate, artifact, tls, signal, handshakeTimeoutMs = DEFAULT_HANDSHAKE_TIMEOUT_MS) {
8
- const url = new URL(candidate.normalized_url);
9
- if (candidate.carrier !== "raw_quic" || url.protocol !== "quic:" || url.port === "") {
10
- throw new TypeError("invalid raw QUIC candidate");
11
- }
12
- const options = {
13
- host: unbracket(url.hostname),
14
- port: Number(url.port),
15
- serverName: unbracket(url.hostname),
16
- path: artifact.path.kind,
17
- trustRootsDer: normalizeCertificateChain(tls.ca),
18
- inboundBidirectionalStreamCapacity: artifact.session.max_inbound_streams + 2,
19
- handshakeTimeoutMs,
20
- };
21
- return await driver.connectRawQuic(options, { signal });
22
- }
23
6
  export function normalizeCertificateChain(input) {
24
7
  const values = Array.isArray(input) ? input : [input];
25
8
  const certificates = [];
@@ -28,32 +11,46 @@ export function normalizeCertificateChain(input) {
28
11
  if (typeof value !== "string" && !(value instanceof Uint8Array)) {
29
12
  throw new TypeError("invalid raw QUIC certificate");
30
13
  }
31
- totalInputBytes += typeof value === "string" ? Buffer.byteLength(value, "utf8") : value.byteLength;
32
- if (totalInputBytes > MAX_CERTIFICATE_PEM_BYTES)
14
+ totalInputBytes += typeof value === "string"
15
+ ? Buffer.byteLength(value, "utf8")
16
+ : value.byteLength;
17
+ if (totalInputBytes > MAX_CERTIFICATE_PEM_BYTES) {
33
18
  throw new TypeError("invalid raw QUIC certificate");
19
+ }
34
20
  if (typeof value === "string") {
35
21
  const blocks = splitCertificatePEM(value);
36
- if (blocks.length === 0)
37
- throw new TypeError("invalid raw QUIC certificate");
38
- if (certificates.length + blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
22
+ if (blocks.length === 0 || certificates.length + blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
39
23
  throw new TypeError("invalid raw QUIC certificate");
40
24
  }
41
25
  for (const block of blocks)
42
26
  certificates.push(parseCertificate(block));
43
27
  }
44
- else if (value instanceof Uint8Array && value.length > 0) {
45
- if (certificates.length >= MAX_CERTIFICATE_CHAIN_LENGTH)
28
+ else if (value.length > 0) {
29
+ if (certificates.length >= MAX_CERTIFICATE_CHAIN_LENGTH) {
46
30
  throw new TypeError("invalid raw QUIC certificate");
31
+ }
47
32
  certificates.push(parseCertificate(value));
48
33
  }
49
34
  else {
50
35
  throw new TypeError("invalid raw QUIC certificate");
51
36
  }
52
37
  }
53
- if (certificates.length === 0)
38
+ if (certificates.length === 0) {
54
39
  throw new TypeError("raw QUIC requires explicit trust roots");
40
+ }
55
41
  return certificates;
56
42
  }
43
+ export function normalizePrivateKey(input) {
44
+ if (input.length === 0)
45
+ throw new TypeError("invalid raw QUIC private key");
46
+ try {
47
+ const key = createPrivateKey(input);
48
+ return new Uint8Array(key.export({ format: "der", type: "pkcs8" }));
49
+ }
50
+ catch {
51
+ throw new TypeError("invalid raw QUIC private key");
52
+ }
53
+ }
57
54
  function splitCertificatePEM(value) {
58
55
  if (Buffer.byteLength(value, "utf8") > MAX_CERTIFICATE_PEM_BYTES) {
59
56
  throw new TypeError("invalid raw QUIC certificate");
@@ -63,37 +60,28 @@ function splitCertificatePEM(value) {
63
60
  while (cursor < value.length) {
64
61
  const begin = value.indexOf(CERTIFICATE_PEM_BEGIN, cursor);
65
62
  if (begin < 0) {
66
- if (value.slice(cursor).trim().length !== 0)
63
+ if (value.slice(cursor).trim().length !== 0) {
67
64
  throw new TypeError("invalid raw QUIC certificate");
65
+ }
68
66
  break;
69
67
  }
70
- if (value.slice(cursor, begin).trim().length !== 0)
68
+ if (value.slice(cursor, begin).trim().length !== 0) {
71
69
  throw new TypeError("invalid raw QUIC certificate");
70
+ }
72
71
  const contentStart = begin + CERTIFICATE_PEM_BEGIN.length;
73
72
  const end = value.indexOf(CERTIFICATE_PEM_END, contentStart);
74
- if (end < 0 || value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart) >= 0 && value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart) < end) {
73
+ const nested = value.indexOf(CERTIFICATE_PEM_BEGIN, contentStart);
74
+ if (end < 0 || nested >= 0 && nested < end) {
75
75
  throw new TypeError("invalid raw QUIC certificate");
76
76
  }
77
77
  blocks.push(value.slice(begin, end + CERTIFICATE_PEM_END.length));
78
- if (blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH)
78
+ if (blocks.length > MAX_CERTIFICATE_CHAIN_LENGTH) {
79
79
  throw new TypeError("invalid raw QUIC certificate");
80
+ }
80
81
  cursor = end + CERTIFICATE_PEM_END.length;
81
82
  }
82
83
  return blocks;
83
84
  }
84
- export function normalizePrivateKey(input) {
85
- if ((typeof input === "string" && input.length === 0) ||
86
- (input instanceof Uint8Array && input.length === 0)) {
87
- throw new TypeError("invalid raw QUIC private key");
88
- }
89
- try {
90
- const key = createPrivateKey(input);
91
- return new Uint8Array(key.export({ format: "der", type: "pkcs8" }));
92
- }
93
- catch {
94
- throw new TypeError("invalid raw QUIC private key");
95
- }
96
- }
97
85
  function parseCertificate(input) {
98
86
  try {
99
87
  return new Uint8Array(new X509Certificate(input).raw);
@@ -102,6 +90,3 @@ function parseCertificate(input) {
102
90
  throw new TypeError("invalid raw QUIC certificate");
103
91
  }
104
92
  }
105
- function unbracket(host) {
106
- return host.startsWith("[") && host.endsWith("]") ? host.slice(1, -1) : host;
107
- }
@@ -2,7 +2,7 @@ import { createAdmissionReasonRegistryV3, receiveSessionAdmissionV3, rejectSessi
2
2
  import { configureServerWebSocketCarrierRoleV3 } from "../v3/webSocketCarrier.js";
3
3
  import { AdmissionStatusV3, encodeFSA3ResponseV3, } from "../v3/artifact.js";
4
4
  import { startNodeWebSocketListenerV3 } from "./webSocketServerV3.js";
5
- import { createNativeRawQuicDriverV3 } from "./nativeTransportAddon.js";
5
+ import { createNativeRawQuicDriver } from "./nativeTransportAddon.js";
6
6
  import { startNodeRawQuicListenerV3 } from "./rawQuicServerV3.js";
7
7
  import { consumeTunnelAuthorizationGrantV3, inspectTunnelAuthorizationGrantV3, retireTunnelAuthorizationGrantV3, runtimeAuthorizationRequestV3FromDecoded, validateTunnelAuthorizationGrantV3, } from "./runtimeAuthorizationV3.js";
8
8
  const DEFAULT_PAIR_TIMEOUT_MS = 10_000;
@@ -107,7 +107,7 @@ async function startTunnelRuntimeV3(state) {
107
107
  pendingSessionTimeoutMs: state.limits.admissionTimeoutMs,
108
108
  cleanupTimeoutMs: state.limits.cleanupTimeoutMs,
109
109
  })
110
- : await startNodeRawQuicListenerV3(rawQuicDriver ??= createNativeRawQuicDriverV3(), {
110
+ : await startNodeRawQuicListenerV3(rawQuicDriver ??= createNativeRawQuicDriver(), {
111
111
  ...listener,
112
112
  path: "tunnel",
113
113
  inboundBidirectionalStreamCapacity: state.options.maxInboundStreams + 2,
@@ -7,7 +7,7 @@ export { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.j
7
7
  export type { RegisterServiceWorkerOptions } from "./registerServiceWorker.js";
8
8
  export { createServiceWorkerControllerGuard } from "./controllerGuard.js";
9
9
  export type { ServiceWorkerControllerGuardConflictPolicy, ServiceWorkerControllerGuardHandle, ServiceWorkerControllerGuardMismatchContext, ServiceWorkerControllerGuardMonitorOptions, ServiceWorkerControllerGuardOptions, ServiceWorkerControllerGuardRepairOptions, } from "./controllerGuard.js";
10
- export { registerProxyAppWindow, registerProxyAppWindowWithServiceWorkerControl, registerProxyControllerWindow, } from "./windowBridge.js";
10
+ export { registerProxyAppWindow, registerProxyAppWindowWithServiceWorkerControl, registerProxyAppWindowWithServiceWorkerRuntime, registerProxyControllerWindow, } from "./windowBridge.js";
11
11
  export type { ProxyAppServiceWorkerControlOptions, ProxyAppWindowHandle, ProxyControllerWindowHandle, RegisterProxyAppWindowOptions, RegisterProxyAppWindowWithServiceWorkerControlOptions, RegisterProxyControllerWindowOptions, } from "./windowBridge.js";
12
12
  export { installWebSocketPatch } from "./wsPatch.js";
13
13
  export type { WebSocketPatchOptions } from "./wsPatch.js";
@@ -3,7 +3,7 @@ export { createProxyRuntime, ensureServiceWorkerRuntimeRegistered } from "./runt
3
3
  export { createProxyServiceWorkerScript } from "./serviceWorker.js";
4
4
  export { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
5
5
  export { createServiceWorkerControllerGuard } from "./controllerGuard.js";
6
- export { registerProxyAppWindow, registerProxyAppWindowWithServiceWorkerControl, registerProxyControllerWindow, } from "./windowBridge.js";
6
+ export { registerProxyAppWindow, registerProxyAppWindowWithServiceWorkerControl, registerProxyAppWindowWithServiceWorkerRuntime, registerProxyControllerWindow, } from "./windowBridge.js";
7
7
  export { installWebSocketPatch } from "./wsPatch.js";
8
8
  export { disableUpstreamServiceWorkerRegister } from "./disableUpstreamServiceWorkerRegister.js";
9
9
  export { connectProxyBrowser, connectProxyControllerBrowser, } from "./integration.js";
@@ -1,10 +1,10 @@
1
- import { type SessionOptions } from "../browser/connectSession.js";
2
- import type { ArtifactLease } from "../public/artifactLease.js";
1
+ import { type SessionOptionsV3 } from "../browser/connectSessionV3.js";
2
+ import type { ArtifactLeaseV3 } from "../v3/artifactLease.js";
3
3
  import type { Session } from "../public/contract.js";
4
4
  import type { ProxyRuntime, ProxyRuntimeOptions } from "./types.js";
5
5
  import { type ProxyControllerWindowHandle, type RegisterProxyControllerWindowOptions } from "./windowBridge.js";
6
6
  export type ProxyBrowserConnectOptions = Readonly<{
7
- connect?: SessionOptions;
7
+ connect?: SessionOptionsV3;
8
8
  runtime?: Omit<ProxyRuntimeOptions, "session">;
9
9
  serviceWorker?: Readonly<{
10
10
  scriptUrl: string;
@@ -19,11 +19,11 @@ export type ProxyBrowserHandle = Readonly<{
19
19
  runtime: ProxyRuntime;
20
20
  dispose(): Promise<void>;
21
21
  }>;
22
- export declare function connectProxyBrowser(lease: ArtifactLease, options?: ProxyBrowserConnectOptions): Promise<ProxyBrowserHandle>;
22
+ export declare function connectProxyBrowser(lease: ArtifactLeaseV3, options?: ProxyBrowserConnectOptions): Promise<ProxyBrowserHandle>;
23
23
  export type ProxyControllerBrowserConnectOptions = ProxyBrowserConnectOptions & Readonly<{
24
24
  controller: Omit<RegisterProxyControllerWindowOptions, "runtime">;
25
25
  }>;
26
26
  export type ProxyControllerBrowserHandle = ProxyBrowserHandle & Readonly<{
27
27
  controller: ProxyControllerWindowHandle;
28
28
  }>;
29
- export declare function connectProxyControllerBrowser(lease: ArtifactLease, options: ProxyControllerBrowserConnectOptions): Promise<ProxyControllerBrowserHandle>;
29
+ export declare function connectProxyControllerBrowser(lease: ArtifactLeaseV3, options: ProxyControllerBrowserConnectOptions): Promise<ProxyControllerBrowserHandle>;
@@ -1,9 +1,9 @@
1
- import { connect } from "../browser/connectSession.js";
1
+ import { connectV3 } from "../browser/connectSessionV3.js";
2
2
  import { createProxyRuntime } from "./runtime.js";
3
3
  import { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
4
4
  import { registerProxyControllerWindow, } from "./windowBridge.js";
5
5
  export async function connectProxyBrowser(lease, options = {}) {
6
- const session = await connect(lease, options.connect);
6
+ const session = await connectV3(lease, options.connect);
7
7
  let runtime;
8
8
  try {
9
9
  if (options.serviceWorker !== undefined)
@@ -5,6 +5,7 @@ import { readU32be, u32be } from "../utils/bin.js";
5
5
  import { SessionError } from "../public/contract.js";
6
6
  import { createStreamMetadata } from "../public/streamMetadata.js";
7
7
  import { ProxyByteReader, writeAll } from "./stream.js";
8
+ import { registerProxyRuntimeServiceWorkerBridge, usesServiceWorkerResponseFlowControl, } from "./serviceWorkerRuntime.js";
8
9
  const PROXY_HTTP_STREAM_KIND = "flowersec-proxy/http1";
9
10
  const PROXY_WEBSOCKET_STREAM_KIND = "flowersec-proxy/ws";
10
11
  const PROXY_WIRE_VERSION = 1;
@@ -160,19 +161,6 @@ function filterHeaders(input, base, extra) {
160
161
  }
161
162
  return result;
162
163
  }
163
- function parseRuntimeRequest(raw) {
164
- const headers = Array.isArray(raw.headers) ? raw.headers.filter((entry) => typeof entry === "object" && entry !== null && typeof entry.name === "string" && typeof entry.value === "string") : [];
165
- const body = raw.body instanceof ArrayBuffer ? raw.body : undefined;
166
- return {
167
- id: typeof raw.id === "string" ? raw.id : "",
168
- method: typeof raw.method === "string" ? raw.method : "GET",
169
- path: typeof raw.path === "string" ? raw.path : "",
170
- headers,
171
- ...(typeof raw.external_origin === "string" ? { externalOrigin: raw.external_origin } : {}),
172
- ...(raw.response_flow_control === "chunk_credit_v2" ? { responseFlowControl: "chunk_credit_v2" } : {}),
173
- ...(body === undefined ? {} : { body }),
174
- };
175
- }
176
164
  class StreamAdmission {
177
165
  concurrent;
178
166
  queued;
@@ -317,22 +305,15 @@ export function createProxyRuntime(options) {
317
305
  ...(runtimeRegistrationToken === undefined ? {} : { runtimeRegistrationToken }),
318
306
  }).catch(() => undefined);
319
307
  const serviceWorker = globalThis.navigator?.serviceWorker;
320
- const onMessage = (event) => {
321
- if (event.data === null || typeof event.data !== "object" || event.data.type !== "flowersec-proxy:fetch")
322
- return;
323
- const port = event.ports?.[0];
324
- if (port === undefined)
325
- return;
326
- dispatchFetch(parseRuntimeRequest(event.data.req), port);
327
- };
328
- serviceWorker?.addEventListener("message", onMessage);
329
308
  serviceWorker?.addEventListener("controllerchange", register);
330
309
  register();
310
+ let serviceWorkerBridge;
331
311
  function dispatchFetch(request, port) {
332
312
  const controller = new AbortController();
333
313
  let stream;
334
314
  let release;
335
- let credit = request.responseFlowControl !== "chunk_credit_v2";
315
+ const flowControlled = usesServiceWorkerResponseFlowControl(request);
316
+ let credit = !flowControlled;
336
317
  let creditWake;
337
318
  port.onmessage = (event) => {
338
319
  if (event.data?.type === "flowersec-proxy:abort") {
@@ -351,7 +332,7 @@ export function createProxyRuntime(options) {
351
332
  await new Promise((resolve) => { creditWake = resolve; });
352
333
  creditWake = undefined;
353
334
  }
354
- credit = request.responseFlowControl !== "chunk_credit_v2";
335
+ credit = !flowControlled;
355
336
  };
356
337
  void (async () => {
357
338
  try {
@@ -439,7 +420,7 @@ export function createProxyRuntime(options) {
439
420
  throw error instanceof SessionError ? error : new SessionError("operation_failed");
440
421
  }
441
422
  }
442
- return Object.freeze({
423
+ const runtime = Object.freeze({
443
424
  limits: Object.freeze({
444
425
  maxJsonFrameBytes,
445
426
  maxChunkBytes,
@@ -457,10 +438,14 @@ export function createProxyRuntime(options) {
457
438
  return;
458
439
  disposed = true;
459
440
  admission.close();
460
- serviceWorker?.removeEventListener("message", onMessage);
461
441
  serviceWorker?.removeEventListener("controllerchange", register);
442
+ serviceWorkerBridge?.dispose();
462
443
  },
463
444
  });
445
+ if (serviceWorker !== undefined) {
446
+ serviceWorkerBridge = registerProxyRuntimeServiceWorkerBridge(runtime, serviceWorker);
447
+ }
448
+ return runtime;
464
449
  }
465
450
  export async function ensureServiceWorkerRuntimeRegistered(options = {}) {
466
451
  const controller = globalThis.navigator?.serviceWorker?.controller;
@@ -0,0 +1,7 @@
1
+ import type { ProxyFetchRequest, ProxyRuntime } from "./types.js";
2
+ type ProxyRuntimeServiceWorkerBridgeHandle = Readonly<{
3
+ dispose(): void;
4
+ }>;
5
+ export declare function usesServiceWorkerResponseFlowControl(request: ProxyFetchRequest): boolean;
6
+ export declare function registerProxyRuntimeServiceWorkerBridge(runtime: ProxyRuntime, serviceWorker: ServiceWorkerContainer): ProxyRuntimeServiceWorkerBridgeHandle;
7
+ export {};
@@ -0,0 +1,75 @@
1
+ const responseFlowControl = new WeakSet();
2
+ function record(value) {
3
+ return value !== null && typeof value === "object" && !Array.isArray(value)
4
+ ? value
5
+ : undefined;
6
+ }
7
+ function parseRuntimeRequest(value) {
8
+ const raw = record(value);
9
+ if (raw === undefined || typeof raw.id !== "string" || typeof raw.method !== "string" ||
10
+ typeof raw.path !== "string" || !Array.isArray(raw.headers)) {
11
+ throw new TypeError("invalid proxy service worker request");
12
+ }
13
+ const headers = raw.headers.map((value) => {
14
+ const header = record(value);
15
+ if (header === undefined || typeof header.name !== "string" || typeof header.value !== "string") {
16
+ throw new TypeError("invalid proxy service worker request headers");
17
+ }
18
+ return Object.freeze({ name: header.name, value: header.value });
19
+ });
20
+ if (raw.body !== undefined && !(raw.body instanceof ArrayBuffer)) {
21
+ throw new TypeError("invalid proxy service worker request body");
22
+ }
23
+ if (raw.external_origin !== undefined && typeof raw.external_origin !== "string") {
24
+ throw new TypeError("invalid proxy service worker request origin");
25
+ }
26
+ if (raw.response_flow_control !== undefined && raw.response_flow_control !== "chunk_credit_v2") {
27
+ throw new TypeError("invalid proxy service worker response flow control");
28
+ }
29
+ const request = Object.freeze({
30
+ id: raw.id,
31
+ method: raw.method,
32
+ path: raw.path,
33
+ headers: Object.freeze(headers),
34
+ ...(typeof raw.external_origin === "string" ? { externalOrigin: raw.external_origin } : {}),
35
+ ...(raw.body instanceof ArrayBuffer ? { body: raw.body } : {}),
36
+ });
37
+ if (raw.response_flow_control === "chunk_credit_v2")
38
+ responseFlowControl.add(request);
39
+ return request;
40
+ }
41
+ export function usesServiceWorkerResponseFlowControl(request) {
42
+ return responseFlowControl.has(request);
43
+ }
44
+ export function registerProxyRuntimeServiceWorkerBridge(runtime, serviceWorker) {
45
+ let disposed = false;
46
+ const onMessage = (event) => {
47
+ const message = record(event.data);
48
+ if (message?.type !== "flowersec-proxy:fetch")
49
+ return;
50
+ const port = event.ports?.[0];
51
+ if (port === undefined)
52
+ return;
53
+ try {
54
+ runtime.dispatchFetch(parseRuntimeRequest(message.req), port);
55
+ }
56
+ catch {
57
+ port.postMessage({
58
+ type: "flowersec-proxy:response_error",
59
+ status: 400,
60
+ code: "invalid_request",
61
+ message: "invalid proxy request",
62
+ });
63
+ port.close();
64
+ }
65
+ };
66
+ serviceWorker.addEventListener("message", onMessage);
67
+ return Object.freeze({
68
+ dispose: () => {
69
+ if (disposed)
70
+ return;
71
+ disposed = true;
72
+ serviceWorker.removeEventListener("message", onMessage);
73
+ },
74
+ });
75
+ }
@@ -43,7 +43,6 @@ export type ProxyFetchRequest = Readonly<{
43
43
  path: string;
44
44
  headers: readonly ProxyHeader[];
45
45
  externalOrigin?: string;
46
- responseFlowControl?: "chunk_credit_v2";
47
46
  body?: ArrayBuffer;
48
47
  }>;
49
48
  export type ProxyRuntime = Readonly<{
@@ -38,6 +38,7 @@ export type ProxyAppWindowHandle = Readonly<{
38
38
  dispose(): void;
39
39
  }>;
40
40
  export declare function registerProxyAppWindow(options: RegisterProxyAppWindowOptions): ProxyAppWindowHandle;
41
+ export declare function registerProxyAppWindowWithServiceWorkerRuntime(options: RegisterProxyAppWindowOptions): ProxyAppWindowHandle;
41
42
  export type RegisterProxyControllerWindowOptions = Readonly<{
42
43
  runtime: ProxyRuntime;
43
44
  allowedOrigins: readonly string[];
@@ -56,6 +57,7 @@ export type ProxyAppServiceWorkerControlOptions = Readonly<{
56
57
  repair?: ServiceWorkerControllerGuardRepairOptions;
57
58
  monitor?: ServiceWorkerControllerGuardMonitorOptions;
58
59
  conflicts?: ServiceWorkerControllerGuardConflictPolicy;
60
+ runtimeRegistrationToken?: string;
59
61
  }>;
60
62
  export type RegisterProxyAppWindowWithServiceWorkerControlOptions = RegisterProxyAppWindowOptions & Readonly<{
61
63
  serviceWorker: ProxyAppServiceWorkerControlOptions;
@@ -2,6 +2,8 @@ import { SDK_DEFAULTS } from "../defaults.js";
2
2
  import { SessionError } from "../public/contract.js";
3
3
  import { createServiceWorkerControllerGuard, } from "./controllerGuard.js";
4
4
  import { registerServiceWorkerAndEnsureControl } from "./registerServiceWorker.js";
5
+ import { ensureServiceWorkerRuntimeRegistered } from "./runtime.js";
6
+ import { registerProxyRuntimeServiceWorkerBridge } from "./serviceWorkerRuntime.js";
5
7
  const FETCH_MESSAGE = "flowersec-proxy:window_fetch_v2";
6
8
  const WEBSOCKET_OPEN_MESSAGE = "flowersec-proxy:window_ws_open_v2";
7
9
  const WEBSOCKET_ACK_MESSAGE = "flowersec-proxy:window_ws_open_ack_v2";
@@ -292,6 +294,31 @@ export function registerProxyAppWindow(options) {
292
294
  dispose: () => { disposed = true; },
293
295
  });
294
296
  }
297
+ export function registerProxyAppWindowWithServiceWorkerRuntime(options) {
298
+ const target = options.targetWindow ?? globalThis.window;
299
+ const serviceWorker = target.navigator?.serviceWorker;
300
+ if (serviceWorker === undefined)
301
+ throw new Error("proxy service worker is unavailable");
302
+ const app = registerProxyAppWindow(options);
303
+ try {
304
+ const serviceWorkerBridge = registerProxyRuntimeServiceWorkerBridge(app.runtime, serviceWorker);
305
+ let disposed = false;
306
+ return Object.freeze({
307
+ runtime: app.runtime,
308
+ dispose: () => {
309
+ if (disposed)
310
+ return;
311
+ disposed = true;
312
+ serviceWorkerBridge.dispose();
313
+ app.dispose();
314
+ },
315
+ });
316
+ }
317
+ catch (error) {
318
+ app.dispose();
319
+ throw error;
320
+ }
321
+ }
295
322
  async function bridgeStreams(runtimeStream, port, signal) {
296
323
  const bridge = new MessagePortByteStream(port);
297
324
  const controller = new AbortController();
@@ -436,10 +463,30 @@ export async function registerProxyAppWindowWithServiceWorkerControl(options) {
436
463
  ...(options.serviceWorker.monitor === undefined ? {} : { monitor: options.serviceWorker.monitor }),
437
464
  ...(options.serviceWorker.conflicts === undefined ? {} : { conflicts: options.serviceWorker.conflicts }),
438
465
  });
439
- await guard.ensure();
440
- const app = registerProxyAppWindow(options);
441
- return Object.freeze({
442
- runtime: app.runtime,
443
- dispose: () => { app.dispose(); guard.dispose(); },
444
- });
466
+ let app;
467
+ try {
468
+ await guard.ensure();
469
+ app = registerProxyAppWindowWithServiceWorkerRuntime(options);
470
+ await ensureServiceWorkerRuntimeRegistered({
471
+ ...(options.serviceWorker.runtimeRegistrationToken === undefined
472
+ ? {}
473
+ : { runtimeRegistrationToken: options.serviceWorker.runtimeRegistrationToken }),
474
+ });
475
+ let disposed = false;
476
+ return Object.freeze({
477
+ runtime: app.runtime,
478
+ dispose: () => {
479
+ if (disposed)
480
+ return;
481
+ disposed = true;
482
+ app?.dispose();
483
+ guard.dispose();
484
+ },
485
+ });
486
+ }
487
+ catch (error) {
488
+ app?.dispose();
489
+ guard.dispose();
490
+ throw error;
491
+ }
445
492
  }
@@ -26,9 +26,7 @@ export declare class UnreliableMessageError extends Error {
26
26
  export type StreamOpenOptions = OperationOptions & Readonly<{
27
27
  metadata?: StreamMetadata;
28
28
  }>;
29
- /** @deprecated Unreliable-message operations use UnreliableMessageErrorCode. */
30
- export type LegacyUnreliableSessionErrorCode = "unreliable_unavailable" | "unreliable_too_large" | "unreliable_dropped";
31
- export type SessionErrorCode = "canceled" | "timeout" | "closed" | "going_away" | "resource_exhausted" | "stream_rejected" | "stream_reset" | "rekey_failed" | "liveness_failed" | LegacyUnreliableSessionErrorCode | "operation_failed";
29
+ export type SessionErrorCode = "canceled" | "timeout" | "closed" | "going_away" | "resource_exhausted" | "stream_rejected" | "stream_reset" | "rekey_failed" | "liveness_failed" | "operation_failed";
32
30
  /** A closed, carrier-neutral session failure with no internal cause or peer detail. */
33
31
  export declare class SessionError extends Error {
34
32
  readonly code: SessionErrorCode;
@@ -18,6 +18,3 @@ export declare class StreamHandlers {
18
18
  handleStream(kind: string, handler: StreamHandler): void;
19
19
  serve(session: Session, options?: OperationOptions): Promise<void>;
20
20
  }
21
- /** Explicit-compatibility application-stream handlers. */
22
- export declare class LegacyStreamHandlers extends StreamHandlers {
23
- }