@floegence/flowersec-core 3.0.4 → 3.1.1
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.
- package/README.md +21 -5
- package/THIRD_PARTY_NOTICES.md +5 -5
- package/dist/browser/index.d.ts +6 -2
- package/dist/browser/index.js +3 -1
- package/dist/facade.d.ts +25 -8
- package/dist/facade.js +15 -4
- package/dist/interop/serverParityPeer.js +219 -3
- package/dist/node/index.d.ts +26 -10
- package/dist/node/index.js +12 -4
- package/dist/public/contract.d.ts +6 -3
- package/dist/v2/unreliableMessage.js +8 -6
- package/dist/v3/connectionController.d.ts +10 -1
- package/dist/v3/connectionController.js +70 -32
- package/dist/v3/controller.js +14 -4
- package/dist/v3/security.d.ts +15 -4
- package/dist/v3/security.js +32 -12
- package/dist/v3/unreliableMessage.js +8 -6
- package/package.json +3 -3
- package/sbom/cyclonedx.json +31 -31
- package/sbom/spdx.json +36 -36
|
@@ -57,6 +57,14 @@ export type ConnectionControllerSnapshotV3<Session extends ManagedSessionV3 = Ma
|
|
|
57
57
|
failure?: ConnectionControllerFailureV3;
|
|
58
58
|
retryDisposition?: RetryDispositionV3;
|
|
59
59
|
}>;
|
|
60
|
+
export type ConnectionDiagnosticV3 = Readonly<{
|
|
61
|
+
state: ConnectionControllerStateV3;
|
|
62
|
+
attempt: number;
|
|
63
|
+
failure?: ConnectionControllerFailureV3;
|
|
64
|
+
retryDisposition?: RetryDispositionV3;
|
|
65
|
+
}>;
|
|
66
|
+
/** Produces a stable diagnostic value without retaining a Session or transport detail. */
|
|
67
|
+
export declare function connectionDiagnosticV3(snapshot: ConnectionControllerSnapshotV3): ConnectionDiagnosticV3;
|
|
60
68
|
export type ConnectionControllerOptionsV3 = Readonly<{
|
|
61
69
|
maximumAttempts?: number;
|
|
62
70
|
clock?: ControllerClockV3;
|
|
@@ -68,7 +76,8 @@ export declare class ConnectionControllerV3Error extends Error {
|
|
|
68
76
|
readonly code: "failed" | "closed" | "canceled";
|
|
69
77
|
readonly failure?: ConnectionControllerFailureV3 | undefined;
|
|
70
78
|
readonly retryDisposition?: RetryDispositionV3 | undefined;
|
|
71
|
-
|
|
79
|
+
readonly diagnostic: ConnectionDiagnosticV3;
|
|
80
|
+
constructor(code: "failed" | "closed" | "canceled", failure?: ConnectionControllerFailureV3 | undefined, retryDisposition?: RetryDispositionV3 | undefined, diagnostic?: ConnectionDiagnosticV3);
|
|
72
81
|
}
|
|
73
82
|
export declare class ConnectionControllerV3<Session extends ManagedSessionV3 = ManagedSessionV3> {
|
|
74
83
|
#private;
|
|
@@ -8,16 +8,40 @@ const ARTIFACT_SOURCE_FAILURE_CODES_V3 = new Set([
|
|
|
8
8
|
"connection_failed",
|
|
9
9
|
"expired_artifact",
|
|
10
10
|
]);
|
|
11
|
+
/** Produces a stable diagnostic value without retaining a Session or transport detail. */
|
|
12
|
+
export function connectionDiagnosticV3(snapshot) {
|
|
13
|
+
const failure = snapshot.failure === undefined
|
|
14
|
+
? undefined
|
|
15
|
+
: Object.freeze({ phase: snapshot.failure.phase, code: snapshot.failure.code });
|
|
16
|
+
const retryDisposition = snapshot.retryDisposition === undefined
|
|
17
|
+
? undefined
|
|
18
|
+
: validateRetryDispositionV3(snapshot.retryDisposition);
|
|
19
|
+
return Object.freeze({
|
|
20
|
+
state: snapshot.state,
|
|
21
|
+
attempt: snapshot.attempt,
|
|
22
|
+
...(failure === undefined ? {} : { failure }),
|
|
23
|
+
...(retryDisposition === undefined ? {} : { retryDisposition }),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
11
26
|
export class ConnectionControllerV3Error extends Error {
|
|
12
27
|
code;
|
|
13
28
|
failure;
|
|
14
29
|
retryDisposition;
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
diagnostic;
|
|
31
|
+
constructor(code, failure, retryDisposition, diagnostic) {
|
|
32
|
+
super(`Flowersec connection controller stopped (code=${code})`);
|
|
17
33
|
this.code = code;
|
|
18
34
|
this.failure = failure;
|
|
19
35
|
this.retryDisposition = retryDisposition;
|
|
20
36
|
this.name = "ConnectionControllerError";
|
|
37
|
+
this.diagnostic = diagnostic ?? Object.freeze({
|
|
38
|
+
state: code === "failed" ? "failed" : code === "closed" ? "closed" : "idle",
|
|
39
|
+
attempt: 0,
|
|
40
|
+
...(failure === undefined ? {} : { failure }),
|
|
41
|
+
...(retryDisposition === undefined ? {} : {
|
|
42
|
+
retryDisposition: validateRetryDispositionV3(retryDisposition),
|
|
43
|
+
}),
|
|
44
|
+
});
|
|
21
45
|
}
|
|
22
46
|
}
|
|
23
47
|
export class ConnectionControllerV3 {
|
|
@@ -95,15 +119,22 @@ export class ConnectionControllerV3 {
|
|
|
95
119
|
if (this.currentSession !== undefined)
|
|
96
120
|
return this.currentSession;
|
|
97
121
|
if (this.#state === "failed") {
|
|
98
|
-
|
|
122
|
+
const snapshot = this.#snapshot();
|
|
123
|
+
throw new ConnectionControllerV3Error("failed", this.#failure, this.#disposition, connectionDiagnosticV3(snapshot));
|
|
99
124
|
}
|
|
100
125
|
if (this.#state === "closed") {
|
|
101
|
-
|
|
126
|
+
const snapshot = this.#snapshot();
|
|
127
|
+
throw new ConnectionControllerV3Error("closed", this.#failure, this.#disposition, connectionDiagnosticV3(snapshot));
|
|
128
|
+
}
|
|
129
|
+
if (options.signal?.aborted === true) {
|
|
130
|
+
throw new ConnectionControllerV3Error("canceled", undefined, undefined, connectionDiagnosticV3(this.#snapshot()));
|
|
102
131
|
}
|
|
103
|
-
if (options.signal?.aborted === true)
|
|
104
|
-
throw new ConnectionControllerV3Error("canceled");
|
|
105
132
|
return await new Promise((resolve, reject) => {
|
|
133
|
+
let settled = false;
|
|
106
134
|
const finish = (session, error) => {
|
|
135
|
+
if (settled)
|
|
136
|
+
return;
|
|
137
|
+
settled = true;
|
|
107
138
|
this.#listeners.delete(listener);
|
|
108
139
|
options.signal?.removeEventListener("abort", canceled);
|
|
109
140
|
session === undefined ? reject(error) : resolve(session);
|
|
@@ -113,15 +144,22 @@ export class ConnectionControllerV3 {
|
|
|
113
144
|
finish(snapshot.currentSession);
|
|
114
145
|
}
|
|
115
146
|
else if (snapshot.state === "failed") {
|
|
116
|
-
finish(undefined, new ConnectionControllerV3Error("failed", snapshot.failure, snapshot.retryDisposition));
|
|
147
|
+
finish(undefined, new ConnectionControllerV3Error("failed", snapshot.failure, snapshot.retryDisposition, connectionDiagnosticV3(snapshot)));
|
|
117
148
|
}
|
|
118
149
|
else if (snapshot.state === "closed") {
|
|
119
|
-
finish(undefined, new ConnectionControllerV3Error("closed", snapshot.failure, snapshot.retryDisposition));
|
|
150
|
+
finish(undefined, new ConnectionControllerV3Error("closed", snapshot.failure, snapshot.retryDisposition, connectionDiagnosticV3(snapshot)));
|
|
120
151
|
}
|
|
121
152
|
};
|
|
122
|
-
const canceled = () =>
|
|
153
|
+
const canceled = () => {
|
|
154
|
+
const snapshot = this.#snapshot();
|
|
155
|
+
finish(undefined, new ConnectionControllerV3Error("canceled", undefined, undefined, connectionDiagnosticV3(snapshot)));
|
|
156
|
+
};
|
|
123
157
|
this.#listeners.add(listener);
|
|
124
158
|
options.signal?.addEventListener("abort", canceled, { once: true });
|
|
159
|
+
if (options.signal?.aborted === true)
|
|
160
|
+
canceled();
|
|
161
|
+
else
|
|
162
|
+
listener(this.#snapshot());
|
|
125
163
|
});
|
|
126
164
|
}
|
|
127
165
|
close() {
|
|
@@ -190,10 +228,10 @@ export class ConnectionControllerV3 {
|
|
|
190
228
|
if (acquisition.kind === "failure") {
|
|
191
229
|
const error = sourceFailure(acquisition);
|
|
192
230
|
const ordinal = this.#cycle.recordFailedAcquisitionOrLease();
|
|
193
|
-
this.#recordFailure("artifact", error.code, error.
|
|
194
|
-
if (error.
|
|
231
|
+
this.#recordFailure("artifact", error.code, error.retryDisposition);
|
|
232
|
+
if (error.retryDisposition.kind === "terminal" || this.#attemptBudgetExhausted())
|
|
195
233
|
return;
|
|
196
|
-
pendingDisposition = error.
|
|
234
|
+
pendingDisposition = error.retryDisposition;
|
|
197
235
|
void ordinal;
|
|
198
236
|
continue;
|
|
199
237
|
}
|
|
@@ -222,7 +260,7 @@ export class ConnectionControllerV3 {
|
|
|
222
260
|
await retireArtifactLeaseV3(claim);
|
|
223
261
|
this.#cycle.recordFailedAcquisitionOrLease();
|
|
224
262
|
const error = new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
|
|
225
|
-
this.#recordFailure("connect", error.code, error.
|
|
263
|
+
this.#recordFailure("connect", error.code, error.retryDisposition);
|
|
226
264
|
return;
|
|
227
265
|
}
|
|
228
266
|
try {
|
|
@@ -232,12 +270,12 @@ export class ConnectionControllerV3 {
|
|
|
232
270
|
await retireArtifactLeaseV3(claim);
|
|
233
271
|
this.#cycle.recordFailedAcquisitionOrLease();
|
|
234
272
|
const error = new ConnectErrorV3("expired_artifact", { kind: "retryable" });
|
|
235
|
-
this.#recordFailure("connect", error.code, error.
|
|
273
|
+
this.#recordFailure("connect", error.code, error.retryDisposition);
|
|
236
274
|
if (this.#attemptBudgetExhausted())
|
|
237
275
|
return;
|
|
238
276
|
next = "primary";
|
|
239
277
|
replacementContext = undefined;
|
|
240
|
-
pendingDisposition = error.
|
|
278
|
+
pendingDisposition = error.retryDisposition;
|
|
241
279
|
continue;
|
|
242
280
|
}
|
|
243
281
|
const candidates = next === "replacement"
|
|
@@ -249,7 +287,7 @@ export class ConnectionControllerV3 {
|
|
|
249
287
|
const error = next === "replacement"
|
|
250
288
|
? replacementTerminal(replacementContext)
|
|
251
289
|
: this.#cycle.blockedPolicyTerminal();
|
|
252
|
-
this.#recordFailure("connect", error.code, error.
|
|
290
|
+
this.#recordFailure("connect", error.code, error.retryDisposition);
|
|
253
291
|
return;
|
|
254
292
|
}
|
|
255
293
|
let rawResult;
|
|
@@ -319,12 +357,12 @@ export class ConnectionControllerV3 {
|
|
|
319
357
|
let sessionFailure;
|
|
320
358
|
try {
|
|
321
359
|
sessionFailure = this.#projectSessionFailure(termination.error);
|
|
322
|
-
validateRetryDispositionV3(sessionFailure.
|
|
360
|
+
validateRetryDispositionV3(sessionFailure.retryDisposition);
|
|
323
361
|
}
|
|
324
362
|
catch {
|
|
325
363
|
sessionFailure = new ConnectErrorV3("connection_failed", { kind: "terminal" });
|
|
326
364
|
}
|
|
327
|
-
const disposition = sessionFailure.
|
|
365
|
+
const disposition = sessionFailure.retryDisposition;
|
|
328
366
|
this.#cycle.recordFailedAcquisitionOrLease();
|
|
329
367
|
this.#recordFailure("session", sessionFailure.code, disposition);
|
|
330
368
|
if (disposition.kind === "terminal")
|
|
@@ -345,13 +383,13 @@ export class ConnectionControllerV3 {
|
|
|
345
383
|
const error = next === "replacement" && result.error.code !== "expired_artifact"
|
|
346
384
|
? replacementTerminal(replacementContext)
|
|
347
385
|
: result.error;
|
|
348
|
-
const disposition = error.
|
|
386
|
+
const disposition = error.retryDisposition;
|
|
349
387
|
this.#recordFailure("connect", error.code, disposition);
|
|
350
388
|
if (disposition.kind === "terminal" || this.#attemptBudgetExhausted())
|
|
351
389
|
return;
|
|
352
390
|
next = "primary";
|
|
353
391
|
replacementContext = undefined;
|
|
354
|
-
pendingDisposition = result.error.
|
|
392
|
+
pendingDisposition = result.error.retryDisposition;
|
|
355
393
|
continue;
|
|
356
394
|
}
|
|
357
395
|
if (result.kind === "post_spend_failure") {
|
|
@@ -365,12 +403,12 @@ export class ConnectionControllerV3 {
|
|
|
365
403
|
};
|
|
366
404
|
}
|
|
367
405
|
this.#cycle.recordFailedAcquisitionOrLease();
|
|
368
|
-
this.#recordFailure("connect", postSpendResult.error.code, postSpendResult.error.
|
|
369
|
-
if (postSpendResult.error.
|
|
406
|
+
this.#recordFailure("connect", postSpendResult.error.code, postSpendResult.error.retryDisposition);
|
|
407
|
+
if (postSpendResult.error.retryDisposition.kind === "terminal" || this.#attemptBudgetExhausted())
|
|
370
408
|
return;
|
|
371
409
|
next = "primary";
|
|
372
410
|
replacementContext = undefined;
|
|
373
|
-
pendingDisposition = postSpendResult.error.
|
|
411
|
+
pendingDisposition = postSpendResult.error.retryDisposition;
|
|
374
412
|
continue;
|
|
375
413
|
}
|
|
376
414
|
if (artifactLeaseStateV3(claim) !== "claimed") {
|
|
@@ -382,17 +420,17 @@ export class ConnectionControllerV3 {
|
|
|
382
420
|
this.#cycle.recordFailedAcquisitionOrLease();
|
|
383
421
|
if (isExpired(artifact, this.#nowUnixSeconds)) {
|
|
384
422
|
const error = new ConnectErrorV3("expired_artifact", { kind: "retryable" });
|
|
385
|
-
this.#recordFailure("connect", error.code, error.
|
|
423
|
+
this.#recordFailure("connect", error.code, error.retryDisposition);
|
|
386
424
|
if (this.#attemptBudgetExhausted())
|
|
387
425
|
return;
|
|
388
426
|
next = "primary";
|
|
389
427
|
replacementContext = undefined;
|
|
390
|
-
pendingDisposition = error.
|
|
428
|
+
pendingDisposition = error.retryDisposition;
|
|
391
429
|
continue;
|
|
392
430
|
}
|
|
393
431
|
if (next === "replacement") {
|
|
394
432
|
const error = replacementTerminal(replacementContext);
|
|
395
|
-
this.#recordFailure("connect", error.code, error.
|
|
433
|
+
this.#recordFailure("connect", error.code, error.retryDisposition);
|
|
396
434
|
return;
|
|
397
435
|
}
|
|
398
436
|
const triggerKeys = blockPolicyRefreshTriggersV3(artifact.path.kind, result.failures, this.#cycle);
|
|
@@ -406,14 +444,14 @@ export class ConnectionControllerV3 {
|
|
|
406
444
|
}
|
|
407
445
|
if (triggerKeys.size > 0 && refresh.code === "connection_failed") {
|
|
408
446
|
const terminal = this.#cycle.blockedPolicyTerminal();
|
|
409
|
-
this.#recordFailure("connect", terminal.code, terminal.
|
|
447
|
+
this.#recordFailure("connect", terminal.code, terminal.retryDisposition);
|
|
410
448
|
return;
|
|
411
449
|
}
|
|
412
|
-
this.#recordFailure("connect", refresh.code, refresh.
|
|
413
|
-
if (refresh.
|
|
450
|
+
this.#recordFailure("connect", refresh.code, refresh.retryDisposition);
|
|
451
|
+
if (refresh.retryDisposition.kind === "terminal" || this.#attemptBudgetExhausted())
|
|
414
452
|
return;
|
|
415
453
|
next = "primary";
|
|
416
|
-
pendingDisposition = refresh.
|
|
454
|
+
pendingDisposition = refresh.retryDisposition;
|
|
417
455
|
}
|
|
418
456
|
}
|
|
419
457
|
async #acquire() {
|
|
@@ -467,7 +505,7 @@ export class ConnectionControllerV3 {
|
|
|
467
505
|
return {
|
|
468
506
|
kind: "failure",
|
|
469
507
|
code: error.code,
|
|
470
|
-
disposition: validateRetryDispositionV3(error.
|
|
508
|
+
disposition: validateRetryDispositionV3(error.retryDisposition),
|
|
471
509
|
};
|
|
472
510
|
}
|
|
473
511
|
catch {
|
|
@@ -696,7 +734,7 @@ function normalizeLeaseAttemptResultV3(value, candidates, capability) {
|
|
|
696
734
|
const error = Reflect.get(value, "error");
|
|
697
735
|
if (!(error instanceof ConnectErrorV3) || !CONNECT_ERROR_CODES_V3.has(error.code))
|
|
698
736
|
return undefined;
|
|
699
|
-
const normalizedError = new ConnectErrorV3(error.code, validateRetryDispositionV3(error.
|
|
737
|
+
const normalizedError = new ConnectErrorV3(error.code, validateRetryDispositionV3(error.retryDisposition));
|
|
700
738
|
return Object.freeze({ kind, error: normalizedError });
|
|
701
739
|
}
|
|
702
740
|
catch {
|
package/dist/v3/controller.js
CHANGED
|
@@ -206,7 +206,7 @@ export class ControllerRetryWaitV3 {
|
|
|
206
206
|
this.#waiting = true;
|
|
207
207
|
this.#manual = false;
|
|
208
208
|
this.#absoluteDeadline = validated.kind === "retry_after"
|
|
209
|
-
? validated.
|
|
209
|
+
? validated.notBeforeUnixMilliseconds
|
|
210
210
|
: undefined;
|
|
211
211
|
try {
|
|
212
212
|
while (!signal.aborted) {
|
|
@@ -264,11 +264,21 @@ function saturatingDifferenceMilliseconds(deadline, now) {
|
|
|
264
264
|
function validateControllerWaitDisposition(value) {
|
|
265
265
|
if (value.kind === "terminal" || value.kind === "retryable")
|
|
266
266
|
return value;
|
|
267
|
-
if (value.kind !== "retry_after"
|
|
268
|
-
value.absoluteUnixMilliseconds < 0 || value.absoluteUnixMilliseconds > 253_402_300_799_999) {
|
|
267
|
+
if (value.kind !== "retry_after") {
|
|
269
268
|
throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
|
|
270
269
|
}
|
|
271
|
-
|
|
270
|
+
const deadline = value.notBeforeUnixMilliseconds ?? value.absoluteUnixMilliseconds;
|
|
271
|
+
if (deadline === undefined || !Number.isSafeInteger(deadline) || deadline < 0 ||
|
|
272
|
+
deadline > 253_402_300_799_999 || value.notBeforeUnixMilliseconds !== undefined &&
|
|
273
|
+
value.absoluteUnixMilliseconds !== undefined &&
|
|
274
|
+
value.notBeforeUnixMilliseconds !== value.absoluteUnixMilliseconds) {
|
|
275
|
+
throw new ConnectErrorV3("artifact_invalid", { kind: "terminal" });
|
|
276
|
+
}
|
|
277
|
+
return Object.freeze({
|
|
278
|
+
kind: "retry_after",
|
|
279
|
+
notBeforeUnixMilliseconds: deadline,
|
|
280
|
+
absoluteUnixMilliseconds: deadline,
|
|
281
|
+
});
|
|
272
282
|
}
|
|
273
283
|
function controllerBackoffForWait(consecutiveFailure) {
|
|
274
284
|
if (!Number.isSafeInteger(consecutiveFailure) || consecutiveFailure < 1) {
|
package/dist/v3/security.d.ts
CHANGED
|
@@ -13,12 +13,22 @@ export type RetryDispositionV3 = Readonly<{
|
|
|
13
13
|
kind: "retryable";
|
|
14
14
|
}> | Readonly<{
|
|
15
15
|
kind: "retry_after";
|
|
16
|
+
notBeforeUnixMilliseconds: number;
|
|
17
|
+
/** @deprecated Use notBeforeUnixMilliseconds. */
|
|
18
|
+
absoluteUnixMilliseconds?: number;
|
|
19
|
+
}>;
|
|
20
|
+
type RetryDispositionInputV3 = RetryDispositionV3 | Readonly<{
|
|
21
|
+
kind: "retry_after";
|
|
22
|
+
/** @deprecated Use notBeforeUnixMilliseconds. */
|
|
16
23
|
absoluteUnixMilliseconds: number;
|
|
24
|
+
notBeforeUnixMilliseconds?: number;
|
|
17
25
|
}>;
|
|
18
26
|
export declare class ConnectErrorV3 extends Error {
|
|
19
27
|
readonly code: PublicConnectErrorCodeV3;
|
|
20
|
-
readonly
|
|
21
|
-
constructor(code: PublicConnectErrorCodeV3, disposition:
|
|
28
|
+
readonly retryDisposition: RetryDispositionV3;
|
|
29
|
+
constructor(code: PublicConnectErrorCodeV3, disposition: RetryDispositionInputV3);
|
|
30
|
+
/** @deprecated Use retryDisposition. */
|
|
31
|
+
get disposition(): RetryDispositionV3;
|
|
22
32
|
}
|
|
23
33
|
export type TransportSecuritySnapshotV3 = Readonly<{
|
|
24
34
|
mode: "ca";
|
|
@@ -29,6 +39,7 @@ export type TransportSecuritySnapshotV3 = Readonly<{
|
|
|
29
39
|
}>;
|
|
30
40
|
export declare function snapshotTransportSecurityPolicyV3(policy: TransportSecurityPolicyV3, attemptNowUnixSeconds: number, supportedModes: readonly ("ca" | "pin")[]): TransportSecuritySnapshotV3;
|
|
31
41
|
export declare function projectTransportFailureV3(failure: TransportFailureV3, _policyMode: "ca" | "pin"): ConnectErrorV3;
|
|
32
|
-
export declare function validateRetryDispositionV3(value:
|
|
33
|
-
export declare function aggregateRetryDispositionsV3(values: readonly
|
|
42
|
+
export declare function validateRetryDispositionV3(value: RetryDispositionInputV3): RetryDispositionV3;
|
|
43
|
+
export declare function aggregateRetryDispositionsV3(values: readonly RetryDispositionInputV3[]): RetryDispositionV3;
|
|
34
44
|
export declare function controllerBackoffMillisecondsV3(consecutiveFailure: number): number;
|
|
45
|
+
export {};
|
package/dist/v3/security.js
CHANGED
|
@@ -11,13 +11,15 @@ export class TransportFailureV3 extends Error {
|
|
|
11
11
|
}
|
|
12
12
|
export class ConnectErrorV3 extends Error {
|
|
13
13
|
code;
|
|
14
|
-
|
|
14
|
+
retryDisposition;
|
|
15
15
|
constructor(code, disposition) {
|
|
16
16
|
super(`Flowersec connection failed (code=${code})`);
|
|
17
17
|
this.code = code;
|
|
18
|
-
this.disposition = disposition;
|
|
19
18
|
this.name = "ConnectError";
|
|
19
|
+
this.retryDisposition = validateRetryDispositionValueV3(disposition);
|
|
20
20
|
}
|
|
21
|
+
/** @deprecated Use retryDisposition. */
|
|
22
|
+
get disposition() { return this.retryDisposition; }
|
|
21
23
|
}
|
|
22
24
|
export function snapshotTransportSecurityPolicyV3(policy, attemptNowUnixSeconds, supportedModes) {
|
|
23
25
|
if (!Number.isSafeInteger(attemptNowUnixSeconds) || attemptNowUnixSeconds < 0) {
|
|
@@ -64,28 +66,26 @@ export function projectTransportFailureV3(failure, _policyMode) {
|
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
68
|
export function validateRetryDispositionV3(value) {
|
|
67
|
-
|
|
68
|
-
return
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if (value.kind !== "retry_after" || !Number.isSafeInteger(value.absoluteUnixMilliseconds) ||
|
|
72
|
-
value.absoluteUnixMilliseconds < 0 || value.absoluteUnixMilliseconds > 253_402_300_799_999) {
|
|
69
|
+
try {
|
|
70
|
+
return validateRetryDispositionValueV3(value);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
73
|
throw new ConnectErrorV3("artifact_invalid", terminal());
|
|
74
74
|
}
|
|
75
|
-
return Object.freeze({ kind: "retry_after", absoluteUnixMilliseconds: value.absoluteUnixMilliseconds });
|
|
76
75
|
}
|
|
77
76
|
export function aggregateRetryDispositionsV3(values) {
|
|
78
77
|
let latest;
|
|
79
78
|
let canRetry = false;
|
|
80
79
|
for (const input of values) {
|
|
81
80
|
const value = validateRetryDispositionV3(input);
|
|
82
|
-
if (value.kind === "retry_after")
|
|
83
|
-
latest = Math.max(latest ?? 0, value.
|
|
81
|
+
if (value.kind === "retry_after") {
|
|
82
|
+
latest = Math.max(latest ?? 0, value.notBeforeUnixMilliseconds);
|
|
83
|
+
}
|
|
84
84
|
else if (value.kind === "retryable")
|
|
85
85
|
canRetry = true;
|
|
86
86
|
}
|
|
87
87
|
if (latest !== undefined)
|
|
88
|
-
return
|
|
88
|
+
return retryAfter(latest);
|
|
89
89
|
return canRetry ? retryable() : terminal();
|
|
90
90
|
}
|
|
91
91
|
export function controllerBackoffMillisecondsV3(consecutiveFailure) {
|
|
@@ -98,3 +98,23 @@ export function controllerBackoffMillisecondsV3(consecutiveFailure) {
|
|
|
98
98
|
}
|
|
99
99
|
const terminal = () => Object.freeze({ kind: "terminal" });
|
|
100
100
|
const retryable = () => Object.freeze({ kind: "retryable" });
|
|
101
|
+
const retryAfter = (notBeforeUnixMilliseconds) => Object.freeze({
|
|
102
|
+
kind: "retry_after",
|
|
103
|
+
notBeforeUnixMilliseconds,
|
|
104
|
+
absoluteUnixMilliseconds: notBeforeUnixMilliseconds,
|
|
105
|
+
});
|
|
106
|
+
function validateRetryDispositionValueV3(value) {
|
|
107
|
+
if (value.kind === "terminal")
|
|
108
|
+
return terminal();
|
|
109
|
+
if (value.kind === "retryable")
|
|
110
|
+
return retryable();
|
|
111
|
+
const legacy = value.absoluteUnixMilliseconds;
|
|
112
|
+
const canonical = value.notBeforeUnixMilliseconds;
|
|
113
|
+
const deadline = canonical ?? legacy;
|
|
114
|
+
if (deadline === undefined ||
|
|
115
|
+
canonical !== undefined && legacy !== undefined && canonical !== legacy ||
|
|
116
|
+
!Number.isSafeInteger(deadline) || deadline < 0 || deadline > 253_402_300_799_999) {
|
|
117
|
+
throw new TypeError("invalid Flowersec retry-after deadline");
|
|
118
|
+
}
|
|
119
|
+
return retryAfter(deadline);
|
|
120
|
+
}
|
|
@@ -38,10 +38,12 @@ class InternalUnreliableMessageChannelV3 {
|
|
|
38
38
|
}
|
|
39
39
|
async send(message, options) {
|
|
40
40
|
throwIfAborted(options.signal);
|
|
41
|
-
if (!(message instanceof Uint8Array) || message.byteLength < 1
|
|
42
|
-
message.byteLength > UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V3) {
|
|
41
|
+
if (!(message instanceof Uint8Array) || message.byteLength < 1) {
|
|
43
42
|
throw new UnreliableMessageError("invalid_message");
|
|
44
43
|
}
|
|
44
|
+
if (message.byteLength > UNRELIABLE_MESSAGE_MAX_PLAINTEXT_BYTES_V3) {
|
|
45
|
+
throw new UnreliableMessageError("too_large");
|
|
46
|
+
}
|
|
45
47
|
const payload = message.slice();
|
|
46
48
|
const expiresAt = requireFutureExpiry(options.expiresAtUnixMs, this.now());
|
|
47
49
|
if (expiresAt === undefined)
|
|
@@ -74,8 +76,8 @@ class InternalUnreliableMessageChannelV3 {
|
|
|
74
76
|
});
|
|
75
77
|
}
|
|
76
78
|
catch (error) {
|
|
77
|
-
if (error
|
|
78
|
-
throw
|
|
79
|
+
if (isAbortError(error))
|
|
80
|
+
throw new UnreliableMessageError("canceled");
|
|
79
81
|
throw new UnreliableMessageError("operation_failed");
|
|
80
82
|
}
|
|
81
83
|
finally {
|
|
@@ -91,7 +93,7 @@ class InternalUnreliableMessageChannelV3 {
|
|
|
91
93
|
}
|
|
92
94
|
catch (error) {
|
|
93
95
|
if (isAbortError(error))
|
|
94
|
-
throw
|
|
96
|
+
throw new UnreliableMessageError("canceled");
|
|
95
97
|
throw new UnreliableMessageError("closed");
|
|
96
98
|
}
|
|
97
99
|
if (isPreviousVersionDatagram(wire)) {
|
|
@@ -241,7 +243,7 @@ function requireFutureExpiry(value, now) {
|
|
|
241
243
|
}
|
|
242
244
|
function throwIfAborted(signal) {
|
|
243
245
|
if (signal?.aborted === true)
|
|
244
|
-
throw new
|
|
246
|
+
throw new UnreliableMessageError("canceled");
|
|
245
247
|
}
|
|
246
248
|
function isAbortError(error) {
|
|
247
249
|
return error instanceof DOMException && error.name === "AbortError" ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floegence/flowersec-core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "Flowersec core TypeScript library for carrier-neutral encrypted sessions and multiplexed streams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
"vitest": "4.1.10"
|
|
94
94
|
},
|
|
95
95
|
"optionalDependencies": {
|
|
96
|
-
"@floegence/flowersec-node-native": "3.
|
|
96
|
+
"@floegence/flowersec-node-native": "3.1.1"
|
|
97
97
|
},
|
|
98
|
-
"flowersecSourceCommit": "
|
|
98
|
+
"flowersecSourceCommit": "ef37df09147b1b08f2a5aaaf3cd0760806afd312"
|
|
99
99
|
}
|
package/sbom/cyclonedx.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"bomFormat": "CycloneDX",
|
|
3
3
|
"specVersion": "1.5",
|
|
4
|
-
"serialNumber": "urn:uuid:
|
|
4
|
+
"serialNumber": "urn:uuid:77052be3-258a-5fb4-894e-2b8f821f3bdf",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
7
|
"component": {
|
|
8
8
|
"type": "library",
|
|
9
9
|
"name": "@floegence/flowersec-core",
|
|
10
|
-
"version": "3.
|
|
11
|
-
"purl": "pkg:npm/%40floegence/flowersec-core@3.
|
|
12
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-core@3.
|
|
10
|
+
"version": "3.1.1",
|
|
11
|
+
"purl": "pkg:npm/%40floegence/flowersec-core@3.1.1",
|
|
12
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-core@3.1.1"
|
|
13
13
|
},
|
|
14
14
|
"properties": [
|
|
15
15
|
{
|
|
16
16
|
"name": "flowersec:source-inventory-sha256",
|
|
17
|
-
"value": "
|
|
17
|
+
"value": "af1844fd4e36b6f817976846fd645bffb060c3d9dfa1f94f579d043b7a13f735"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
20
|
},
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
{
|
|
23
23
|
"type": "library",
|
|
24
24
|
"name": "@floegence/flowersec-node-native",
|
|
25
|
-
"version": "3.
|
|
26
|
-
"purl": "pkg:npm/%40floegence/flowersec-node-native@3.
|
|
27
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native@3.
|
|
25
|
+
"version": "3.1.1",
|
|
26
|
+
"purl": "pkg:npm/%40floegence/flowersec-node-native@3.1.1",
|
|
27
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native@3.1.1",
|
|
28
28
|
"licenses": [
|
|
29
29
|
{
|
|
30
30
|
"expression": "MIT"
|
|
@@ -50,9 +50,9 @@
|
|
|
50
50
|
{
|
|
51
51
|
"type": "library",
|
|
52
52
|
"name": "@floegence/flowersec-node-native-darwin-arm64",
|
|
53
|
-
"version": "3.
|
|
54
|
-
"purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.
|
|
55
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.
|
|
53
|
+
"version": "3.1.1",
|
|
54
|
+
"purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.1.1",
|
|
55
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.1.1",
|
|
56
56
|
"licenses": [
|
|
57
57
|
{
|
|
58
58
|
"expression": "MIT"
|
|
@@ -78,9 +78,9 @@
|
|
|
78
78
|
{
|
|
79
79
|
"type": "library",
|
|
80
80
|
"name": "@floegence/flowersec-node-native-darwin-x64",
|
|
81
|
-
"version": "3.
|
|
82
|
-
"purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.
|
|
83
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.
|
|
81
|
+
"version": "3.1.1",
|
|
82
|
+
"purl": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.1.1",
|
|
83
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.1.1",
|
|
84
84
|
"licenses": [
|
|
85
85
|
{
|
|
86
86
|
"expression": "MIT"
|
|
@@ -106,9 +106,9 @@
|
|
|
106
106
|
{
|
|
107
107
|
"type": "library",
|
|
108
108
|
"name": "@floegence/flowersec-node-native-linux-arm64-gnu",
|
|
109
|
-
"version": "3.
|
|
110
|
-
"purl": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.
|
|
111
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.
|
|
109
|
+
"version": "3.1.1",
|
|
110
|
+
"purl": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.1.1",
|
|
111
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.1.1",
|
|
112
112
|
"licenses": [
|
|
113
113
|
{
|
|
114
114
|
"expression": "MIT"
|
|
@@ -134,9 +134,9 @@
|
|
|
134
134
|
{
|
|
135
135
|
"type": "library",
|
|
136
136
|
"name": "@floegence/flowersec-node-native-linux-x64-gnu",
|
|
137
|
-
"version": "3.
|
|
138
|
-
"purl": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.
|
|
139
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.
|
|
137
|
+
"version": "3.1.1",
|
|
138
|
+
"purl": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.1.1",
|
|
139
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.1.1",
|
|
140
140
|
"licenses": [
|
|
141
141
|
{
|
|
142
142
|
"expression": "MIT"
|
|
@@ -330,9 +330,9 @@
|
|
|
330
330
|
],
|
|
331
331
|
"dependencies": [
|
|
332
332
|
{
|
|
333
|
-
"ref": "pkg:npm/%40floegence/flowersec-core@3.
|
|
333
|
+
"ref": "pkg:npm/%40floegence/flowersec-core@3.1.1",
|
|
334
334
|
"dependsOn": [
|
|
335
|
-
"pkg:npm/%40floegence/flowersec-node-native@3.
|
|
335
|
+
"pkg:npm/%40floegence/flowersec-node-native@3.1.1",
|
|
336
336
|
"pkg:npm/%40noble/ciphers@2.3.0",
|
|
337
337
|
"pkg:npm/%40noble/curves@2.3.0",
|
|
338
338
|
"pkg:npm/%40noble/hashes@2.3.0",
|
|
@@ -341,28 +341,28 @@
|
|
|
341
341
|
]
|
|
342
342
|
},
|
|
343
343
|
{
|
|
344
|
-
"ref": "pkg:npm/%40floegence/flowersec-node-native@3.
|
|
344
|
+
"ref": "pkg:npm/%40floegence/flowersec-node-native@3.1.1",
|
|
345
345
|
"dependsOn": [
|
|
346
|
-
"pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.
|
|
347
|
-
"pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.
|
|
348
|
-
"pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.
|
|
349
|
-
"pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.
|
|
346
|
+
"pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.1.1",
|
|
347
|
+
"pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.1.1",
|
|
348
|
+
"pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.1.1",
|
|
349
|
+
"pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.1.1"
|
|
350
350
|
]
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
|
-
"ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.
|
|
353
|
+
"ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-arm64@3.1.1",
|
|
354
354
|
"dependsOn": []
|
|
355
355
|
},
|
|
356
356
|
{
|
|
357
|
-
"ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.
|
|
357
|
+
"ref": "pkg:npm/%40floegence/flowersec-node-native-darwin-x64@3.1.1",
|
|
358
358
|
"dependsOn": []
|
|
359
359
|
},
|
|
360
360
|
{
|
|
361
|
-
"ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.
|
|
361
|
+
"ref": "pkg:npm/%40floegence/flowersec-node-native-linux-arm64-gnu@3.1.1",
|
|
362
362
|
"dependsOn": []
|
|
363
363
|
},
|
|
364
364
|
{
|
|
365
|
-
"ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.
|
|
365
|
+
"ref": "pkg:npm/%40floegence/flowersec-node-native-linux-x64-gnu@3.1.1",
|
|
366
366
|
"dependsOn": []
|
|
367
367
|
},
|
|
368
368
|
{
|