@floegence/flowersec-core 2.5.3 → 3.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 (119) hide show
  1. package/README.md +46 -18
  2. package/dist/browser/connectSessionV3.d.ts +13 -0
  3. package/dist/browser/connectSessionV3.js +75 -0
  4. package/dist/browser/index.d.ts +3 -2
  5. package/dist/browser/index.js +2 -1
  6. package/dist/browser/v2.d.ts +3 -0
  7. package/dist/browser/v2.js +2 -0
  8. package/dist/facade.d.ts +15 -8
  9. package/dist/facade.js +5 -4
  10. package/dist/framing/jsonframe.d.ts +1 -1
  11. package/dist/framing/jsonframe.js +3 -1
  12. package/dist/interop/proxyServerPeer.js +1 -1
  13. package/dist/interop/serverParityPeer.js +8 -1
  14. package/dist/node/acceptor.d.ts +10 -0
  15. package/dist/node/acceptor.js +53 -7
  16. package/dist/node/acceptorV3.d.ts +57 -0
  17. package/dist/node/acceptorV3.js +227 -0
  18. package/dist/node/connectSessionV3.d.ts +21 -0
  19. package/dist/node/connectSessionV3.js +73 -0
  20. package/dist/node/index.d.ts +15 -10
  21. package/dist/node/index.js +6 -5
  22. package/dist/node/nativeTransportAddon.d.ts +39 -1
  23. package/dist/node/nativeTransportAddon.js +29 -5
  24. package/dist/node/rawQuicAdapterV3.d.ts +10 -0
  25. package/dist/node/rawQuicAdapterV3.js +76 -0
  26. package/dist/node/rawQuicServerV3.d.ts +24 -0
  27. package/dist/node/rawQuicServerV3.js +34 -0
  28. package/dist/node/runtimeAuthorizationV3.d.ts +29 -0
  29. package/dist/node/runtimeAuthorizationV3.js +126 -0
  30. package/dist/node/tunnelRuntimeV3.d.ts +57 -0
  31. package/dist/node/tunnelRuntimeV3.js +857 -0
  32. package/dist/node/v2.d.ts +11 -0
  33. package/dist/node/v2.js +6 -0
  34. package/dist/node/webSocketServerV3.d.ts +27 -0
  35. package/dist/node/webSocketServerV3.js +219 -0
  36. package/dist/proxy/runtime.js +39 -6
  37. package/dist/proxy/serviceWorker.d.ts +2 -0
  38. package/dist/proxy/serviceWorker.js +63 -7
  39. package/dist/proxy/windowBridge.d.ts +1 -0
  40. package/dist/proxy/windowBridge.js +93 -33
  41. package/dist/proxy/wsPatch.d.ts +1 -0
  42. package/dist/proxy/wsPatch.js +39 -6
  43. package/dist/public/streamHandlers.d.ts +3 -0
  44. package/dist/public/streamHandlers.js +6 -1
  45. package/dist/rpc/client.js +9 -7
  46. package/dist/rpc/server.js +44 -20
  47. package/dist/rpc/validate.d.ts +1 -0
  48. package/dist/rpc/validate.js +18 -6
  49. package/dist/v2/index.d.ts +14 -0
  50. package/dist/v2/index.js +7 -0
  51. package/dist/v2/protocol.js +23 -32
  52. package/dist/v2/publicSession.js +4 -0
  53. package/dist/v2/session.d.ts +5 -0
  54. package/dist/v2/session.js +94 -35
  55. package/dist/v3/admissionError.d.ts +4 -0
  56. package/dist/v3/admissionError.js +8 -0
  57. package/dist/v3/artifact.d.ts +145 -0
  58. package/dist/v3/artifact.js +1162 -0
  59. package/dist/v3/artifactLease.d.ts +17 -0
  60. package/dist/v3/artifactLease.js +109 -0
  61. package/dist/v3/browserRuntime.d.ts +37 -0
  62. package/dist/v3/browserRuntime.js +197 -0
  63. package/dist/v3/browserSessionRuntime.d.ts +2 -0
  64. package/dist/v3/browserSessionRuntime.js +21 -0
  65. package/dist/v3/capability.d.ts +32 -0
  66. package/dist/v3/capability.js +345 -0
  67. package/dist/v3/carrier.d.ts +84 -0
  68. package/dist/v3/carrier.js +411 -0
  69. package/dist/v3/connectionController.d.ts +87 -0
  70. package/dist/v3/connectionController.js +805 -0
  71. package/dist/v3/contract.d.ts +65 -0
  72. package/dist/v3/contract.js +3 -0
  73. package/dist/v3/controller.d.ts +43 -0
  74. package/dist/v3/controller.js +303 -0
  75. package/dist/v3/handshake.d.ts +81 -0
  76. package/dist/v3/handshake.js +471 -0
  77. package/dist/v3/jcs.d.ts +6 -0
  78. package/dist/v3/jcs.js +69 -0
  79. package/dist/v3/jsonPreflight.d.ts +1 -0
  80. package/dist/v3/jsonPreflight.js +251 -0
  81. package/dist/v3/nodeRuntime.d.ts +40 -0
  82. package/dist/v3/nodeRuntime.js +315 -0
  83. package/dist/v3/nodeSessionRuntime.d.ts +2 -0
  84. package/dist/v3/nodeSessionRuntime.js +13 -0
  85. package/dist/v3/protocol.d.ts +99 -0
  86. package/dist/v3/protocol.js +773 -0
  87. package/dist/v3/publicApi.d.ts +12 -0
  88. package/dist/v3/publicApi.js +46 -0
  89. package/dist/v3/publicSession.d.ts +1 -0
  90. package/dist/v3/publicSession.js +281 -0
  91. package/dist/v3/retryDisposition.d.ts +16 -0
  92. package/dist/v3/retryDisposition.js +30 -0
  93. package/dist/v3/runtimeAdapters.d.ts +9 -0
  94. package/dist/v3/runtimeAdapters.js +109 -0
  95. package/dist/v3/security.d.ts +34 -0
  96. package/dist/v3/security.js +100 -0
  97. package/dist/v3/serverAdmission.d.ts +38 -0
  98. package/dist/v3/serverAdmission.js +223 -0
  99. package/dist/v3/session.d.ts +318 -0
  100. package/dist/v3/session.js +2289 -0
  101. package/dist/v3/sessionConnector.d.ts +37 -0
  102. package/dist/v3/sessionConnector.js +440 -0
  103. package/dist/v3/streamLifetimeLedger.d.ts +31 -0
  104. package/dist/v3/streamLifetimeLedger.js +131 -0
  105. package/dist/v3/streamMetadata.d.ts +1 -0
  106. package/dist/v3/streamMetadata.js +7 -0
  107. package/dist/v3/transportConstants.d.ts +56 -0
  108. package/dist/v3/transportConstants.js +59 -0
  109. package/dist/v3/unreliableMessage.d.ts +44 -0
  110. package/dist/v3/unreliableMessage.js +284 -0
  111. package/dist/v3/webSocketCarrier.d.ts +31 -0
  112. package/dist/v3/webSocketCarrier.js +370 -0
  113. package/dist/v3/webTransportCarrier.d.ts +27 -0
  114. package/dist/v3/webTransportCarrier.js +534 -0
  115. package/dist/yamux/stream.d.ts +3 -0
  116. package/dist/yamux/stream.js +17 -4
  117. package/package.json +4 -4
  118. package/sbom/cyclonedx.json +6 -6
  119. package/sbom/spdx.json +11 -11
@@ -0,0 +1,65 @@
1
+ import type { RpcError } from "../rpc/wire.js";
2
+ import type { ByteStream, CarrierKind as PublicCarrierKind, IncomingStream, JsonObject, JsonPrimitive, JsonValue, OperationOptions, PathKind as PublicPathKind, RpcPeer, RpcResult, Session, SessionTermination, StreamOpenOptions, UnreliableMessageChannel, UnreliableMessageSendOptions, UnreliableMessageSendResult } from "../public/contract.js";
3
+ export { SessionError } from "../public/contract.js";
4
+ export type { SessionErrorCode } from "../public/contract.js";
5
+ export type CarrierKind = PublicCarrierKind;
6
+ export type PathKind = PublicPathKind;
7
+ export type JsonPrimitiveV3 = JsonPrimitive;
8
+ export type JsonValueV3 = JsonValue;
9
+ export type JsonObjectV3 = JsonObject;
10
+ export type OperationOptionsV3 = OperationOptions;
11
+ export type UnreliableMessageSendOptionsV3 = UnreliableMessageSendOptions;
12
+ export type UnreliableMessageSendResultV3 = UnreliableMessageSendResult;
13
+ export type UnreliableMessageChannelV3 = UnreliableMessageChannel;
14
+ export type StreamOpenOptionsV3 = StreamOpenOptions;
15
+ export type RpcResultV3<Response = unknown> = RpcResult<Response>;
16
+ export type RpcPeerV3 = RpcPeer;
17
+ export type ByteStreamV3 = ByteStream;
18
+ export type IncomingStreamV3 = IncomingStream;
19
+ export type SessionTerminationV3 = SessionTermination;
20
+ export type SessionV3 = Session;
21
+ export interface InternalByteStreamV3 {
22
+ readonly id: bigint;
23
+ readonly kind: string;
24
+ readonly terminalError: Error | undefined;
25
+ read(options?: OperationOptionsV3): Promise<Uint8Array | null>;
26
+ write(data: Uint8Array, options?: OperationOptionsV3): Promise<number>;
27
+ closeWrite(): Promise<void>;
28
+ reset(): Promise<void>;
29
+ close(): Promise<void>;
30
+ }
31
+ export interface InternalIncomingStreamV3 {
32
+ readonly id: bigint;
33
+ readonly kind: string;
34
+ readonly metadata: JsonObjectV3;
35
+ readonly stream: InternalByteStreamV3;
36
+ }
37
+ export interface InternalSessionV3 {
38
+ readonly path: PathKind;
39
+ readonly endpointInstanceId: string | undefined;
40
+ readonly rpc: InternalRpcPeerV3;
41
+ readonly termination: Promise<Readonly<{
42
+ error: Error;
43
+ }>>;
44
+ readonly unreliableMessages?: UnreliableMessageChannelV3 | undefined;
45
+ openStream(kind: string, options?: InternalStreamOpenOptionsV3): Promise<InternalByteStreamV3>;
46
+ acceptStream(options?: OperationOptionsV3): Promise<InternalIncomingStreamV3>;
47
+ rekey(options?: OperationOptionsV3): Promise<void>;
48
+ probeLiveness(options?: OperationOptionsV3): Promise<number>;
49
+ waitTermination(): Promise<Readonly<{
50
+ error: Error;
51
+ }>>;
52
+ close(): Promise<void>;
53
+ }
54
+ export interface InternalRpcPeerV3 {
55
+ call(typeId: number, payload: unknown, signal?: AbortSignal): Promise<{
56
+ payload: unknown;
57
+ error?: RpcError;
58
+ }>;
59
+ notify(typeId: number, payload: unknown): Promise<void>;
60
+ onNotify(typeId: number, handler: (payload: unknown) => void): () => void;
61
+ close(): void;
62
+ }
63
+ export type InternalStreamOpenOptionsV3 = OperationOptionsV3 & Readonly<{
64
+ metadata?: JsonObjectV3;
65
+ }>;
@@ -0,0 +1,3 @@
1
+ // Package exports keep this module private, but its declarations must remain in
2
+ // the emitted closure because other internal declaration files import them.
3
+ export { SessionError } from "../public/contract.js";
@@ -0,0 +1,43 @@
1
+ import type { ArtifactPathKindV3, CanonicalArtifactCandidateV3 } from "./artifact.js";
2
+ import { ConnectErrorV3, type RetryDispositionV3, type TransportFailureV3 } from "./security.js";
3
+ export type CandidateFailureV3 = Readonly<{
4
+ candidate: CanonicalArtifactCandidateV3;
5
+ failure: TransportFailureV3;
6
+ disposition?: RetryDispositionV3;
7
+ }>;
8
+ export declare class ControllerCycleStateV3 {
9
+ #private;
10
+ constructor(maximumAttempts?: number);
11
+ beginAcquisition(): boolean;
12
+ recordFailedAcquisitionOrLease(): number;
13
+ claimReplacementQuota(): boolean;
14
+ blockPinPolicy(endpointKey: string, digest: string): void;
15
+ isBlocked(endpointKey: string, digest: string): boolean;
16
+ isSecurityLockedEndpoint(endpointKey: string): boolean;
17
+ recordPolicyTrigger(opaque: boolean): void;
18
+ blockedPolicyTerminal(): ConnectErrorV3;
19
+ established(): void;
20
+ snapshot(): Readonly<{
21
+ attempts: number;
22
+ consecutiveFailures: number;
23
+ replacementUsed: boolean;
24
+ }>;
25
+ }
26
+ export declare function saturatingControllerIncrementV3(value: number): number;
27
+ export declare function endpointKeyV3(path: ArtifactPathKindV3, candidate: CanonicalArtifactCandidateV3): string;
28
+ export declare function selectReplacementCandidatesV3(path: ArtifactPathKindV3, primary: readonly CanonicalArtifactCandidateV3[], replacement: readonly CanonicalArtifactCandidateV3[], triggerEndpointKeys: ReadonlySet<string>, failedEndpointKeys: ReadonlySet<string>, cycle: ControllerCycleStateV3): readonly CanonicalArtifactCandidateV3[];
29
+ export declare function filterBlockedCandidatesV3(path: ArtifactPathKindV3, candidates: readonly CanonicalArtifactCandidateV3[], cycle: ControllerCycleStateV3): readonly CanonicalArtifactCandidateV3[];
30
+ export declare function aggregateCandidateFailuresV3(failures: readonly CandidateFailureV3[], policyRefreshExecutable: boolean): ConnectErrorV3 | "policy_refresh";
31
+ /** Records policy-refresh provenance at the A-result linearization point. */
32
+ export declare function blockPolicyRefreshTriggersV3(path: ArtifactPathKindV3, failures: readonly CandidateFailureV3[], cycle: ControllerCycleStateV3): ReadonlySet<string>;
33
+ export type ControllerClockV3 = Readonly<{
34
+ wallNowMilliseconds(): number;
35
+ monotonicNowMilliseconds(): number;
36
+ sleep(milliseconds: number, signal: AbortSignal): Promise<void>;
37
+ }>;
38
+ export declare class ControllerRetryWaitV3 {
39
+ #private;
40
+ constructor(clock?: ControllerClockV3);
41
+ retryNow(): boolean;
42
+ wait(disposition: RetryDispositionV3, consecutiveFailure: number, signal: AbortSignal): Promise<boolean>;
43
+ }
@@ -0,0 +1,303 @@
1
+ import { tlsPolicyDigestV3 } from "./artifact.js";
2
+ import { ConnectErrorV3, aggregateRetryDispositionsV3, } from "./security.js";
3
+ const WALL_CLOCK_RECHECK_MILLISECONDS = 1_000;
4
+ export class ControllerCycleStateV3 {
5
+ #maximumAttempts;
6
+ #attempts = 0;
7
+ #consecutiveFailures = 0;
8
+ #replacementUsed = false;
9
+ #blockedPinPolicies = new Map();
10
+ #securityLockedEndpoints = new Set();
11
+ #securityPolicyTrigger = false;
12
+ #opaquePolicyTrigger = false;
13
+ constructor(maximumAttempts = 0) {
14
+ if (!Number.isSafeInteger(maximumAttempts) || maximumAttempts < 0) {
15
+ throw new RangeError("maximumAttempts must be a non-negative safe integer");
16
+ }
17
+ this.#maximumAttempts = maximumAttempts;
18
+ }
19
+ beginAcquisition() {
20
+ if (this.#maximumAttempts !== 0 && this.#attempts >= this.#maximumAttempts)
21
+ return false;
22
+ this.#attempts = saturatingControllerIncrementV3(this.#attempts);
23
+ return true;
24
+ }
25
+ recordFailedAcquisitionOrLease() {
26
+ this.#consecutiveFailures = Math.min(Number.MAX_SAFE_INTEGER, this.#consecutiveFailures + 1);
27
+ return this.#consecutiveFailures;
28
+ }
29
+ claimReplacementQuota() {
30
+ if (this.#replacementUsed)
31
+ return false;
32
+ this.#replacementUsed = true;
33
+ return true;
34
+ }
35
+ blockPinPolicy(endpointKey, digest) {
36
+ const values = this.#blockedPinPolicies.get(endpointKey) ?? new Set();
37
+ values.add(digest);
38
+ this.#blockedPinPolicies.set(endpointKey, values);
39
+ this.#securityLockedEndpoints.add(endpointKey);
40
+ }
41
+ isBlocked(endpointKey, digest) {
42
+ return this.#blockedPinPolicies.get(endpointKey)?.has(digest) === true;
43
+ }
44
+ isSecurityLockedEndpoint(endpointKey) {
45
+ return this.#securityLockedEndpoints.has(endpointKey);
46
+ }
47
+ recordPolicyTrigger(opaque) {
48
+ if (opaque)
49
+ this.#opaquePolicyTrigger = true;
50
+ else
51
+ this.#securityPolicyTrigger = true;
52
+ }
53
+ blockedPolicyTerminal() {
54
+ if (!this.#securityPolicyTrigger && !this.#opaquePolicyTrigger) {
55
+ return new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
56
+ }
57
+ return new ConnectErrorV3(this.#securityPolicyTrigger ? "transport_security_failed" : "connection_failed", { kind: "terminal" });
58
+ }
59
+ established() {
60
+ this.#attempts = 0;
61
+ this.#consecutiveFailures = 0;
62
+ this.#replacementUsed = false;
63
+ this.#blockedPinPolicies.clear();
64
+ this.#securityLockedEndpoints.clear();
65
+ this.#securityPolicyTrigger = false;
66
+ this.#opaquePolicyTrigger = false;
67
+ }
68
+ snapshot() {
69
+ return Object.freeze({
70
+ attempts: this.#attempts,
71
+ consecutiveFailures: this.#consecutiveFailures,
72
+ replacementUsed: this.#replacementUsed,
73
+ });
74
+ }
75
+ }
76
+ export function saturatingControllerIncrementV3(value) {
77
+ if (!Number.isSafeInteger(value) || value < 0) {
78
+ throw new RangeError("controller counter must be a non-negative safe integer");
79
+ }
80
+ return value === Number.MAX_SAFE_INTEGER ? value : value + 1;
81
+ }
82
+ export function endpointKeyV3(path, candidate) {
83
+ return `${candidate.carrier}\0${path}\0${candidate.normalized_url}`;
84
+ }
85
+ export function selectReplacementCandidatesV3(path, primary, replacement, triggerEndpointKeys, failedEndpointKeys, cycle) {
86
+ const primaryByKey = new Map(primary.map((candidate) => [endpointKeyV3(path, candidate), candidate]));
87
+ for (const key of triggerEndpointKeys) {
88
+ const candidate = primaryByKey.get(key);
89
+ if (candidate?.tls.mode === "pin") {
90
+ cycle.blockPinPolicy(key, tlsPolicyDigestV3(candidate.tls).hashBase64URL);
91
+ }
92
+ }
93
+ const eligible = [];
94
+ let changedPin = false;
95
+ for (const candidate of replacement) {
96
+ const key = endpointKeyV3(path, candidate);
97
+ const previous = primaryByKey.get(key);
98
+ if (previous === undefined) {
99
+ if (!blockedOrDowngraded(candidate, key, cycle))
100
+ eligible.push(candidate);
101
+ continue;
102
+ }
103
+ if (triggerEndpointKeys.has(key)) {
104
+ if (candidate.tls.mode !== "pin" || previous.tls.mode !== "pin")
105
+ continue;
106
+ const oldDigest = tlsPolicyDigestV3(previous.tls).hashBase64URL;
107
+ const newDigest = tlsPolicyDigestV3(candidate.tls).hashBase64URL;
108
+ if (oldDigest !== newDigest && !cycle.isBlocked(key, newDigest)) {
109
+ changedPin = true;
110
+ eligible.push(candidate);
111
+ }
112
+ continue;
113
+ }
114
+ if (!failedEndpointKeys.has(key) && !blockedOrDowngraded(candidate, key, cycle))
115
+ eligible.push(candidate);
116
+ }
117
+ return Object.freeze(changedPin ? eligible : []);
118
+ }
119
+ export function filterBlockedCandidatesV3(path, candidates, cycle) {
120
+ return Object.freeze(candidates.filter((candidate) => !blockedOrDowngraded(candidate, endpointKeyV3(path, candidate), cycle)));
121
+ }
122
+ export function aggregateCandidateFailuresV3(failures, policyRefreshExecutable) {
123
+ if (failures.some(({ failure }) => failure.code === "invalid_artifact")) {
124
+ return new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
125
+ }
126
+ if (failures.some(({ failure }) => failure.code === "expired_artifact")) {
127
+ return new ConnectErrorV3("expired_artifact", { kind: "retryable" });
128
+ }
129
+ const securityTriggers = failures.filter(({ failure, candidate }) => isNativePolicyTriggerV3(candidate, failure));
130
+ const opaqueTriggers = failures.filter(({ failure, candidate }) => candidate.tls.mode === "pin" && failure.code === "connection_failed" &&
131
+ failure.detail === "browser_pin_opaque");
132
+ if (policyRefreshExecutable && (securityTriggers.length > 0 || opaqueTriggers.length > 0))
133
+ return "policy_refresh";
134
+ if (securityTriggers.length > 0 || failures.some(({ failure }) => failure.code === "tls_failed")) {
135
+ return new ConnectErrorV3("transport_security_failed", { kind: "terminal" });
136
+ }
137
+ if (opaqueTriggers.length > 0)
138
+ return new ConnectErrorV3("connection_failed", { kind: "terminal" });
139
+ if (failures.length > 0 && failures.every(({ failure }) => failure.code === "tls_unsupported")) {
140
+ return new ConnectErrorV3("transport_security_unsupported", { kind: "terminal" });
141
+ }
142
+ const ordinary = failures.filter(({ failure }) => failure.code === "connection_failed");
143
+ if (ordinary.length > 0) {
144
+ return new ConnectErrorV3("connection_failed", aggregateRetryDispositionsV3(ordinary.map(({ disposition }) => disposition ?? { kind: "retryable" })));
145
+ }
146
+ return new ConnectErrorV3("connection_failed", { kind: "terminal" });
147
+ }
148
+ /** Records policy-refresh provenance at the A-result linearization point. */
149
+ export function blockPolicyRefreshTriggersV3(path, failures, cycle) {
150
+ const triggerKeys = new Set();
151
+ for (const { candidate, failure } of failures) {
152
+ if (!isPolicyTriggerV3(candidate, failure))
153
+ continue;
154
+ cycle.recordPolicyTrigger(failure.code === "connection_failed" && failure.detail === "browser_pin_opaque");
155
+ const key = endpointKeyV3(path, candidate);
156
+ triggerKeys.add(key);
157
+ if (candidate.tls.mode === "pin") {
158
+ cycle.blockPinPolicy(key, tlsPolicyDigestV3(candidate.tls).hashBase64URL);
159
+ }
160
+ }
161
+ return triggerKeys;
162
+ }
163
+ function blockedOrDowngraded(candidate, endpointKey, cycle) {
164
+ if (cycle.isSecurityLockedEndpoint(endpointKey) && candidate.tls.mode === "ca")
165
+ return true;
166
+ if (candidate.tls.mode === "ca")
167
+ return false;
168
+ return cycle.isBlocked(endpointKey, tlsPolicyDigestV3(candidate.tls).hashBase64URL);
169
+ }
170
+ function isPolicyTriggerV3(candidate, failure) {
171
+ return isNativePolicyTriggerV3(candidate, failure) ||
172
+ (candidate.tls.mode === "pin" && failure.code === "connection_failed" &&
173
+ failure.detail === "browser_pin_opaque");
174
+ }
175
+ function isNativePolicyTriggerV3(candidate, failure) {
176
+ return candidate.tls.mode === "pin" && (failure.code === "tls_policy_expired" ||
177
+ (failure.code === "tls_failed" &&
178
+ (failure.detail === "pin_mismatch" || failure.detail === "unknown")));
179
+ }
180
+ export class ControllerRetryWaitV3 {
181
+ #clock;
182
+ #waiting = false;
183
+ #manual = false;
184
+ #absoluteDeadline;
185
+ #wake;
186
+ constructor(clock = defaultControllerClockV3()) {
187
+ this.#clock = clock;
188
+ }
189
+ retryNow() {
190
+ if (!this.#waiting)
191
+ return false;
192
+ if (this.#absoluteDeadline !== undefined &&
193
+ this.#clock.wallNowMilliseconds() < this.#absoluteDeadline)
194
+ return false;
195
+ this.#manual = true;
196
+ this.#wake?.();
197
+ return true;
198
+ }
199
+ async wait(disposition, consecutiveFailure, signal) {
200
+ const validated = validateControllerWaitDisposition(disposition);
201
+ if (validated.kind === "terminal" || signal.aborted)
202
+ return false;
203
+ if (this.#waiting)
204
+ throw new Error("Flowersec v3 retry wait is already active");
205
+ const backoffDeadline = saturatingAddMilliseconds(this.#clock.monotonicNowMilliseconds(), controllerBackoffForWait(consecutiveFailure));
206
+ this.#waiting = true;
207
+ this.#manual = false;
208
+ this.#absoluteDeadline = validated.kind === "retry_after"
209
+ ? validated.absoluteUnixMilliseconds
210
+ : undefined;
211
+ try {
212
+ while (!signal.aborted) {
213
+ const wallNow = this.#clock.wallNowMilliseconds();
214
+ const monoNow = this.#clock.monotonicNowMilliseconds();
215
+ const wallRemaining = this.#absoluteDeadline === undefined
216
+ ? 0
217
+ : saturatingDifferenceMilliseconds(this.#absoluteDeadline, wallNow);
218
+ const monoRemaining = this.#manual ? 0 : saturatingDifferenceMilliseconds(backoffDeadline, monoNow);
219
+ if (wallRemaining === 0 && monoRemaining === 0)
220
+ return true;
221
+ const delay = Math.max(1, Math.min(WALL_CLOCK_RECHECK_MILLISECONDS, ...(wallRemaining === 0 ? [] : [wallRemaining]), ...(monoRemaining === 0 ? [] : [monoRemaining])));
222
+ await this.#sleepOrWake(delay, signal);
223
+ }
224
+ return false;
225
+ }
226
+ catch (error) {
227
+ if (signal.aborted)
228
+ return false;
229
+ throw error;
230
+ }
231
+ finally {
232
+ this.#waiting = false;
233
+ this.#manual = false;
234
+ this.#absoluteDeadline = undefined;
235
+ this.#wake = undefined;
236
+ }
237
+ }
238
+ async #sleepOrWake(milliseconds, signal) {
239
+ let wake;
240
+ const manual = new Promise((resolve) => { wake = resolve; });
241
+ this.#wake = wake;
242
+ try {
243
+ await Promise.race([this.#clock.sleep(milliseconds, signal), manual]);
244
+ }
245
+ finally {
246
+ if (this.#wake === wake)
247
+ this.#wake = undefined;
248
+ }
249
+ }
250
+ }
251
+ function saturatingAddMilliseconds(value, increment) {
252
+ if (!Number.isFinite(value) || !Number.isFinite(increment) || increment < 0)
253
+ return Number.MAX_SAFE_INTEGER;
254
+ if (value >= Number.MAX_SAFE_INTEGER - increment)
255
+ return Number.MAX_SAFE_INTEGER;
256
+ return value + increment;
257
+ }
258
+ function saturatingDifferenceMilliseconds(deadline, now) {
259
+ if (!Number.isFinite(deadline) || !Number.isFinite(now) || deadline <= now)
260
+ return 0;
261
+ const difference = deadline - now;
262
+ return Number.isSafeInteger(difference) ? difference : Number.MAX_SAFE_INTEGER;
263
+ }
264
+ function validateControllerWaitDisposition(value) {
265
+ if (value.kind === "terminal" || value.kind === "retryable")
266
+ return value;
267
+ if (value.kind !== "retry_after" || !Number.isSafeInteger(value.absoluteUnixMilliseconds) ||
268
+ value.absoluteUnixMilliseconds < 0 || value.absoluteUnixMilliseconds > 253_402_300_799_999) {
269
+ throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
270
+ }
271
+ return value;
272
+ }
273
+ function controllerBackoffForWait(consecutiveFailure) {
274
+ if (!Number.isSafeInteger(consecutiveFailure) || consecutiveFailure < 1) {
275
+ throw new RangeError("consecutive failure ordinal must be positive");
276
+ }
277
+ if (consecutiveFailure >= 8)
278
+ return 30_000;
279
+ return 250 * 2 ** (consecutiveFailure - 1);
280
+ }
281
+ function defaultControllerClockV3() {
282
+ return Object.freeze({
283
+ wallNowMilliseconds: Date.now,
284
+ monotonicNowMilliseconds: () => Math.floor(performance.now()),
285
+ sleep: async (milliseconds, signal) => await new Promise((resolve, reject) => {
286
+ if (signal.aborted) {
287
+ reject(signal.reason);
288
+ return;
289
+ }
290
+ const timer = setTimeout(finish, milliseconds);
291
+ const abort = () => {
292
+ clearTimeout(timer);
293
+ signal.removeEventListener("abort", abort);
294
+ reject(signal.reason);
295
+ };
296
+ function finish() {
297
+ signal.removeEventListener("abort", abort);
298
+ resolve();
299
+ }
300
+ signal.addEventListener("abort", abort, { once: true });
301
+ }),
302
+ });
303
+ }
@@ -0,0 +1,81 @@
1
+ import { CipherSuiteV3 } from "./protocol.js";
2
+ import { FLOWERSEC_V3_PROFILE } from "./transportConstants.js";
3
+ export declare enum HandshakeMessageTypeV3 {
4
+ ClientInit = 1,
5
+ ServerFinished = 2,
6
+ ClientFinished = 3
7
+ }
8
+ export type HandshakeFrameV3 = Readonly<{
9
+ type: HandshakeMessageTypeV3;
10
+ payload: Uint8Array;
11
+ raw: Uint8Array;
12
+ }>;
13
+ export type ClientInitV3 = Readonly<{
14
+ profile: typeof FLOWERSEC_V3_PROFILE;
15
+ channelID: string;
16
+ sessionContractHash: Uint8Array;
17
+ clientRole: 1;
18
+ suite: CipherSuiteV3;
19
+ clientEphemeralPublic: Uint8Array;
20
+ nonceC: Uint8Array;
21
+ selectedFeatures: number;
22
+ maxInboundStreams: number;
23
+ clientAdmissionBinding: Uint8Array;
24
+ clientEndpointInstanceID: string;
25
+ }>;
26
+ export type ServerFinishedCoreV3 = Readonly<{
27
+ suite: CipherSuiteV3;
28
+ handshakeID: Uint8Array;
29
+ serverEphemeralPublic: Uint8Array;
30
+ nonceS: Uint8Array;
31
+ sessionContractHash: Uint8Array;
32
+ selectedFeatures: number;
33
+ maxInboundStreams: number;
34
+ serverAdmissionBinding: Uint8Array;
35
+ serverEndpointInstanceID: string;
36
+ }>;
37
+ export type ServerFinishedV3 = Readonly<{
38
+ core: ServerFinishedCoreV3;
39
+ serverConfirm: Uint8Array;
40
+ }>;
41
+ export type ClientFinishedV3 = Readonly<{
42
+ handshakeID: Uint8Array;
43
+ clientConfirm: Uint8Array;
44
+ }>;
45
+ export type HandshakeExpectationsV3 = Readonly<{
46
+ path: "direct" | "tunnel";
47
+ channelID: string;
48
+ sessionContractHash: Uint8Array;
49
+ suite: CipherSuiteV3;
50
+ maxInboundStreams: number;
51
+ admissionBinding: Uint8Array;
52
+ expectedEndpointInstanceID: string;
53
+ }>;
54
+ export declare function encodeControlPrefaceV3(): Uint8Array;
55
+ export declare function parseControlPrefaceV3(raw: Uint8Array): void;
56
+ export declare function encodeHandshakeFrameV3(type: HandshakeMessageTypeV3, payload: Uint8Array): Uint8Array;
57
+ export declare function decodeHandshakeFrameV3(raw: Uint8Array): HandshakeFrameV3;
58
+ export declare function encodeClientInitV3(message: ClientInitV3): Uint8Array;
59
+ export declare function decodeClientInitV3(raw: Uint8Array): ClientInitV3;
60
+ export declare function encodeServerFinishedCoreV3(core: ServerFinishedCoreV3, suite: CipherSuiteV3): Uint8Array;
61
+ export declare function encodeServerFinishedV3(message: ServerFinishedV3, suite: CipherSuiteV3): Uint8Array;
62
+ export declare function decodeServerFinishedV3(raw: Uint8Array, suite: CipherSuiteV3): ServerFinishedV3;
63
+ export declare function encodeClientFinishedCoreV3(handshakeID: Uint8Array): Uint8Array;
64
+ export declare function encodeClientFinishedV3(message: ClientFinishedV3): Uint8Array;
65
+ export declare function decodeClientFinishedV3(raw: Uint8Array): ClientFinishedV3;
66
+ export declare function generateEphemeralKeyV3(suite: CipherSuiteV3, entropy: (length: number) => Uint8Array): Readonly<{
67
+ privateKey: Uint8Array;
68
+ publicKey: Uint8Array;
69
+ }>;
70
+ export declare function ephemeralPublicKeyV3(suite: CipherSuiteV3, privateKey: Uint8Array): Uint8Array;
71
+ export declare function computeSharedSecretV3(suite: CipherSuiteV3, privateKey: Uint8Array, peerPublicKey: Uint8Array): Uint8Array;
72
+ export declare function deriveHandshakePRKV3(psk: Uint8Array, sharedSecret: Uint8Array): Uint8Array;
73
+ export declare function computeHandshakeH0V3(controlPrefaceRaw: Uint8Array, clientInitRaw: Uint8Array): Uint8Array;
74
+ export declare function computeHandshakeH1V3(h0: Uint8Array, serverCoreRaw: Uint8Array): Uint8Array;
75
+ export declare function computeServerConfirmV3(handshakePRK: Uint8Array, h1: Uint8Array): Uint8Array;
76
+ export declare function computeHandshakeH2V3(h1: Uint8Array, serverFinishedRaw: Uint8Array, clientCoreRaw: Uint8Array): Uint8Array;
77
+ export declare function computeClientConfirmV3(handshakePRK: Uint8Array, h2: Uint8Array): Uint8Array;
78
+ export declare function computeHandshakeH3V3(h2: Uint8Array, clientFinishedRaw: Uint8Array): Uint8Array;
79
+ export declare function deriveSessionPRKV3(h3: Uint8Array, handshakePRK: Uint8Array): Uint8Array;
80
+ export declare function validateClientInitV3(message: ClientInitV3, expected: HandshakeExpectationsV3): void;
81
+ export declare function validateServerFinishedV3(message: ServerFinishedV3, expected: HandshakeExpectationsV3): void;