@floegence/flowersec-core 0.27.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (268) hide show
  1. package/README.md +91 -22
  2. package/THIRD_PARTY_NOTICES.md +141 -0
  3. package/dist/browser/connectSession.d.ts +13 -0
  4. package/dist/browser/connectSession.js +40 -0
  5. package/dist/browser/index.d.ts +3 -10
  6. package/dist/browser/index.js +2 -5
  7. package/dist/browser/runtimeCapability.d.ts +13 -0
  8. package/dist/browser/runtimeCapability.js +27 -0
  9. package/dist/browser/sessionRuntime.d.ts +2 -0
  10. package/dist/browser/sessionRuntime.js +21 -0
  11. package/dist/browser/webTransportClient.d.ts +8 -0
  12. package/dist/browser/webTransportClient.js +55 -0
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +126 -0
  15. package/dist/cliSpendMarker.d.ts +1 -0
  16. package/dist/cliSpendMarker.js +22 -0
  17. package/dist/connectionController.d.ts +54 -0
  18. package/dist/connectionController.js +438 -0
  19. package/dist/connector/adapters/webSocketCandidate.d.ts +7 -0
  20. package/dist/connector/adapters/webSocketCandidate.js +116 -0
  21. package/dist/connector/adapters/webTransportCandidate.d.ts +5 -0
  22. package/dist/connector/adapters/webTransportCandidate.js +73 -0
  23. package/dist/connector/admissionCommit.d.ts +31 -0
  24. package/dist/connector/admissionCommit.js +239 -0
  25. package/dist/connector/sessionAcceptor.d.ts +16 -0
  26. package/dist/connector/sessionAcceptor.js +114 -0
  27. package/dist/connector/sessionConfig.d.ts +3 -0
  28. package/dist/connector/sessionConfig.js +30 -0
  29. package/dist/connector/sessionConnector.d.ts +45 -0
  30. package/dist/connector/sessionConnector.js +603 -0
  31. package/dist/defaults.d.ts +7 -7
  32. package/dist/defaults.js +7 -7
  33. package/dist/facade.d.ts +12 -27
  34. package/dist/facade.js +6 -24
  35. package/dist/node/connectSession.d.ts +21 -0
  36. package/dist/node/connectSession.js +62 -0
  37. package/dist/node/index.d.ts +3 -11
  38. package/dist/node/index.js +2 -8
  39. package/dist/node/runtimeCapability.d.ts +7 -0
  40. package/dist/node/runtimeCapability.js +10 -0
  41. package/dist/node/sessionRuntime.d.ts +2 -0
  42. package/dist/node/sessionRuntime.js +13 -0
  43. package/dist/node/webTransportClient.d.ts +9 -0
  44. package/dist/node/webTransportClient.js +56 -0
  45. package/dist/node/webTransportServer.d.ts +22 -0
  46. package/dist/node/webTransportServer.js +100 -0
  47. package/dist/node/wsFactory.d.ts +3 -1
  48. package/dist/node/wsFactory.js +4 -3
  49. package/dist/proxy/controllerGuard.d.ts +4 -4
  50. package/dist/proxy/controllerGuard.js +83 -177
  51. package/dist/proxy/disableUpstreamServiceWorkerRegister.d.ts +3 -1
  52. package/dist/proxy/disableUpstreamServiceWorkerRegister.js +19 -17
  53. package/dist/proxy/index.d.ts +16 -15
  54. package/dist/proxy/index.js +8 -14
  55. package/dist/proxy/integration.d.ts +23 -71
  56. package/dist/proxy/integration.js +37 -340
  57. package/dist/proxy/registerServiceWorker.d.ts +1 -10
  58. package/dist/proxy/registerServiceWorker.js +43 -105
  59. package/dist/proxy/runtime.d.ts +3 -63
  60. package/dist/proxy/runtime.js +349 -462
  61. package/dist/proxy/scope.d.ts +6 -0
  62. package/dist/proxy/scope.js +131 -0
  63. package/dist/proxy/serviceWorker.d.ts +5 -16
  64. package/dist/proxy/serviceWorker.js +230 -617
  65. package/dist/proxy/stream.d.ts +9 -0
  66. package/dist/proxy/stream.js +40 -0
  67. package/dist/proxy/types.d.ts +78 -30
  68. package/dist/proxy/windowBridge.d.ts +61 -0
  69. package/dist/proxy/windowBridge.js +344 -0
  70. package/dist/proxy/wsPatch.d.ts +4 -14
  71. package/dist/proxy/wsPatch.js +173 -268
  72. package/dist/public/artifact.d.ts +11 -0
  73. package/dist/public/artifact.js +44 -0
  74. package/dist/public/artifactLease.d.ts +10 -0
  75. package/dist/public/artifactLease.js +48 -0
  76. package/dist/public/connectError.d.ts +6 -0
  77. package/dist/public/connectError.js +9 -0
  78. package/dist/public/contract.d.ts +83 -0
  79. package/dist/public/contract.js +17 -0
  80. package/dist/public/streamMetadata.d.ts +10 -0
  81. package/dist/public/streamMetadata.js +40 -0
  82. package/dist/rpc/caller.d.ts +1 -1
  83. package/dist/rpc/client.d.ts +1 -4
  84. package/dist/rpc/client.js +0 -21
  85. package/dist/rpc/rpcProxy.d.ts +1 -1
  86. package/dist/rpc/server.d.ts +1 -1
  87. package/dist/rpc/validate.d.ts +1 -1
  88. package/dist/rpc/wire.d.ts +15 -0
  89. package/dist/runtime/errors.d.ts +6 -0
  90. package/dist/runtime/errors.js +10 -0
  91. package/dist/transport/webSocketAdapter.d.ts +24 -0
  92. package/dist/transport/webSocketAdapter.js +234 -0
  93. package/dist/transport/webTransportAdapter.d.ts +27 -0
  94. package/dist/transport/webTransportAdapter.js +519 -0
  95. package/dist/utils/errors.d.ts +2 -20
  96. package/dist/utils/errors.js +73 -20
  97. package/dist/v2/admissionError.d.ts +4 -0
  98. package/dist/v2/admissionError.js +8 -0
  99. package/dist/v2/artifact.d.ts +128 -0
  100. package/dist/v2/artifact.js +1126 -0
  101. package/dist/v2/artifactLease.d.ts +1 -0
  102. package/dist/v2/artifactLease.js +7 -0
  103. package/dist/v2/capability.d.ts +29 -0
  104. package/dist/v2/capability.js +190 -0
  105. package/dist/v2/carrier.d.ts +79 -0
  106. package/dist/v2/carrier.js +405 -0
  107. package/dist/v2/contract.d.ts +37 -0
  108. package/dist/v2/contract.js +1 -0
  109. package/dist/v2/handshake.d.ts +80 -0
  110. package/dist/v2/handshake.js +470 -0
  111. package/dist/v2/opaqueArtifact.d.ts +1 -0
  112. package/dist/v2/opaqueArtifact.js +2 -0
  113. package/dist/v2/protocol.d.ts +99 -0
  114. package/dist/v2/protocol.js +739 -0
  115. package/dist/v2/publicSession.d.ts +1 -0
  116. package/dist/v2/publicSession.js +179 -0
  117. package/dist/v2/retryDisposition.d.ts +16 -0
  118. package/dist/v2/retryDisposition.js +30 -0
  119. package/dist/v2/session.d.ts +277 -0
  120. package/dist/v2/session.js +1879 -0
  121. package/dist/v2/streamLifetimeLedger.d.ts +31 -0
  122. package/dist/v2/streamLifetimeLedger.js +131 -0
  123. package/dist/v2/streamMetadata.d.ts +1 -0
  124. package/dist/v2/streamMetadata.js +7 -0
  125. package/dist/v2/unreliableMessage.d.ts +43 -0
  126. package/dist/v2/unreliableMessage.js +252 -0
  127. package/dist/vendor/tr46.d.ts +10 -0
  128. package/dist/vendor/tr46.js +501 -0
  129. package/dist/ws-client/binaryTransport.d.ts +0 -3
  130. package/dist/ws-client/binaryTransport.js +15 -39
  131. package/dist/yamux/errors.d.ts +0 -4
  132. package/dist/yamux/errors.js +0 -9
  133. package/dist/yamux/session.d.ts +0 -5
  134. package/dist/yamux/session.js +4 -70
  135. package/dist/yamux/stream.d.ts +1 -0
  136. package/dist/yamux/stream.js +4 -0
  137. package/package.json +29 -81
  138. package/sbom/cyclonedx.json +2764 -0
  139. package/sbom/spdx.json +1947 -0
  140. package/YAMUX_ALIGNMENT.md +0 -127
  141. package/dist/_examples/flowersec/demo/v1.facade.gen.d.ts +0 -12
  142. package/dist/_examples/flowersec/demo/v1.facade.gen.js +0 -15
  143. package/dist/_examples/flowersec/demo/v1.gen.d.ts +0 -16
  144. package/dist/_examples/flowersec/demo/v1.gen.js +0 -86
  145. package/dist/_examples/flowersec/demo/v1.rpc.gen.d.ts +0 -11
  146. package/dist/_examples/flowersec/demo/v1.rpc.gen.js +0 -22
  147. package/dist/browser/connect.d.ts +0 -13
  148. package/dist/browser/connect.js +0 -31
  149. package/dist/browser/reconnectConfig.d.ts +0 -15
  150. package/dist/browser/reconnectConfig.js +0 -20
  151. package/dist/client-connect/common.d.ts +0 -26
  152. package/dist/client-connect/common.js +0 -167
  153. package/dist/client-connect/connectCore.d.ts +0 -61
  154. package/dist/client-connect/connectCore.js +0 -572
  155. package/dist/client-connect/contract.d.ts +0 -8
  156. package/dist/client-connect/contract.js +0 -51
  157. package/dist/client-connect/termination.d.ts +0 -6
  158. package/dist/client-connect/termination.js +0 -7
  159. package/dist/client-connect/transportSecurity.d.ts +0 -27
  160. package/dist/client-connect/transportSecurity.js +0 -199
  161. package/dist/client-connect/tunnelAttachCloseReason.d.ts +0 -3
  162. package/dist/client-connect/tunnelAttachCloseReason.js +0 -23
  163. package/dist/client.d.ts +0 -23
  164. package/dist/connect/artifact.d.ts +0 -36
  165. package/dist/connect/artifact.js +0 -213
  166. package/dist/connect/resolveArtifact.d.ts +0 -22
  167. package/dist/connect/resolveArtifact.js +0 -87
  168. package/dist/controlplane/channelInit.d.ts +0 -27
  169. package/dist/controlplane/channelInit.js +0 -116
  170. package/dist/controlplane/http.d.ts +0 -16
  171. package/dist/controlplane/http.js +0 -59
  172. package/dist/controlplane/index.d.ts +0 -6
  173. package/dist/controlplane/index.js +0 -5
  174. package/dist/controlplane/issuer.d.ts +0 -18
  175. package/dist/controlplane/issuer.js +0 -113
  176. package/dist/controlplane/request.d.ts +0 -51
  177. package/dist/controlplane/request.js +0 -273
  178. package/dist/controlplane/token.d.ts +0 -33
  179. package/dist/controlplane/token.js +0 -119
  180. package/dist/direct-client/connect.d.ts +0 -9
  181. package/dist/direct-client/connect.js +0 -90
  182. package/dist/direct-client/index.d.ts +0 -1
  183. package/dist/direct-client/index.js +0 -1
  184. package/dist/e2ee/constants.d.ts +0 -9
  185. package/dist/e2ee/constants.js +0 -18
  186. package/dist/e2ee/errors.d.ts +0 -5
  187. package/dist/e2ee/errors.js +0 -8
  188. package/dist/e2ee/framing.d.ts +0 -12
  189. package/dist/e2ee/framing.js +0 -57
  190. package/dist/e2ee/handshake.d.ts +0 -88
  191. package/dist/e2ee/handshake.js +0 -353
  192. package/dist/e2ee/index.d.ts +0 -7
  193. package/dist/e2ee/index.js +0 -7
  194. package/dist/e2ee/kdf.d.ts +0 -15
  195. package/dist/e2ee/kdf.js +0 -39
  196. package/dist/e2ee/record.d.ts +0 -11
  197. package/dist/e2ee/record.js +0 -80
  198. package/dist/e2ee/secureChannel.d.ts +0 -93
  199. package/dist/e2ee/secureChannel.js +0 -342
  200. package/dist/e2ee/transcript.d.ts +0 -23
  201. package/dist/e2ee/transcript.js +0 -31
  202. package/dist/endpoint/index.d.ts +0 -84
  203. package/dist/endpoint/index.js +0 -557
  204. package/dist/endpoint/node.d.ts +0 -9
  205. package/dist/endpoint/node.js +0 -51
  206. package/dist/gen/flowersec/controlplane/v1.gen.d.ts +0 -36
  207. package/dist/gen/flowersec/controlplane/v1.gen.js +0 -135
  208. package/dist/gen/flowersec/direct/v1.gen.d.ts +0 -21
  209. package/dist/gen/flowersec/direct/v1.gen.js +0 -101
  210. package/dist/gen/flowersec/e2ee/v1.gen.d.ts +0 -68
  211. package/dist/gen/flowersec/e2ee/v1.gen.js +0 -194
  212. package/dist/gen/flowersec/rpc/v1.gen.d.ts +0 -30
  213. package/dist/gen/flowersec/rpc/v1.gen.js +0 -107
  214. package/dist/gen/flowersec/tunnel/v1.gen.d.ts +0 -23
  215. package/dist/gen/flowersec/tunnel/v1.gen.js +0 -104
  216. package/dist/index.d.ts +0 -22
  217. package/dist/index.js +0 -22
  218. package/dist/node/connect.d.ts +0 -10
  219. package/dist/node/connect.js +0 -29
  220. package/dist/node/reconnectConfig.d.ts +0 -16
  221. package/dist/node/reconnectConfig.js +0 -20
  222. package/dist/observability/index.d.ts +0 -1
  223. package/dist/observability/index.js +0 -1
  224. package/dist/observability/observer.d.ts +0 -52
  225. package/dist/observability/observer.js +0 -314
  226. package/dist/proxy/appWindow.d.ts +0 -37
  227. package/dist/proxy/appWindow.js +0 -273
  228. package/dist/proxy/bootstrap.d.ts +0 -35
  229. package/dist/proxy/bootstrap.js +0 -119
  230. package/dist/proxy/constants.d.ts +0 -7
  231. package/dist/proxy/constants.js +0 -8
  232. package/dist/proxy/controllerWindow.d.ts +0 -12
  233. package/dist/proxy/controllerWindow.js +0 -384
  234. package/dist/proxy/cookieJar.d.ts +0 -7
  235. package/dist/proxy/cookieJar.js +0 -136
  236. package/dist/proxy/headerPolicy.d.ts +0 -14
  237. package/dist/proxy/headerPolicy.js +0 -134
  238. package/dist/proxy/portStream.d.ts +0 -5
  239. package/dist/proxy/portStream.js +0 -256
  240. package/dist/proxy/preset.d.ts +0 -29
  241. package/dist/proxy/preset.js +0 -111
  242. package/dist/proxy/runtimeScope.d.ts +0 -57
  243. package/dist/proxy/runtimeScope.js +0 -218
  244. package/dist/proxy/server.d.ts +0 -28
  245. package/dist/proxy/server.js +0 -833
  246. package/dist/proxy/windowBridgeProtocol.d.ts +0 -65
  247. package/dist/proxy/windowBridgeProtocol.js +0 -11
  248. package/dist/reconnect/artifactControlplane.d.ts +0 -16
  249. package/dist/reconnect/artifactControlplane.js +0 -35
  250. package/dist/reconnect/index.d.ts +0 -36
  251. package/dist/reconnect/index.js +0 -388
  252. package/dist/rpc-proxy/index.d.ts +0 -1
  253. package/dist/rpc-proxy/index.js +0 -1
  254. package/dist/rpc-proxy/rpcProxy.d.ts +0 -1
  255. package/dist/rpc-proxy/rpcProxy.js +0 -1
  256. package/dist/streamhello/index.d.ts +0 -1
  257. package/dist/streamhello/index.js +0 -1
  258. package/dist/streamhello/streamHello.d.ts +0 -3
  259. package/dist/streamhello/streamHello.js +0 -13
  260. package/dist/streamio/index.d.ts +0 -32
  261. package/dist/streamio/index.js +0 -87
  262. package/dist/tunnel-client/connect.d.ts +0 -9
  263. package/dist/tunnel-client/connect.js +0 -147
  264. package/dist/tunnel-client/index.d.ts +0 -1
  265. package/dist/tunnel-client/index.js +0 -1
  266. package/dist/ws/index.d.ts +0 -1
  267. package/dist/ws/index.js +0 -1
  268. /package/dist/{client.js → rpc/wire.js} +0 -0
@@ -0,0 +1,45 @@
1
+ import { type ArtifactV2, type CanonicalArtifactCandidateV2 } from "../v2/artifact.js";
2
+ import { type SessionDeadlineFactoryV2, type SessionProtocolRuntimeV2, type SessionV2 as InternalSessionV2 } from "../v2/session.js";
3
+ import type { ArtifactLeaseV2 } from "../v2/artifactLease.js";
4
+ import { type RuntimeCapabilityDescriptorV2 } from "../v2/capability.js";
5
+ import { type ReadyAdmissionTransportV2 } from "./admissionCommit.js";
6
+ export type SessionConnectorStateV2 = "attempt" | "ready" | "winner" | "admitted" | "established" | "terminated";
7
+ export type ConnectorArtifactLeaseV2 = ArtifactLeaseV2;
8
+ export type CandidateAttemptV2 = Readonly<{
9
+ candidate: CanonicalArtifactCandidateV2;
10
+ ready(signal?: AbortSignal): Promise<ReadyAdmissionTransportV2>;
11
+ abort(): void;
12
+ }>;
13
+ export type CandidateAttemptFactoryV2 = Readonly<{
14
+ create(candidate: CanonicalArtifactCandidateV2, artifact: ArtifactV2): CandidateAttemptV2;
15
+ }>;
16
+ export declare function composeCandidateAttemptFactoryV2(factories: Readonly<Partial<Record<CanonicalArtifactCandidateV2["carrier"], CandidateAttemptFactoryV2>>>): CandidateAttemptFactoryV2;
17
+ export type SessionConnectorOptionsV2 = Readonly<{
18
+ signal?: AbortSignal;
19
+ connectTimeoutMs?: number;
20
+ }>;
21
+ type SessionConnectorInternalOptionsV2 = Readonly<{
22
+ runtime: SessionProtocolRuntimeV2;
23
+ connectTimeoutMs?: number;
24
+ deadlineFactory?: SessionDeadlineFactoryV2;
25
+ loserCloseTimeoutMs?: number;
26
+ now?: () => number;
27
+ capability?: RuntimeCapabilityDescriptorV2;
28
+ }>;
29
+ export type SessionConnectResultV2 = Readonly<{
30
+ candidate: CanonicalArtifactCandidateV2;
31
+ session: InternalSessionV2;
32
+ }>;
33
+ export declare class SessionConnectorV2 {
34
+ private readonly lease;
35
+ private readonly attemptFactory;
36
+ private readonly options;
37
+ state: SessionConnectorStateV2;
38
+ private claimed;
39
+ private readonly artifact;
40
+ constructor(lease: ConnectorArtifactLeaseV2, attemptFactory: CandidateAttemptFactoryV2, options: SessionConnectorInternalOptionsV2);
41
+ connect(options?: Readonly<{
42
+ signal?: AbortSignal;
43
+ }>): Promise<SessionConnectResultV2>;
44
+ }
45
+ export {};
@@ -0,0 +1,603 @@
1
+ import { buildFSB2RequestV2, encodeFSB2RequestV2, validateArtifactV2, } from "../v2/artifact.js";
2
+ import { AdmissionSessionV2Error } from "../v2/admissionError.js";
3
+ import { establishSessionV2, SessionV2Error, } from "../v2/session.js";
4
+ import { unwrapArtifact } from "../v2/opaqueArtifact.js";
5
+ import { artifactLeaseArtifact } from "../public/artifactLease.js";
6
+ import { ConnectError } from "../public/connectError.js";
7
+ import { AbortError, TimeoutError, connectErrorDetailsInternal, createConnectErrorInternal, } from "../utils/errors.js";
8
+ import { validateRuntimeCapabilityDescriptorV2, } from "../v2/capability.js";
9
+ import { SDK_DEFAULTS } from "../defaults.js";
10
+ import { sessionConfigFromArtifactV2 } from "./sessionConfig.js";
11
+ import { commitClientAdmissionV2, CredentialCommitError, } from "./admissionCommit.js";
12
+ export function composeCandidateAttemptFactoryV2(factories) {
13
+ return Object.freeze({
14
+ create(candidate, artifact) {
15
+ const factory = factories[candidate.carrier];
16
+ if (factory === undefined)
17
+ throw new Error(`runtime does not implement ${candidate.carrier}`);
18
+ return factory.create(candidate, artifact);
19
+ },
20
+ });
21
+ }
22
+ export class SessionConnectorV2 {
23
+ lease;
24
+ attemptFactory;
25
+ options;
26
+ state = "attempt";
27
+ claimed = false;
28
+ artifact;
29
+ constructor(lease, attemptFactory, options) {
30
+ this.lease = lease;
31
+ this.attemptFactory = attemptFactory;
32
+ this.options = options;
33
+ this.artifact = unwrapArtifact(artifactLeaseArtifact(lease));
34
+ }
35
+ async connect(options = {}) {
36
+ const path = connectorPath(this.artifact);
37
+ if (this.claimed) {
38
+ throw connectorError(path, "validate", "invalid_input", new Error("Flowersec v2 artifact is already claimed"));
39
+ }
40
+ this.claimed = true;
41
+ let stage = "validate";
42
+ let deadline;
43
+ let operation;
44
+ const diagnostics = [];
45
+ try {
46
+ let loserCloseTimeoutMs;
47
+ let connectTimeoutMs;
48
+ try {
49
+ loserCloseTimeoutMs = normalizeLoserCloseTimeout(this.options.loserCloseTimeoutMs);
50
+ connectTimeoutMs = normalizeConnectTimeout(this.options.connectTimeoutMs);
51
+ }
52
+ catch (error) {
53
+ throw connectorError(path, "validate", "invalid_option", error);
54
+ }
55
+ let canonical;
56
+ try {
57
+ canonical = validateArtifactV2(this.artifact);
58
+ }
59
+ catch (error) {
60
+ throw connectorError(path, "validate", "invalid_input", error);
61
+ }
62
+ let remaining;
63
+ try {
64
+ remaining = artifactRemainingMilliseconds(this.artifact, this.options.now);
65
+ }
66
+ catch (error) {
67
+ throw connectorError(path, "validate", error instanceof ArtifactExpiredError ? "timeout" : "invalid_input", error);
68
+ }
69
+ const capability = this.options.capability;
70
+ if (capability === undefined) {
71
+ throw connectorError(path, "validate", "invalid_option", new TypeError("runtime capability is required"));
72
+ }
73
+ try {
74
+ validateRuntimeCapabilityDescriptorV2(capability);
75
+ }
76
+ catch (error) {
77
+ throw connectorError(path, "validate", "invalid_option", error);
78
+ }
79
+ if (capability.language !== "typescript") {
80
+ throw connectorError(path, "validate", "invalid_option", new TypeError("SessionV2 connector requires a TypeScript runtime capability descriptor"));
81
+ }
82
+ const requiredRole = this.artifact.path.kind === "tunnel" && this.artifact.path.role === 2
83
+ ? "server"
84
+ : "client";
85
+ const supported = new Set(capability.tuples
86
+ .filter(({ networkMode, sessionRole, path }) => networkMode === "dial" && sessionRole === requiredRole && path === this.artifact.path.kind)
87
+ .map(({ carrier }) => carrier));
88
+ const candidates = canonical.candidates.filter((candidate) => supported.has(candidate.carrier));
89
+ if (candidates.length === 0) {
90
+ throw connectorError(path, "validate", "unsupported_capability", new Error("no runtime-compatible Flowersec v2 candidate"));
91
+ }
92
+ try {
93
+ deadline = createConnectorDeadline(Math.min(connectTimeoutMs, this.artifact.session.establish_timeout_seconds * 1_000, remaining), this.options.deadlineFactory);
94
+ }
95
+ catch (error) {
96
+ throw connectorError(path, "validate", "invalid_option", error);
97
+ }
98
+ operation = combineConnectorSignals(options.signal, deadline.signal);
99
+ const operationSignal = operation.signal;
100
+ const cancellationSignal = options.signal;
101
+ const abortSources = cancellationSignal === undefined
102
+ ? { timeoutSignal: deadline.signal }
103
+ : { cancellationSignal, timeoutSignal: deadline.signal };
104
+ stage = "connect";
105
+ throwIfAborted(operationSignal);
106
+ if (this.attemptFactory == null) {
107
+ throw connectorError(path, "validate", "invalid_option", new TypeError("candidate attempt factory is required"));
108
+ }
109
+ const attempts = [];
110
+ const createErrors = [];
111
+ for (const candidate of candidates) {
112
+ try {
113
+ const attempt = this.attemptFactory.create(candidate, this.artifact);
114
+ if (attempt == null)
115
+ throw new TypeError("candidate factory returned no attempt");
116
+ attempts.push(attempt);
117
+ }
118
+ catch (error) {
119
+ const failure = candidateFailure(candidate, "connect", "dial_failed", error);
120
+ diagnostics.push(failure.diagnostic);
121
+ createErrors.push(failure.error);
122
+ }
123
+ }
124
+ if (attempts.length === 0) {
125
+ throw connectorError(path, "connect", dominantDiagnosticCode(diagnostics, "dial_failed"), aggregateErrors("no Flowersec v2 candidate could be created", createErrors), diagnostics);
126
+ }
127
+ const barrier = deferred();
128
+ const results = new ResultQueue();
129
+ const attemptTasks = attempts.map(async (attempt) => {
130
+ await barrier.promise;
131
+ let result;
132
+ try {
133
+ result = { attempt, ready: await attempt.ready(operationSignal) };
134
+ }
135
+ catch (error) {
136
+ result = { attempt, error };
137
+ }
138
+ results.push(result);
139
+ return result;
140
+ });
141
+ barrier.resolve();
142
+ let winner;
143
+ const errors = [...createErrors];
144
+ try {
145
+ for (let remaining = attempts.length; remaining > 0;) {
146
+ throwIfAborted(operationSignal);
147
+ const result = await raceAbort(results.shift(), operationSignal);
148
+ remaining--;
149
+ if (result.ready !== undefined) {
150
+ winner = result;
151
+ break;
152
+ }
153
+ const failure = candidateFailure(result.attempt.candidate, "connect", "dial_failed", result.error);
154
+ diagnostics.push(failure.diagnostic);
155
+ errors.push(failure.error);
156
+ }
157
+ }
158
+ catch (error) {
159
+ const cleanupFailures = await closeCandidateLosers(attempts, attemptTasks, undefined, operationSignal, abortSources, loserCloseTimeoutMs);
160
+ diagnostics.push(...cleanupFailures.map(({ diagnostic }) => diagnostic));
161
+ throw connectorError(path, "connect", contextualErrorCode(error, "dial_failed", options.signal, deadline.signal), aggregateErrors("candidate race failed", [asError(error), ...cleanupFailures.map(({ error }) => error)]), diagnostics);
162
+ }
163
+ if (winner?.ready === undefined) {
164
+ const cleanupFailures = await closeCandidateLosers(attempts, attemptTasks, undefined, operationSignal, abortSources, loserCloseTimeoutMs);
165
+ diagnostics.push(...cleanupFailures.map(({ diagnostic }) => diagnostic));
166
+ const failureStage = cleanupFailures.length === 0 ? "connect" : "close";
167
+ throw connectorError(path, failureStage, failureStage === "connect"
168
+ ? dominantDiagnosticCode(diagnostics, "dial_failed")
169
+ : dominantCandidateCode(cleanupFailures, "not_connected"), aggregateErrors(cleanupFailures.length === 0
170
+ ? "no Flowersec v2 candidate became ready"
171
+ : "no Flowersec v2 candidate became ready; candidate cleanup failed", [...errors, ...cleanupFailures.map(({ error }) => error)]), diagnostics);
172
+ }
173
+ this.state = "ready";
174
+ const selected = { attempt: winner.attempt, ready: winner.ready };
175
+ this.state = "winner";
176
+ stage = "close";
177
+ const cleanupFailures = await closeCandidateLosers(attempts, attemptTasks, selected, operationSignal, abortSources, loserCloseTimeoutMs);
178
+ if (cleanupFailures.length !== 0) {
179
+ const winnerClose = await captureCandidateCleanupFailure(selected.attempt.candidate, "close selected candidate", () => selected.ready.close(), loserCloseTimeoutMs, operationSignal, abortSources, selected.ready.abort);
180
+ if (winnerClose !== undefined)
181
+ cleanupFailures.push(winnerClose);
182
+ diagnostics.push(...cleanupFailures.map(({ diagnostic }) => diagnostic));
183
+ throw connectorError(path, "close", dominantCandidateCode(cleanupFailures, "not_connected"), aggregateErrors("candidate cleanup failed", cleanupFailures.map(({ error }) => error)), diagnostics);
184
+ }
185
+ stage = "validate";
186
+ try {
187
+ artifactRemainingMilliseconds(this.artifact, this.options.now);
188
+ }
189
+ catch (error) {
190
+ const closeFailure = await captureCandidateCleanupFailure(selected.attempt.candidate, "close selected candidate", () => selected.ready.close(), loserCloseTimeoutMs, operationSignal, abortSources, selected.ready.abort);
191
+ if (closeFailure !== undefined)
192
+ diagnostics.push(closeFailure.diagnostic);
193
+ throw connectorError(path, "validate", error instanceof ArtifactExpiredError ? "timeout" : "invalid_input", aggregateErrors("artifact validation failed after candidate selection", [
194
+ asError(error),
195
+ ...(closeFailure === undefined ? [] : [closeFailure.error]),
196
+ ]), diagnostics);
197
+ }
198
+ let rawFSB2;
199
+ let config;
200
+ try {
201
+ rawFSB2 = encodeFSB2RequestV2(buildFSB2RequestV2(this.artifact, selected.attempt.candidate.id));
202
+ config = sessionConfigFromArtifactV2(this.artifact, rawFSB2, this.options.runtime, this.options.deadlineFactory);
203
+ }
204
+ catch (error) {
205
+ const closeFailure = await captureCandidateCleanupFailure(selected.attempt.candidate, "close selected candidate", () => selected.ready.close(), loserCloseTimeoutMs, operationSignal, abortSources, selected.ready.abort);
206
+ if (closeFailure !== undefined)
207
+ diagnostics.push(closeFailure.diagnostic);
208
+ throw connectorError(path, "validate", "invalid_input", aggregateErrors("failed to prepare Flowersec v2 admission request", [asError(error), ...(closeFailure === undefined ? [] : [closeFailure.error])]), diagnostics);
209
+ }
210
+ stage = "attach";
211
+ try {
212
+ throwIfAborted(operationSignal);
213
+ }
214
+ catch (error) {
215
+ const closeFailure = await captureCandidateCleanupFailure(selected.attempt.candidate, "close selected candidate", () => selected.ready.close(), loserCloseTimeoutMs, operationSignal, abortSources, selected.ready.abort);
216
+ if (closeFailure !== undefined)
217
+ diagnostics.push(closeFailure.diagnostic);
218
+ throw connectorError(path, "attach", contextualErrorCode(error, "attach_failed", options.signal, deadline.signal), aggregateErrors("connection canceled before durable spend", [
219
+ asError(error),
220
+ ...(closeFailure === undefined ? [] : [closeFailure.error]),
221
+ ]), diagnostics);
222
+ }
223
+ let session;
224
+ stage = "attach";
225
+ try {
226
+ throwIfAborted(operationSignal);
227
+ const committed = await commitClientAdmissionV2(selected.ready, this.lease, () => {
228
+ artifactRemainingMilliseconds(this.artifact, this.options.now);
229
+ }, rawFSB2, config, operationSignal);
230
+ if (committed == null)
231
+ throw new TypeError("candidate commit returned no carrier session");
232
+ this.state = "admitted";
233
+ session = await establishSessionV2(committed, config, operationSignal === undefined ? {} : { signal: operationSignal });
234
+ throwIfAborted(operationSignal);
235
+ }
236
+ catch (error) {
237
+ const failureStage = error instanceof ArtifactExpiredError
238
+ ? "validate"
239
+ : error instanceof SessionV2Error || error instanceof CredentialCommitError
240
+ ? "handshake"
241
+ : "attach";
242
+ const defaultCode = error instanceof ArtifactExpiredError
243
+ ? "timeout"
244
+ : error instanceof CredentialCommitError
245
+ ? "credential_commit_failed"
246
+ : error instanceof SessionV2Error
247
+ ? "handshake_failed"
248
+ : "attach_failed";
249
+ diagnostics.push(candidateFailure(selected.attempt.candidate, failureStage, defaultCode, error).diagnostic);
250
+ const closeFailure = await captureCandidateCleanupFailure(selected.attempt.candidate, "close selected candidate", () => closeCommittedSession(session, selected.ready), loserCloseTimeoutMs, operationSignal, abortSources, selected.ready.abort);
251
+ if (closeFailure !== undefined)
252
+ diagnostics.push(closeFailure.diagnostic);
253
+ throw connectorError(path, failureStage, contextualErrorCode(error, defaultCode, options.signal, deadline.signal), aggregateErrors(error instanceof CredentialCommitError
254
+ ? "durable credential spend failed"
255
+ : error instanceof AdmissionSessionV2Error
256
+ ? "Flowersec v2 admission failed"
257
+ : failureStage === "handshake"
258
+ ? "Flowersec v2 session handshake failed"
259
+ : "Flowersec v2 candidate commit failed", [asError(error), ...(closeFailure === undefined ? [] : [closeFailure.error])]), diagnostics);
260
+ }
261
+ this.state = "established";
262
+ void session.waitTermination().finally(() => {
263
+ this.state = "terminated";
264
+ });
265
+ return { candidate: selected.attempt.candidate, session };
266
+ }
267
+ catch (error) {
268
+ this.state = "terminated";
269
+ throw normalizeConnectorError(error, path, stage, diagnostics, options.signal, deadline?.signal);
270
+ }
271
+ finally {
272
+ operation?.cancel();
273
+ deadline?.cancel();
274
+ }
275
+ }
276
+ }
277
+ async function closeCommittedSession(session, ready) {
278
+ const operations = [() => ready.close()];
279
+ if (session !== undefined && typeof session.close === "function") {
280
+ operations.push(() => session.close());
281
+ }
282
+ const settled = await Promise.allSettled(operations.map(async (operation) => await operation()));
283
+ const failures = settled.flatMap((result) => result.status === "rejected" ? [asError(result.reason)] : []);
284
+ if (failures.length !== 0)
285
+ throw aggregateErrors("committed session cleanup failed", failures);
286
+ }
287
+ async function closeCandidateLosers(attempts, attemptTasks, winner, operationSignal, abortSources, timeoutMs) {
288
+ const deadline = new AbortController();
289
+ const timeoutError = new TimeoutError("candidate cleanup timeout");
290
+ const timer = setTimeout(() => deadline.abort(timeoutError), timeoutMs);
291
+ const signal = combineConnectorSignals(operationSignal, deadline.signal);
292
+ const failures = [];
293
+ const loserSet = new Set(attempts.filter((attempt) => winner === undefined || attempt !== winner.attempt));
294
+ try {
295
+ for (const attempt of loserSet) {
296
+ try {
297
+ attempt.abort();
298
+ }
299
+ catch (error) {
300
+ failures.push(candidateFailure(attempt.candidate, "close", "not_connected", error));
301
+ }
302
+ }
303
+ let results;
304
+ try {
305
+ results = await raceAbort(Promise.all(attemptTasks), signal.signal);
306
+ }
307
+ catch (error) {
308
+ if (!signal.signal.aborted)
309
+ throw error;
310
+ const code = cleanupErrorCode(error, "not_connected", abortSources, deadline.signal);
311
+ for (const attempt of loserSet) {
312
+ failures.push(candidateFailure(attempt.candidate, "close", code, abortReason(signal.signal)));
313
+ }
314
+ return failures;
315
+ }
316
+ for (const result of results) {
317
+ if (result.ready === undefined || !loserSet.has(result.attempt))
318
+ continue;
319
+ const failure = await captureCandidateCleanupFailure(result.attempt.candidate, "close candidate loser", () => result.ready.close(), timeoutMs, signal.signal, abortSources, result.ready.abort);
320
+ if (failure !== undefined)
321
+ failures.push(failure);
322
+ }
323
+ return failures;
324
+ }
325
+ finally {
326
+ clearTimeout(timer);
327
+ signal.cancel();
328
+ }
329
+ }
330
+ async function captureCandidateCleanupFailure(candidate, label, operation, timeoutMs, operationSignal, abortSources = {}, abort) {
331
+ const deadline = new AbortController();
332
+ const timer = setTimeout(() => deadline.abort(new TimeoutError(`${label} timeout`)), timeoutMs);
333
+ const signal = combineConnectorSignals(operationSignal, deadline.signal);
334
+ try {
335
+ let pending;
336
+ try {
337
+ pending = Promise.resolve(operation());
338
+ }
339
+ catch (error) {
340
+ return candidateFailure(candidate, "close", "not_connected", new Error(`${label}: ${asError(error).message}`));
341
+ }
342
+ void pending.catch(() => undefined);
343
+ await raceAbort(pending, signal.signal);
344
+ return undefined;
345
+ }
346
+ catch (error) {
347
+ const code = cleanupErrorCode(error, "not_connected", abortSources, deadline.signal);
348
+ const causes = [asError(error)];
349
+ if (signal.signal.aborted && abort !== undefined) {
350
+ try {
351
+ abort();
352
+ }
353
+ catch (abortError) {
354
+ causes.push(asError(abortError));
355
+ }
356
+ }
357
+ return candidateFailure(candidate, "close", code, causes.length === 1
358
+ ? new Error(`${label}: ${causes[0].message}`)
359
+ : aggregateErrors(`${label} failed`, causes));
360
+ }
361
+ finally {
362
+ clearTimeout(timer);
363
+ signal.cancel();
364
+ }
365
+ }
366
+ function normalizeLoserCloseTimeout(value) {
367
+ const timeout = value ?? 1_000;
368
+ if (!Number.isInteger(timeout) || timeout < 1 || timeout > 60_000) {
369
+ throw new RangeError("loserCloseTimeoutMs must be an integer from 1 to 60000");
370
+ }
371
+ return timeout;
372
+ }
373
+ function normalizeConnectTimeout(value) {
374
+ const timeout = value ?? SDK_DEFAULTS.transport.connectTimeoutMs;
375
+ if (!Number.isSafeInteger(timeout) || timeout < 1) {
376
+ throw new RangeError("connectTimeoutMs must be a positive safe integer");
377
+ }
378
+ return timeout;
379
+ }
380
+ function artifactRemainingMilliseconds(artifact, now) {
381
+ const current = (now ?? Date.now)();
382
+ if (!Number.isFinite(current))
383
+ throw new TypeError("session V2 clock returned a non-finite value");
384
+ const remaining = artifact.session.init_expire_at_unix_s * 1_000 - current;
385
+ if (remaining <= 0)
386
+ throw new ArtifactExpiredError();
387
+ return remaining;
388
+ }
389
+ class ArtifactExpiredError extends Error {
390
+ constructor() {
391
+ super("Flowersec v2 artifact initiation deadline expired");
392
+ this.name = "ArtifactExpiredError";
393
+ }
394
+ }
395
+ class ResultQueue {
396
+ values = [];
397
+ waiters = [];
398
+ push(value) {
399
+ const waiter = this.waiters.shift();
400
+ if (waiter !== undefined)
401
+ waiter(value);
402
+ else
403
+ this.values.push(value);
404
+ }
405
+ async shift() {
406
+ const value = this.values.shift();
407
+ if (value !== undefined)
408
+ return value;
409
+ return await new Promise((resolve) => this.waiters.push(resolve));
410
+ }
411
+ }
412
+ function deferred() {
413
+ let resolve;
414
+ const promise = new Promise((resolvePromise) => { resolve = resolvePromise; });
415
+ return { promise, resolve };
416
+ }
417
+ async function raceAbort(promise, signal) {
418
+ if (signal === undefined)
419
+ return await promise;
420
+ throwIfAborted(signal);
421
+ return await new Promise((resolve, reject) => {
422
+ const abort = () => reject(abortReason(signal));
423
+ signal.addEventListener("abort", abort, { once: true });
424
+ void promise.then((value) => { signal.removeEventListener("abort", abort); resolve(value); }, (error) => { signal.removeEventListener("abort", abort); reject(error); });
425
+ });
426
+ }
427
+ function throwIfAborted(signal) {
428
+ if (signal?.aborted === true)
429
+ throw abortReason(signal);
430
+ }
431
+ function abortReason(signal) {
432
+ return signal.reason instanceof Error ? signal.reason : new Error("session V2 connect aborted");
433
+ }
434
+ function asError(error) {
435
+ return error instanceof Error ? error : new Error(String(error));
436
+ }
437
+ function connectorPath(artifact) {
438
+ if (artifact?.path?.kind === "tunnel")
439
+ return "tunnel";
440
+ if (artifact?.path?.kind === "direct")
441
+ return "direct";
442
+ return "auto";
443
+ }
444
+ function connectorError(path, stage, code, cause, diagnostics = []) {
445
+ return createConnectErrorInternal({
446
+ path,
447
+ stage,
448
+ code,
449
+ cause,
450
+ diagnostics,
451
+ });
452
+ }
453
+ function normalizeConnectorError(error, path, stage, diagnostics, cancellationSignal, timeoutSignal) {
454
+ if (error instanceof ConnectError) {
455
+ const details = connectErrorDetailsInternal(error);
456
+ const merged = mergeDiagnostics(details.diagnostics, diagnostics);
457
+ if (merged.length === details.diagnostics.length)
458
+ return error;
459
+ return createConnectErrorInternal({
460
+ path,
461
+ stage: details.stage,
462
+ code: details.code,
463
+ cause: details.cause ?? error,
464
+ diagnostics: merged,
465
+ });
466
+ }
467
+ return connectorError(path, stage, contextualErrorCode(error, defaultCodeForStage(stage), cancellationSignal, timeoutSignal), error, diagnostics);
468
+ }
469
+ function defaultCodeForStage(stage) {
470
+ switch (stage) {
471
+ case "validate": return "invalid_input";
472
+ case "connect": return "dial_failed";
473
+ case "attach": return "attach_failed";
474
+ case "handshake": return "handshake_failed";
475
+ case "close": return "not_connected";
476
+ case "secure": return "handshake_failed";
477
+ case "yamux": return "mux_failed";
478
+ case "rpc": return "rpc_failed";
479
+ }
480
+ }
481
+ function contextualErrorCode(error, defaultCode, cancellationSignal, timeoutSignal) {
482
+ if (cancellationSignal?.aborted === true)
483
+ return "canceled";
484
+ if (timeoutSignal?.aborted === true)
485
+ return "timeout";
486
+ if (error instanceof TimeoutError || (error instanceof SessionV2Error && error.code === "timeout"))
487
+ return "timeout";
488
+ if (error instanceof AbortError ||
489
+ (error instanceof SessionV2Error && error.code === "aborted") ||
490
+ (error instanceof DOMException && error.name === "AbortError"))
491
+ return "canceled";
492
+ return defaultCode;
493
+ }
494
+ function cleanupErrorCode(error, defaultCode, abortSources, cleanupDeadline) {
495
+ return contextualErrorCode(error, defaultCode, abortSources.cancellationSignal, abortSources.timeoutSignal?.aborted === true ? abortSources.timeoutSignal : cleanupDeadline);
496
+ }
497
+ function candidateFailure(candidate, stage, defaultCode, error) {
498
+ const failure = asError(error);
499
+ const code = contextualErrorCode(error, defaultCode);
500
+ return {
501
+ error: failure,
502
+ diagnostic: {
503
+ candidateId: candidate.id,
504
+ carrier: candidate.carrier,
505
+ stage,
506
+ code,
507
+ message: boundedCandidateDiagnosticMessage(failure.message),
508
+ },
509
+ };
510
+ }
511
+ function dominantCandidateCode(failures, defaultCode) {
512
+ if (failures.some(({ diagnostic }) => diagnostic.code === "canceled"))
513
+ return "canceled";
514
+ if (failures.some(({ diagnostic }) => diagnostic.code === "timeout"))
515
+ return "timeout";
516
+ if (failures.some(({ diagnostic }) => diagnostic.code === "not_connected"))
517
+ return "not_connected";
518
+ return defaultCode;
519
+ }
520
+ function dominantDiagnosticCode(diagnostics, defaultCode) {
521
+ if (diagnostics.some(({ code }) => code === "canceled"))
522
+ return "canceled";
523
+ if (diagnostics.some(({ code }) => code === "timeout"))
524
+ return "timeout";
525
+ return defaultCode;
526
+ }
527
+ const MAX_CANDIDATE_DIAGNOSTIC_MESSAGE_BYTES = 1_024;
528
+ const candidateDiagnosticEncoder = new TextEncoder();
529
+ function boundedCandidateDiagnosticMessage(message) {
530
+ if (candidateDiagnosticEncoder.encode(message).length <= MAX_CANDIDATE_DIAGNOSTIC_MESSAGE_BYTES)
531
+ return message;
532
+ let result = "";
533
+ let bytes = 0;
534
+ for (const character of message) {
535
+ const characterBytes = candidateDiagnosticEncoder.encode(character).length;
536
+ if (bytes + characterBytes > MAX_CANDIDATE_DIAGNOSTIC_MESSAGE_BYTES)
537
+ break;
538
+ result += character;
539
+ bytes += characterBytes;
540
+ }
541
+ return result;
542
+ }
543
+ function mergeDiagnostics(left, right) {
544
+ const merged = [];
545
+ const seen = new Set();
546
+ for (const diagnostic of [...left, ...right]) {
547
+ const key = [
548
+ diagnostic.candidateId,
549
+ diagnostic.carrier,
550
+ diagnostic.stage,
551
+ diagnostic.code,
552
+ diagnostic.message,
553
+ ].join("\u0000");
554
+ if (seen.has(key))
555
+ continue;
556
+ seen.add(key);
557
+ merged.push(diagnostic);
558
+ }
559
+ return merged;
560
+ }
561
+ function aggregateErrors(label, errors) {
562
+ if (errors.length === 0)
563
+ return new Error(label);
564
+ return new AggregateError(errors, `${label}: ${errors.map(({ message }) => message).join("; ")}`);
565
+ }
566
+ function createConnectorDeadline(timeoutMs, factory) {
567
+ if (factory !== undefined) {
568
+ const handle = factory(timeoutMs, "establish");
569
+ if (handle == null || !isAbortSignal(handle.signal) || typeof handle.cancel !== "function") {
570
+ throw new TypeError("deadlineFactory returned an invalid establish deadline handle");
571
+ }
572
+ return handle;
573
+ }
574
+ const controller = new AbortController();
575
+ const timer = setTimeout(() => controller.abort(new TimeoutError("session V2 establish deadline exceeded")), timeoutMs);
576
+ return { signal: controller.signal, cancel: () => clearTimeout(timer) };
577
+ }
578
+ function isAbortSignal(value) {
579
+ if (typeof value !== "object" || value === null)
580
+ return false;
581
+ const signal = value;
582
+ return typeof signal.aborted === "boolean" &&
583
+ typeof signal.addEventListener === "function" &&
584
+ typeof signal.removeEventListener === "function";
585
+ }
586
+ function combineConnectorSignals(...signals) {
587
+ const controller = new AbortController();
588
+ const cleanups = [];
589
+ for (const signal of signals) {
590
+ if (signal === undefined)
591
+ continue;
592
+ const abort = () => {
593
+ if (!controller.signal.aborted)
594
+ controller.abort(signal.reason instanceof Error ? signal.reason : new Error("session V2 connect aborted"));
595
+ };
596
+ signal.addEventListener("abort", abort, { once: true });
597
+ cleanups.push(() => signal.removeEventListener("abort", abort));
598
+ if (signal.aborted)
599
+ abort();
600
+ }
601
+ return { signal: controller.signal, cancel: () => { for (const cleanup of cleanups.splice(0))
602
+ cleanup(); } };
603
+ }
@@ -4,6 +4,13 @@ export declare const SDK_DEFAULTS: Readonly<{
4
4
  handshakeTimeoutMs: 10000;
5
5
  handshakeClockSkewMs: 30000;
6
6
  }>;
7
+ connectionController: Readonly<{
8
+ initialDelayMs: 250;
9
+ maxDelayMs: 30000;
10
+ factor: 2;
11
+ jitterRatio: 0;
12
+ defaultAttemptLimit: null;
13
+ }>;
7
14
  e2ee: Readonly<{
8
15
  maxHandshakePayloadBytes: number;
9
16
  maxRecordBytes: number;
@@ -39,11 +46,4 @@ export declare const SDK_DEFAULTS: Readonly<{
39
46
  defaultTimeoutMs: 30000;
40
47
  maxTimeoutMs: 300000;
41
48
  }>;
42
- reconnect: Readonly<{
43
- maxAttempts: 5;
44
- initialDelayMs: 500;
45
- maxDelayMs: 10000;
46
- factor: 1.8;
47
- jitterRatio: 0.2;
48
- }>;
49
49
  }>;