@floegence/flowersec-core 2.3.10 → 2.4.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 +1 -1
- package/dist/public/contract.d.ts +3 -3
- package/dist/v2/publicSession.js +84 -4
- package/package.json +3 -3
- package/sbom/cyclonedx.json +6 -6
- package/sbom/spdx.json +11 -11
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ The root type exports are:
|
|
|
29
29
|
|
|
30
30
|
Retry ownership belongs to `ConnectionController`; applications do not classify error text or run a parallel retry scheduler. Public failures remain redacted and reveal no carrier, candidate, URL, credential, stage, key, or diagnostic details.
|
|
31
31
|
|
|
32
|
-
`RpcResult<Response>` is a discriminated union. `RpcPeer.call(...)` requires a decoder for successful payloads, so the typed success value has passed application validation before it is returned. Check `result.ok` before reading either the typed success `payload` or bounded application `error`; a result cannot contain both. RPC call and notify
|
|
32
|
+
`RpcResult<Response>` is a discriminated union. `RpcPeer.call(...)` requires a decoder for successful payloads, so the typed success value has passed application validation before it is returned. Check `result.ok` before reading either the typed success `payload` or bounded application `error`; a result cannot contain both. RPC call and notify accept only `JsonValue` payloads and reject values that cannot be represented on the wire before sending. TypeScript `RpcPeer.onNotify(typeId, decoder, handler)` receives peer outbound notifications through the local Session's inbound reserved RPC stream. A notification reaches the handler only after its decoder succeeds; decoder and handler failures are isolated from RPC serving.
|
|
33
33
|
|
|
34
34
|
When connector options omit a connection timeout, browser and Node.js connectors use the shared ten-second default.
|
|
35
35
|
|
|
@@ -42,9 +42,9 @@ export type RpcResult<Response = unknown> = Readonly<{
|
|
|
42
42
|
}>;
|
|
43
43
|
}>;
|
|
44
44
|
export interface RpcPeer {
|
|
45
|
-
call<Request =
|
|
46
|
-
notify<Payload =
|
|
47
|
-
onNotify<Payload
|
|
45
|
+
call<Request extends JsonValue = JsonValue, Response = unknown>(typeId: number, payload: Request, decodeResponse: (payload: JsonValue) => Response, options?: OperationOptions): Promise<RpcResult<Response>>;
|
|
46
|
+
notify<Payload extends JsonValue = JsonValue>(typeId: number, payload: Payload, options?: OperationOptions): Promise<void>;
|
|
47
|
+
onNotify<Payload>(typeId: number, decodePayload: (payload: JsonValue) => Payload, handler: (payload: Payload) => void | Promise<void>): () => void;
|
|
48
48
|
}
|
|
49
49
|
export interface ByteStream {
|
|
50
50
|
readonly kind: string;
|
package/dist/v2/publicSession.js
CHANGED
|
@@ -2,8 +2,18 @@ import { SessionError } from "./contract.js";
|
|
|
2
2
|
import { createStreamMetadataV2, streamMetadataValuesV2 } from "./streamMetadata.js";
|
|
3
3
|
/** @internal */
|
|
4
4
|
export function projectSessionV2(session) {
|
|
5
|
-
const
|
|
5
|
+
const notificationOwner = {
|
|
6
|
+
subscriptions: new Set(),
|
|
7
|
+
closed: false,
|
|
8
|
+
};
|
|
9
|
+
const clearNotificationSubscriptions = () => {
|
|
10
|
+
notificationOwner.closed = true;
|
|
11
|
+
for (const unsubscribe of [...notificationOwner.subscriptions])
|
|
12
|
+
unsubscribe();
|
|
13
|
+
};
|
|
14
|
+
const rpc = projectRpcPeerV2(session.rpc, notificationOwner);
|
|
6
15
|
const unreliable = session.unreliableMessages;
|
|
16
|
+
void session.termination.then(clearNotificationSubscriptions, clearNotificationSubscriptions);
|
|
7
17
|
return Object.freeze({
|
|
8
18
|
rpc,
|
|
9
19
|
...(unreliable === undefined ? {} : { unreliableMessages: unreliable }),
|
|
@@ -64,6 +74,9 @@ export function projectSessionV2(session) {
|
|
|
64
74
|
catch (error) {
|
|
65
75
|
throw redactSessionError(error);
|
|
66
76
|
}
|
|
77
|
+
finally {
|
|
78
|
+
clearNotificationSubscriptions();
|
|
79
|
+
}
|
|
67
80
|
},
|
|
68
81
|
});
|
|
69
82
|
}
|
|
@@ -115,14 +128,16 @@ function projectByteStreamV2(stream) {
|
|
|
115
128
|
},
|
|
116
129
|
});
|
|
117
130
|
}
|
|
118
|
-
function projectRpcPeerV2(peer) {
|
|
131
|
+
function projectRpcPeerV2(peer, notificationOwner) {
|
|
119
132
|
return Object.freeze({
|
|
120
133
|
async call(typeId, payload, decodeResponse, options) {
|
|
121
134
|
try {
|
|
135
|
+
assertJsonValue(payload);
|
|
122
136
|
const result = await peer.call(typeId, payload, options?.signal);
|
|
123
137
|
if (result.error !== undefined) {
|
|
124
138
|
return Object.freeze({ ok: false, error: Object.freeze({ ...result.error }) });
|
|
125
139
|
}
|
|
140
|
+
assertJsonValue(result.payload);
|
|
126
141
|
return Object.freeze({ ok: true, payload: decodeResponse(result.payload) });
|
|
127
142
|
}
|
|
128
143
|
catch (error) {
|
|
@@ -131,6 +146,7 @@ function projectRpcPeerV2(peer) {
|
|
|
131
146
|
},
|
|
132
147
|
async notify(typeId, payload, options) {
|
|
133
148
|
try {
|
|
149
|
+
assertJsonValue(payload);
|
|
134
150
|
if (options?.signal?.aborted)
|
|
135
151
|
throw options.signal.reason ?? new DOMException("The operation was aborted", "AbortError");
|
|
136
152
|
await raceWithSignal(peer.notify(typeId, payload), options?.signal);
|
|
@@ -139,11 +155,75 @@ function projectRpcPeerV2(peer) {
|
|
|
139
155
|
throw redactSessionError(error);
|
|
140
156
|
}
|
|
141
157
|
},
|
|
142
|
-
onNotify(typeId, handler) {
|
|
143
|
-
|
|
158
|
+
onNotify(typeId, decodePayload, handler) {
|
|
159
|
+
if (notificationOwner.closed)
|
|
160
|
+
return () => undefined;
|
|
161
|
+
const unsubscribe = peer.onNotify(typeId, (payload) => {
|
|
162
|
+
let decoded;
|
|
163
|
+
try {
|
|
164
|
+
assertJsonValue(payload);
|
|
165
|
+
decoded = decodePayload(payload);
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
try {
|
|
171
|
+
void Promise.resolve(handler(decoded)).catch(() => undefined);
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
// Notification handlers are isolated from RPC serving.
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
let subscribed = true;
|
|
178
|
+
const cancel = () => {
|
|
179
|
+
if (!subscribed)
|
|
180
|
+
return;
|
|
181
|
+
subscribed = false;
|
|
182
|
+
notificationOwner.subscriptions.delete(cancel);
|
|
183
|
+
unsubscribe();
|
|
184
|
+
};
|
|
185
|
+
notificationOwner.subscriptions.add(cancel);
|
|
186
|
+
return cancel;
|
|
144
187
|
},
|
|
145
188
|
});
|
|
146
189
|
}
|
|
190
|
+
function assertJsonValue(value) {
|
|
191
|
+
validateJsonValue(value, new Set());
|
|
192
|
+
}
|
|
193
|
+
function validateJsonValue(value, ancestors) {
|
|
194
|
+
if (value === null || typeof value === "string" || typeof value === "boolean")
|
|
195
|
+
return;
|
|
196
|
+
if (typeof value === "number") {
|
|
197
|
+
if (Number.isFinite(value))
|
|
198
|
+
return;
|
|
199
|
+
throw new TypeError("RPC payload contains a non-finite number");
|
|
200
|
+
}
|
|
201
|
+
if (typeof value !== "object")
|
|
202
|
+
throw new TypeError("RPC payload is not a JSON value");
|
|
203
|
+
if (ancestors.has(value))
|
|
204
|
+
throw new TypeError("RPC payload contains a cycle");
|
|
205
|
+
ancestors.add(value);
|
|
206
|
+
try {
|
|
207
|
+
if (Array.isArray(value)) {
|
|
208
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
209
|
+
if (!(index in value))
|
|
210
|
+
throw new TypeError("RPC payload contains a sparse array");
|
|
211
|
+
validateJsonValue(value[index], ancestors);
|
|
212
|
+
}
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const prototype = Object.getPrototypeOf(value);
|
|
216
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
217
|
+
throw new TypeError("RPC payload contains a non-JSON object");
|
|
218
|
+
}
|
|
219
|
+
for (const key of Object.keys(value)) {
|
|
220
|
+
validateJsonValue(value[key], ancestors);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
ancestors.delete(value);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
147
227
|
async function raceWithSignal(operation, signal) {
|
|
148
228
|
if (signal === undefined)
|
|
149
229
|
return await operation;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floegence/flowersec-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.4.0",
|
|
4
4
|
"description": "Flowersec core TypeScript library for carrier-neutral encrypted sessions and multiplexed streams.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"ws": "^8.21.2"
|
|
78
78
|
},
|
|
79
79
|
"optionalDependencies": {
|
|
80
|
-
"@floegence/flowersec-node-native": "2.
|
|
80
|
+
"@floegence/flowersec-node-native": "2.4.0"
|
|
81
81
|
},
|
|
82
82
|
"devDependencies": {
|
|
83
83
|
"@playwright/test": "1.62.1",
|
|
@@ -95,5 +95,5 @@
|
|
|
95
95
|
"vite": "^8.2.1",
|
|
96
96
|
"vitest": "4.1.10"
|
|
97
97
|
},
|
|
98
|
-
"flowersecSourceCommit": "
|
|
98
|
+
"flowersecSourceCommit": "0849589d3516564cc2f32ce51f15a1bba4aa3aa6"
|
|
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:c502284d-2e30-5f97-8204-2cdd20d4483a",
|
|
5
5
|
"version": 1,
|
|
6
6
|
"metadata": {
|
|
7
7
|
"component": {
|
|
8
8
|
"type": "library",
|
|
9
9
|
"name": "@floegence/flowersec-core",
|
|
10
|
-
"version": "2.
|
|
11
|
-
"purl": "pkg:npm/%40floegence/flowersec-core@2.
|
|
12
|
-
"bom-ref": "pkg:npm/%40floegence/flowersec-core@2.
|
|
10
|
+
"version": "2.4.0",
|
|
11
|
+
"purl": "pkg:npm/%40floegence/flowersec-core@2.4.0",
|
|
12
|
+
"bom-ref": "pkg:npm/%40floegence/flowersec-core@2.4.0"
|
|
13
13
|
},
|
|
14
14
|
"properties": [
|
|
15
15
|
{
|
|
16
16
|
"name": "flowersec:source-inventory-sha256",
|
|
17
|
-
"value": "
|
|
17
|
+
"value": "1c377fd1fb5ea77eadd418a3990b5ae9585edf1a740a95901287df743cc2ec9c"
|
|
18
18
|
}
|
|
19
19
|
]
|
|
20
20
|
},
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
],
|
|
191
191
|
"dependencies": [
|
|
192
192
|
{
|
|
193
|
-
"ref": "pkg:npm/%40floegence/flowersec-core@2.
|
|
193
|
+
"ref": "pkg:npm/%40floegence/flowersec-core@2.4.0",
|
|
194
194
|
"dependsOn": [
|
|
195
195
|
"pkg:npm/%40noble/ciphers@2.3.0",
|
|
196
196
|
"pkg:npm/%40noble/curves@2.3.0",
|
package/sbom/spdx.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"dataLicense": "CC0-1.0",
|
|
4
4
|
"SPDXID": "SPDXRef-DOCUMENT",
|
|
5
5
|
"name": "flowersec-ts",
|
|
6
|
-
"documentNamespace": "https://github.com/floegence/flowersec/sbom/flowersec-ts/
|
|
6
|
+
"documentNamespace": "https://github.com/floegence/flowersec/sbom/flowersec-ts/1c377fd1fb5ea77eadd418a3990b5ae9585edf1a740a95901287df743cc2ec9c",
|
|
7
7
|
"creationInfo": {
|
|
8
8
|
"created": "1970-01-01T00:00:00Z",
|
|
9
9
|
"creators": [
|
|
@@ -13,19 +13,19 @@
|
|
|
13
13
|
"packages": [
|
|
14
14
|
{
|
|
15
15
|
"name": "@floegence/flowersec-core",
|
|
16
|
-
"SPDXID": "SPDXRef-Package-
|
|
17
|
-
"versionInfo": "2.
|
|
16
|
+
"SPDXID": "SPDXRef-Package-d5c0c7bb3c27968de167",
|
|
17
|
+
"versionInfo": "2.4.0",
|
|
18
18
|
"downloadLocation": "NOASSERTION",
|
|
19
19
|
"filesAnalyzed": false,
|
|
20
20
|
"licenseConcluded": "NOASSERTION",
|
|
21
21
|
"licenseDeclared": "NOASSERTION",
|
|
22
22
|
"copyrightText": "NOASSERTION",
|
|
23
|
-
"comment": "Flowersec source inventory SHA-256:
|
|
23
|
+
"comment": "Flowersec source inventory SHA-256: 1c377fd1fb5ea77eadd418a3990b5ae9585edf1a740a95901287df743cc2ec9c",
|
|
24
24
|
"externalRefs": [
|
|
25
25
|
{
|
|
26
26
|
"referenceCategory": "PACKAGE-MANAGER",
|
|
27
27
|
"referenceType": "purl",
|
|
28
|
-
"referenceLocator": "pkg:npm/%40floegence/flowersec-core@2.
|
|
28
|
+
"referenceLocator": "pkg:npm/%40floegence/flowersec-core@2.4.0"
|
|
29
29
|
}
|
|
30
30
|
]
|
|
31
31
|
},
|
|
@@ -136,30 +136,30 @@
|
|
|
136
136
|
{
|
|
137
137
|
"spdxElementId": "SPDXRef-DOCUMENT",
|
|
138
138
|
"relationshipType": "DESCRIBES",
|
|
139
|
-
"relatedSpdxElement": "SPDXRef-Package-
|
|
139
|
+
"relatedSpdxElement": "SPDXRef-Package-d5c0c7bb3c27968de167"
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
|
-
"spdxElementId": "SPDXRef-Package-
|
|
142
|
+
"spdxElementId": "SPDXRef-Package-d5c0c7bb3c27968de167",
|
|
143
143
|
"relationshipType": "DEPENDS_ON",
|
|
144
144
|
"relatedSpdxElement": "SPDXRef-Package-5ce913a03239b02770fb"
|
|
145
145
|
},
|
|
146
146
|
{
|
|
147
|
-
"spdxElementId": "SPDXRef-Package-
|
|
147
|
+
"spdxElementId": "SPDXRef-Package-d5c0c7bb3c27968de167",
|
|
148
148
|
"relationshipType": "DEPENDS_ON",
|
|
149
149
|
"relatedSpdxElement": "SPDXRef-Package-01009adf60db02c13634"
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
|
-
"spdxElementId": "SPDXRef-Package-
|
|
152
|
+
"spdxElementId": "SPDXRef-Package-d5c0c7bb3c27968de167",
|
|
153
153
|
"relationshipType": "DEPENDS_ON",
|
|
154
154
|
"relatedSpdxElement": "SPDXRef-Package-8815b117c8a1d5f7eeb0"
|
|
155
155
|
},
|
|
156
156
|
{
|
|
157
|
-
"spdxElementId": "SPDXRef-Package-
|
|
157
|
+
"spdxElementId": "SPDXRef-Package-d5c0c7bb3c27968de167",
|
|
158
158
|
"relationshipType": "DEPENDS_ON",
|
|
159
159
|
"relatedSpdxElement": "SPDXRef-Package-f8b45a289df643042b94"
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
|
-
"spdxElementId": "SPDXRef-Package-
|
|
162
|
+
"spdxElementId": "SPDXRef-Package-d5c0c7bb3c27968de167",
|
|
163
163
|
"relationshipType": "DEPENDS_ON",
|
|
164
164
|
"relatedSpdxElement": "SPDXRef-Package-b779412f685822663496"
|
|
165
165
|
},
|