@floegence/flowersec-core 2.5.4 → 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.
- package/README.md +46 -18
- package/dist/browser/connectSessionV3.d.ts +13 -0
- package/dist/browser/connectSessionV3.js +75 -0
- package/dist/browser/index.d.ts +3 -2
- package/dist/browser/index.js +2 -1
- package/dist/browser/v2.d.ts +3 -0
- package/dist/browser/v2.js +2 -0
- package/dist/facade.d.ts +15 -8
- package/dist/facade.js +5 -4
- package/dist/interop/proxyServerPeer.js +1 -1
- package/dist/interop/serverParityPeer.js +8 -1
- package/dist/node/acceptor.d.ts +10 -0
- package/dist/node/acceptor.js +45 -2
- package/dist/node/acceptorV3.d.ts +57 -0
- package/dist/node/acceptorV3.js +227 -0
- package/dist/node/connectSessionV3.d.ts +21 -0
- package/dist/node/connectSessionV3.js +73 -0
- package/dist/node/index.d.ts +15 -10
- package/dist/node/index.js +6 -5
- package/dist/node/nativeTransportAddon.d.ts +39 -1
- package/dist/node/nativeTransportAddon.js +29 -5
- package/dist/node/rawQuicAdapterV3.d.ts +10 -0
- package/dist/node/rawQuicAdapterV3.js +76 -0
- package/dist/node/rawQuicServerV3.d.ts +24 -0
- package/dist/node/rawQuicServerV3.js +34 -0
- package/dist/node/runtimeAuthorizationV3.d.ts +29 -0
- package/dist/node/runtimeAuthorizationV3.js +126 -0
- package/dist/node/tunnelRuntimeV3.d.ts +57 -0
- package/dist/node/tunnelRuntimeV3.js +857 -0
- package/dist/node/v2.d.ts +11 -0
- package/dist/node/v2.js +6 -0
- package/dist/node/webSocketServerV3.d.ts +27 -0
- package/dist/node/webSocketServerV3.js +219 -0
- package/dist/public/streamHandlers.d.ts +3 -0
- package/dist/public/streamHandlers.js +6 -1
- package/dist/v2/index.d.ts +14 -0
- package/dist/v2/index.js +7 -0
- package/dist/v2/protocol.js +23 -32
- package/dist/v2/session.d.ts +5 -0
- package/dist/v2/session.js +90 -35
- package/dist/v3/admissionError.d.ts +4 -0
- package/dist/v3/admissionError.js +8 -0
- package/dist/v3/artifact.d.ts +145 -0
- package/dist/v3/artifact.js +1162 -0
- package/dist/v3/artifactLease.d.ts +17 -0
- package/dist/v3/artifactLease.js +109 -0
- package/dist/v3/browserRuntime.d.ts +37 -0
- package/dist/v3/browserRuntime.js +197 -0
- package/dist/v3/browserSessionRuntime.d.ts +2 -0
- package/dist/v3/browserSessionRuntime.js +21 -0
- package/dist/v3/capability.d.ts +32 -0
- package/dist/v3/capability.js +345 -0
- package/dist/v3/carrier.d.ts +84 -0
- package/dist/v3/carrier.js +411 -0
- package/dist/v3/connectionController.d.ts +87 -0
- package/dist/v3/connectionController.js +805 -0
- package/dist/v3/contract.d.ts +65 -0
- package/dist/v3/contract.js +3 -0
- package/dist/v3/controller.d.ts +43 -0
- package/dist/v3/controller.js +303 -0
- package/dist/v3/handshake.d.ts +81 -0
- package/dist/v3/handshake.js +471 -0
- package/dist/v3/jcs.d.ts +6 -0
- package/dist/v3/jcs.js +69 -0
- package/dist/v3/jsonPreflight.d.ts +1 -0
- package/dist/v3/jsonPreflight.js +251 -0
- package/dist/v3/nodeRuntime.d.ts +40 -0
- package/dist/v3/nodeRuntime.js +315 -0
- package/dist/v3/nodeSessionRuntime.d.ts +2 -0
- package/dist/v3/nodeSessionRuntime.js +13 -0
- package/dist/v3/protocol.d.ts +99 -0
- package/dist/v3/protocol.js +773 -0
- package/dist/v3/publicApi.d.ts +12 -0
- package/dist/v3/publicApi.js +46 -0
- package/dist/v3/publicSession.d.ts +1 -0
- package/dist/v3/publicSession.js +281 -0
- package/dist/v3/retryDisposition.d.ts +16 -0
- package/dist/v3/retryDisposition.js +30 -0
- package/dist/v3/runtimeAdapters.d.ts +9 -0
- package/dist/v3/runtimeAdapters.js +109 -0
- package/dist/v3/security.d.ts +34 -0
- package/dist/v3/security.js +100 -0
- package/dist/v3/serverAdmission.d.ts +38 -0
- package/dist/v3/serverAdmission.js +223 -0
- package/dist/v3/session.d.ts +318 -0
- package/dist/v3/session.js +2289 -0
- package/dist/v3/sessionConnector.d.ts +37 -0
- package/dist/v3/sessionConnector.js +440 -0
- package/dist/v3/streamLifetimeLedger.d.ts +31 -0
- package/dist/v3/streamLifetimeLedger.js +131 -0
- package/dist/v3/streamMetadata.d.ts +1 -0
- package/dist/v3/streamMetadata.js +7 -0
- package/dist/v3/transportConstants.d.ts +56 -0
- package/dist/v3/transportConstants.js +59 -0
- package/dist/v3/unreliableMessage.d.ts +44 -0
- package/dist/v3/unreliableMessage.js +284 -0
- package/dist/v3/webSocketCarrier.d.ts +31 -0
- package/dist/v3/webSocketCarrier.js +370 -0
- package/dist/v3/webTransportCarrier.d.ts +27 -0
- package/dist/v3/webTransportCarrier.js +534 -0
- package/package.json +4 -4
- package/sbom/cyclonedx.json +6 -6
- package/sbom/spdx.json +11 -11
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ArtifactV3 } from "./artifact.js";
|
|
2
|
+
export declare class ArtifactLeaseV3Error extends Error {
|
|
3
|
+
readonly code = "already_consumed";
|
|
4
|
+
constructor();
|
|
5
|
+
}
|
|
6
|
+
export declare class ArtifactLeaseV3 {
|
|
7
|
+
private readonly leaseBrand;
|
|
8
|
+
private constructor();
|
|
9
|
+
}
|
|
10
|
+
export declare class ClaimedArtifactLeaseV3 {
|
|
11
|
+
private readonly claimBrand;
|
|
12
|
+
private constructor();
|
|
13
|
+
}
|
|
14
|
+
export declare function claimArtifactLeaseV3(lease: ArtifactLeaseV3): ClaimedArtifactLeaseV3;
|
|
15
|
+
export declare function claimedArtifactV3(claim: ClaimedArtifactLeaseV3): ArtifactV3;
|
|
16
|
+
export declare function commitArtifactLeaseSpendV3(claim: ClaimedArtifactLeaseV3, signal?: AbortSignal): Promise<void>;
|
|
17
|
+
export declare function retireArtifactLeaseV3(claim: ClaimedArtifactLeaseV3): Promise<void>;
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
export class ArtifactLeaseV3Error extends Error {
|
|
2
|
+
code = "already_consumed";
|
|
3
|
+
constructor() {
|
|
4
|
+
super("Flowersec artifact lease is no longer available");
|
|
5
|
+
this.name = "ArtifactLeaseError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
const leaseStates = new WeakMap();
|
|
9
|
+
const claimStates = new WeakMap();
|
|
10
|
+
export class ArtifactLeaseV3 {
|
|
11
|
+
constructor() { }
|
|
12
|
+
}
|
|
13
|
+
export class ClaimedArtifactLeaseV3 {
|
|
14
|
+
constructor() { }
|
|
15
|
+
}
|
|
16
|
+
/** @internal Artifact sources construct idle one-shot leases through this boundary. */
|
|
17
|
+
export function createArtifactLeaseV3Internal(artifact, commitSpend, retireCleanup) {
|
|
18
|
+
if (typeof commitSpend !== "function" || (retireCleanup !== undefined && typeof retireCleanup !== "function")) {
|
|
19
|
+
throw new TypeError("invalid Flowersec v3 artifact lease callbacks");
|
|
20
|
+
}
|
|
21
|
+
const lease = new ArtifactLeaseV3();
|
|
22
|
+
leaseStates.set(lease, {
|
|
23
|
+
artifact,
|
|
24
|
+
status: "idle",
|
|
25
|
+
commit: commitSpend,
|
|
26
|
+
cleanup: retireCleanup,
|
|
27
|
+
cleanupStarted: false,
|
|
28
|
+
});
|
|
29
|
+
return Object.freeze(lease);
|
|
30
|
+
}
|
|
31
|
+
/** @internal Creates another handle over the same atomic state for race tests and wrappers. */
|
|
32
|
+
export function duplicateArtifactLeaseHandleV3(lease) {
|
|
33
|
+
const state = leaseStates.get(lease);
|
|
34
|
+
if (state === undefined)
|
|
35
|
+
throw new ArtifactLeaseV3Error();
|
|
36
|
+
const copy = new ArtifactLeaseV3();
|
|
37
|
+
leaseStates.set(copy, state);
|
|
38
|
+
return Object.freeze(copy);
|
|
39
|
+
}
|
|
40
|
+
export function claimArtifactLeaseV3(lease) {
|
|
41
|
+
const state = leaseStates.get(lease);
|
|
42
|
+
if (state === undefined || state.status !== "idle")
|
|
43
|
+
throw new ArtifactLeaseV3Error();
|
|
44
|
+
state.status = "claimed";
|
|
45
|
+
const claim = new ClaimedArtifactLeaseV3();
|
|
46
|
+
claimStates.set(claim, state);
|
|
47
|
+
return Object.freeze(claim);
|
|
48
|
+
}
|
|
49
|
+
export function claimedArtifactV3(claim) {
|
|
50
|
+
const state = claimStates.get(claim);
|
|
51
|
+
if (state === undefined || (state.status !== "claimed" && state.status !== "spending")) {
|
|
52
|
+
throw new ArtifactLeaseV3Error();
|
|
53
|
+
}
|
|
54
|
+
return state.artifact;
|
|
55
|
+
}
|
|
56
|
+
export async function commitArtifactLeaseSpendV3(claim, signal) {
|
|
57
|
+
const state = claimStates.get(claim);
|
|
58
|
+
if (state === undefined || state.status !== "claimed")
|
|
59
|
+
throw new ArtifactLeaseV3Error();
|
|
60
|
+
state.status = "spending";
|
|
61
|
+
const callback = Promise.resolve().then(() => state.commit(signal));
|
|
62
|
+
let abort;
|
|
63
|
+
try {
|
|
64
|
+
if (signal === undefined) {
|
|
65
|
+
await callback;
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
const canceled = new Promise((_, reject) => {
|
|
69
|
+
abort = () => reject(signal.reason ?? new Error("artifact spend canceled"));
|
|
70
|
+
if (signal.aborted)
|
|
71
|
+
abort();
|
|
72
|
+
else
|
|
73
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
74
|
+
});
|
|
75
|
+
await Promise.race([callback, canceled]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
if (signal !== undefined && abort !== undefined)
|
|
80
|
+
signal.removeEventListener("abort", abort);
|
|
81
|
+
state.status = "consumed";
|
|
82
|
+
// A cancellation result is authoritative for the lease even when an
|
|
83
|
+
// application callback ignores the signal and settles later.
|
|
84
|
+
void callback.catch(() => undefined);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
export async function retireArtifactLeaseV3(claim) {
|
|
88
|
+
const state = claimStates.get(claim);
|
|
89
|
+
if (state === undefined || state.status !== "claimed")
|
|
90
|
+
throw new ArtifactLeaseV3Error();
|
|
91
|
+
state.status = "retired";
|
|
92
|
+
if (state.cleanup === undefined || state.cleanupStarted)
|
|
93
|
+
return;
|
|
94
|
+
state.cleanupStarted = true;
|
|
95
|
+
try {
|
|
96
|
+
await state.cleanup();
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
// Retirement is final. Cleanup failures are deliberately not allowed to
|
|
100
|
+
// restore or leak ownership of a one-shot credential.
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/** @internal */
|
|
104
|
+
export function artifactLeaseStateV3(lease) {
|
|
105
|
+
const state = lease instanceof ArtifactLeaseV3 ? leaseStates.get(lease) : claimStates.get(lease);
|
|
106
|
+
if (state === undefined)
|
|
107
|
+
throw new ArtifactLeaseV3Error();
|
|
108
|
+
return state.status;
|
|
109
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type RuntimeCapabilityDescriptorV3 } from "./capability.js";
|
|
2
|
+
import type { CanonicalArtifactCandidateV3 } from "./artifact.js";
|
|
3
|
+
import type { NativeCarrierSessionV3 } from "./carrier.js";
|
|
4
|
+
type BrowserFeaturesV3 = Readonly<{
|
|
5
|
+
WebSocket?: unknown;
|
|
6
|
+
WebTransport?: unknown;
|
|
7
|
+
navigator?: Readonly<{
|
|
8
|
+
userAgentData?: Readonly<{
|
|
9
|
+
getHighEntropyValues(hints: readonly string[]): Promise<unknown>;
|
|
10
|
+
}>;
|
|
11
|
+
}>;
|
|
12
|
+
}>;
|
|
13
|
+
type WebTransportLikeV3 = Readonly<{
|
|
14
|
+
ready: Promise<void>;
|
|
15
|
+
close(options?: Readonly<{
|
|
16
|
+
closeCode?: number;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}>): void;
|
|
19
|
+
}>;
|
|
20
|
+
type WebTransportConstructorV3 = new (url: string, options?: Readonly<{
|
|
21
|
+
serverCertificateHashes: readonly Readonly<{
|
|
22
|
+
algorithm: "sha-256";
|
|
23
|
+
value: ArrayBuffer;
|
|
24
|
+
}>[];
|
|
25
|
+
}>) => WebTransportLikeV3;
|
|
26
|
+
export declare class BrowserRuntimeCapabilityRegistryV3 {
|
|
27
|
+
#private;
|
|
28
|
+
private constructor();
|
|
29
|
+
static create(features?: BrowserFeaturesV3): Promise<BrowserRuntimeCapabilityRegistryV3>;
|
|
30
|
+
snapshot(): RuntimeCapabilityDescriptorV3;
|
|
31
|
+
pinEnabled(): boolean;
|
|
32
|
+
invalidatePinSupport(): boolean;
|
|
33
|
+
webTransportConstructor(): WebTransportConstructorV3 | undefined;
|
|
34
|
+
}
|
|
35
|
+
export declare function createBrowserWebTransportV3(candidate: CanonicalArtifactCandidateV3, attemptNowUnixSeconds: number, capabilitySnapshot: RuntimeCapabilityDescriptorV3, registry: BrowserRuntimeCapabilityRegistryV3, signal?: AbortSignal): Promise<WebTransportLikeV3>;
|
|
36
|
+
export declare function createBrowserWebTransportCarrierV3(candidate: CanonicalArtifactCandidateV3, attemptNowUnixSeconds: number, capabilitySnapshot: RuntimeCapabilityDescriptorV3, registry: BrowserRuntimeCapabilityRegistryV3, inboundBidirectionalStreamCapacity: number, signal?: AbortSignal): Promise<NativeCarrierSessionV3>;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { defineRuntimeCapabilityDescriptorV3 } from "./capability.js";
|
|
2
|
+
import { snapshotTransportSecurityPolicyV3, TransportFailureV3 } from "./security.js";
|
|
3
|
+
import { adaptWebTransportCarrierSessionV3, hasWebTransportConstructorSurfaceV3, } from "./webTransportCarrier.js";
|
|
4
|
+
import { FLOWERSEC_V3_PATHS, FLOWERSEC_V3_WIRE_PROFILES } from "./transportConstants.js";
|
|
5
|
+
const PIN_BROWSER_VERSION = "151.0.7922.34";
|
|
6
|
+
const webSocketTuples = tuplesFor("websocket", false, ["ca"]);
|
|
7
|
+
const webTransportCATuples = tuplesFor("webtransport", true, ["ca"]);
|
|
8
|
+
const webTransportPinTuples = tuplesFor("webtransport", true, ["ca", "pin"]);
|
|
9
|
+
export class BrowserRuntimeCapabilityRegistryV3 {
|
|
10
|
+
#features;
|
|
11
|
+
#hasWebSocket;
|
|
12
|
+
#hasWebTransport;
|
|
13
|
+
#pinState;
|
|
14
|
+
constructor(features, pinEnabled) {
|
|
15
|
+
this.#features = features;
|
|
16
|
+
this.#hasWebSocket = typeof features.WebSocket === "function";
|
|
17
|
+
this.#hasWebTransport = hasWebTransportConstructorSurfaceV3(features.WebTransport);
|
|
18
|
+
this.#pinState = pinEnabled ? "enabled" : "ca_only";
|
|
19
|
+
}
|
|
20
|
+
static async create(features = globalThis) {
|
|
21
|
+
return new BrowserRuntimeCapabilityRegistryV3(features, await exactChromiumPinProvider(features));
|
|
22
|
+
}
|
|
23
|
+
snapshot() {
|
|
24
|
+
const tuples = [];
|
|
25
|
+
const unsupported = [
|
|
26
|
+
{ carrier: "raw_quic", reason: "browser_no_raw_udp" },
|
|
27
|
+
];
|
|
28
|
+
if (this.#hasWebSocket)
|
|
29
|
+
tuples.push(...webSocketTuples);
|
|
30
|
+
else
|
|
31
|
+
unsupported.push({ carrier: "websocket", reason: "browser_websocket_api_unavailable" });
|
|
32
|
+
if (this.#hasWebTransport) {
|
|
33
|
+
tuples.push(...(this.#pinState === "enabled" ? webTransportPinTuples : webTransportCATuples));
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
unsupported.push({ carrier: "webtransport", reason: "browser_webtransport_api_unavailable" });
|
|
37
|
+
}
|
|
38
|
+
unsupported.sort((left, right) => left.carrier < right.carrier ? -1 : left.carrier > right.carrier ? 1 : 0);
|
|
39
|
+
return defineRuntimeCapabilityDescriptorV3("browser", tuples, unsupported);
|
|
40
|
+
}
|
|
41
|
+
pinEnabled() {
|
|
42
|
+
return this.#pinState === "enabled";
|
|
43
|
+
}
|
|
44
|
+
invalidatePinSupport() {
|
|
45
|
+
if (this.#pinState === "ca_only")
|
|
46
|
+
return false;
|
|
47
|
+
this.#pinState = "ca_only";
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
webTransportConstructor() {
|
|
51
|
+
return hasWebTransportConstructorSurfaceV3(this.#features.WebTransport)
|
|
52
|
+
? this.#features.WebTransport
|
|
53
|
+
: undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export async function createBrowserWebTransportV3(candidate, attemptNowUnixSeconds, capabilitySnapshot, registry, signal) {
|
|
57
|
+
if (candidate.carrier !== "webtransport")
|
|
58
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
59
|
+
const path = pathFromProfile(candidate.wire_profile);
|
|
60
|
+
validateNormalizedBrowserURL(candidate.normalized_url, path);
|
|
61
|
+
const tuple = capabilitySnapshot.tuples.find((entry) => entry.carrier === "webtransport" && entry.networkMode === "dial" && entry.path === path);
|
|
62
|
+
const modes = tuple?.securityModes ?? [];
|
|
63
|
+
const policy = snapshotTransportSecurityPolicyV3(candidate.tls, attemptNowUnixSeconds, modes);
|
|
64
|
+
if (policy.mode === "pin" && !registry.pinEnabled())
|
|
65
|
+
throw new TransportFailureV3("tls_unsupported");
|
|
66
|
+
const Constructor = registry.webTransportConstructor();
|
|
67
|
+
if (Constructor === undefined)
|
|
68
|
+
throw new TransportFailureV3("tls_unsupported");
|
|
69
|
+
if (signal?.aborted === true)
|
|
70
|
+
throw signal.reason;
|
|
71
|
+
let transport;
|
|
72
|
+
try {
|
|
73
|
+
transport = policy.mode === "ca"
|
|
74
|
+
? new Constructor(candidate.normalized_url)
|
|
75
|
+
: new Constructor(candidate.normalized_url, {
|
|
76
|
+
serverCertificateHashes: policy.activeLeafDerSHA256.map((value) => ({
|
|
77
|
+
algorithm: "sha-256",
|
|
78
|
+
value: exactArrayBuffer(value),
|
|
79
|
+
})),
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
if (policy.mode === "pin" && isNotSupportedError(error)) {
|
|
84
|
+
registry.invalidatePinSupport();
|
|
85
|
+
throw new TransportFailureV3("tls_unsupported", undefined, error);
|
|
86
|
+
}
|
|
87
|
+
throw new TransportFailureV3("connection_failed", undefined, error);
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
await raceAbort(transport.ready, signal);
|
|
91
|
+
return transport;
|
|
92
|
+
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
try {
|
|
95
|
+
transport.close({ closeCode: 6, reason: "WebTransport start failed" });
|
|
96
|
+
}
|
|
97
|
+
catch { /* best effort */ }
|
|
98
|
+
if (error instanceof BrowserRuntimeAbortV3)
|
|
99
|
+
throw error.reason;
|
|
100
|
+
throw new TransportFailureV3("connection_failed", policy.mode === "pin" ? "browser_pin_opaque" : undefined, error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export async function createBrowserWebTransportCarrierV3(candidate, attemptNowUnixSeconds, capabilitySnapshot, registry, inboundBidirectionalStreamCapacity, signal) {
|
|
104
|
+
const path = pathFromProfile(candidate.wire_profile);
|
|
105
|
+
const transport = await createBrowserWebTransportV3(candidate, attemptNowUnixSeconds, capabilitySnapshot, registry, signal);
|
|
106
|
+
try {
|
|
107
|
+
return adaptWebTransportCarrierSessionV3(transport, { path, inboundBidirectionalStreamCapacity });
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
try {
|
|
111
|
+
transport.close({ closeCode: 6, reason: "WebTransport adapter failed" });
|
|
112
|
+
}
|
|
113
|
+
catch { /* best effort */ }
|
|
114
|
+
throw error;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function exactChromiumPinProvider(features) {
|
|
118
|
+
if (!hasWebTransportConstructorSurfaceV3(features.WebTransport))
|
|
119
|
+
return false;
|
|
120
|
+
const provider = features.navigator?.userAgentData;
|
|
121
|
+
if (provider === undefined || typeof provider.getHighEntropyValues !== "function")
|
|
122
|
+
return false;
|
|
123
|
+
let value;
|
|
124
|
+
try {
|
|
125
|
+
value = await provider.getHighEntropyValues(["fullVersionList"]);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
if (typeof value !== "object" || value === null)
|
|
131
|
+
return false;
|
|
132
|
+
const list = value.fullVersionList;
|
|
133
|
+
if (!Array.isArray(list))
|
|
134
|
+
return false;
|
|
135
|
+
const chromium = list.filter((entry) => typeof entry === "object" && entry !== null &&
|
|
136
|
+
entry.brand === "Chromium");
|
|
137
|
+
if (chromium.length !== 1)
|
|
138
|
+
return false;
|
|
139
|
+
const version = chromium[0].version;
|
|
140
|
+
return typeof version === "string" && /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/.test(version) &&
|
|
141
|
+
version === PIN_BROWSER_VERSION;
|
|
142
|
+
}
|
|
143
|
+
function tuplesFor(carrier, datagrams, securityModes) {
|
|
144
|
+
return Object.freeze([
|
|
145
|
+
{ carrier, datagrams, migration: false, networkMode: "dial", path: "direct", reliableStreams: true, securityModes, sessionRole: "client" },
|
|
146
|
+
{ carrier, datagrams, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, securityModes, sessionRole: "client" },
|
|
147
|
+
{ carrier, datagrams, migration: false, networkMode: "dial", path: "tunnel", reliableStreams: true, securityModes, sessionRole: "server" },
|
|
148
|
+
]);
|
|
149
|
+
}
|
|
150
|
+
function validateNormalizedBrowserURL(raw, path) {
|
|
151
|
+
let parsed;
|
|
152
|
+
try {
|
|
153
|
+
parsed = new URL(raw);
|
|
154
|
+
}
|
|
155
|
+
catch {
|
|
156
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
157
|
+
}
|
|
158
|
+
const expectedPath = FLOWERSEC_V3_PATHS.webtransport[path];
|
|
159
|
+
if (parsed.href !== raw || parsed.protocol !== "https:" || parsed.username !== "" || parsed.password !== "" ||
|
|
160
|
+
parsed.pathname !== expectedPath || parsed.search !== "" || parsed.hash !== "") {
|
|
161
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function pathFromProfile(profile) {
|
|
165
|
+
if (profile === FLOWERSEC_V3_WIRE_PROFILES.direct)
|
|
166
|
+
return "direct";
|
|
167
|
+
if (profile === FLOWERSEC_V3_WIRE_PROFILES.tunnel)
|
|
168
|
+
return "tunnel";
|
|
169
|
+
throw new TransportFailureV3("invalid_artifact");
|
|
170
|
+
}
|
|
171
|
+
function exactArrayBuffer(value) {
|
|
172
|
+
return value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
|
|
173
|
+
}
|
|
174
|
+
function isNotSupportedError(error) {
|
|
175
|
+
return error instanceof DOMException ? error.name === "NotSupportedError" :
|
|
176
|
+
typeof error === "object" && error !== null && error.name === "NotSupportedError";
|
|
177
|
+
}
|
|
178
|
+
class BrowserRuntimeAbortV3 {
|
|
179
|
+
reason;
|
|
180
|
+
constructor(reason) {
|
|
181
|
+
this.reason = reason;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function raceAbort(promise, signal) {
|
|
185
|
+
if (signal === undefined)
|
|
186
|
+
return await promise;
|
|
187
|
+
if (signal.aborted)
|
|
188
|
+
throw new BrowserRuntimeAbortV3(signal.reason);
|
|
189
|
+
return await new Promise((resolve, reject) => {
|
|
190
|
+
const abort = () => {
|
|
191
|
+
signal.removeEventListener("abort", abort);
|
|
192
|
+
reject(new BrowserRuntimeAbortV3(signal.reason));
|
|
193
|
+
};
|
|
194
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
195
|
+
void promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
196
|
+
});
|
|
197
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RuntimeError } from "../runtime/errors.js";
|
|
2
|
+
export const browserSessionRuntimeV3 = Object.freeze({
|
|
3
|
+
entropy(length) {
|
|
4
|
+
requireLength(length);
|
|
5
|
+
const runtime = globalThis.crypto;
|
|
6
|
+
if (runtime === undefined || typeof runtime.getRandomValues !== "function") {
|
|
7
|
+
throw new RuntimeError("runtime_unsupported", "browser cryptographic entropy is unavailable");
|
|
8
|
+
}
|
|
9
|
+
const output = new Uint8Array(length);
|
|
10
|
+
runtime.getRandomValues(output);
|
|
11
|
+
return output;
|
|
12
|
+
},
|
|
13
|
+
monotonicMilliseconds() {
|
|
14
|
+
return performance.now();
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
function requireLength(length) {
|
|
18
|
+
if (!Number.isSafeInteger(length) || length < 1 || length > 65_536) {
|
|
19
|
+
throw new RangeError("entropy length must be an integer from 1 to 65536");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { CarrierKind, PathKind } from "../public/contract.js";
|
|
2
|
+
export type TransportSecurityModeV3 = "ca" | "pin";
|
|
3
|
+
export type NetworkModeV3 = "dial" | "listen";
|
|
4
|
+
export type SessionRoleV3 = "client" | "server";
|
|
5
|
+
export type RuntimeCapabilityTupleV3 = Readonly<{
|
|
6
|
+
carrier: CarrierKind;
|
|
7
|
+
datagrams: boolean;
|
|
8
|
+
migration: boolean;
|
|
9
|
+
networkMode: NetworkModeV3;
|
|
10
|
+
path: PathKind;
|
|
11
|
+
reliableStreams: true;
|
|
12
|
+
securityModes: readonly TransportSecurityModeV3[];
|
|
13
|
+
sessionRole: SessionRoleV3;
|
|
14
|
+
}>;
|
|
15
|
+
export type UnsupportedRuntimeCarrierV3 = Readonly<{
|
|
16
|
+
carrier: CarrierKind;
|
|
17
|
+
reason: string;
|
|
18
|
+
}>;
|
|
19
|
+
export type RuntimeCapabilityDescriptorV3 = Readonly<{
|
|
20
|
+
language: string;
|
|
21
|
+
runtime: string;
|
|
22
|
+
schemaVersion: 3;
|
|
23
|
+
tuples: readonly RuntimeCapabilityTupleV3[];
|
|
24
|
+
unsupported: readonly UnsupportedRuntimeCarrierV3[];
|
|
25
|
+
}>;
|
|
26
|
+
export declare function defineRuntimeCapabilityDescriptorV3(runtime: "browser" | "node", tuples: readonly RuntimeCapabilityTupleV3[], unsupported: readonly UnsupportedRuntimeCarrierV3[]): RuntimeCapabilityDescriptorV3;
|
|
27
|
+
export declare function encodeRuntimeCapabilityDescriptorV3(descriptor: RuntimeCapabilityDescriptorV3): string;
|
|
28
|
+
export declare function decodeRuntimeCapabilityDescriptorV3(raw: string | Uint8Array): RuntimeCapabilityDescriptorV3;
|
|
29
|
+
export declare function runtimeCapabilityDigestV3(descriptor: RuntimeCapabilityDescriptorV3): Uint8Array;
|
|
30
|
+
export declare function runtimeCapabilityDigestHexV3(descriptor: RuntimeCapabilityDescriptorV3): string;
|
|
31
|
+
export declare function supportsCandidateSecurityV3(descriptor: RuntimeCapabilityDescriptorV3, carrier: CarrierKind, path: PathKind, sessionRole: SessionRoleV3, mode: TransportSecurityModeV3): boolean;
|
|
32
|
+
export declare function validateRuntimeCapabilityDescriptorV3(descriptor: RuntimeCapabilityDescriptorV3): void;
|