@matter-server/ble-proxy 0.7.2-alpha.0-20260601-e778038 → 0.8.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/dist/esm/BleProxyConnection.d.ts +34 -0
- package/dist/esm/BleProxyConnection.d.ts.map +1 -0
- package/dist/esm/BleProxyConnection.js +210 -0
- package/dist/esm/BleProxyConnection.js.map +6 -0
- package/dist/esm/BleProxyHandler.d.ts +15 -20
- package/dist/esm/BleProxyHandler.d.ts.map +1 -1
- package/dist/esm/BleProxyHandler.js +114 -183
- package/dist/esm/BleProxyHandler.js.map +1 -1
- package/dist/esm/ProxyBleChannel.d.ts.map +1 -1
- package/dist/esm/ProxyBleChannel.js +70 -40
- package/dist/esm/ProxyBleChannel.js.map +1 -1
- package/dist/esm/ProxyBleClient.d.ts.map +1 -1
- package/dist/esm/ProxyBleClient.js +16 -24
- package/dist/esm/ProxyBleClient.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +21 -20
- package/src/BleProxyConnection.ts +282 -0
- package/src/BleProxyHandler.ts +121 -246
- package/src/ProxyBleChannel.ts +92 -56
- package/src/ProxyBleClient.ts +22 -26
- package/src/index.ts +1 -0
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { Observable } from "@matter/main";
|
|
7
|
+
import { WebSocket } from "ws";
|
|
8
|
+
import { type BinaryFrame, type BleProxyCommandMap, type BleProxyCommandName, type BleProxyEventName } from "./BleProxyProtocol.js";
|
|
9
|
+
/**
|
|
10
|
+
* One BLE proxy client socket. Owns that client's handshake state, pending
|
|
11
|
+
* command map, and command/event/binary-frame plumbing. The hub
|
|
12
|
+
* (`BleProxyHandler`) creates one per accepted WebSocket connection.
|
|
13
|
+
*/
|
|
14
|
+
export declare class BleProxyConnection {
|
|
15
|
+
#private;
|
|
16
|
+
readonly binaryFrameReceived: Observable<[frame: BinaryFrame], void>;
|
|
17
|
+
readonly eventReceived: Observable<[event: BleProxyEventName, data: Record<string, unknown>], void>;
|
|
18
|
+
/** Emitted once when this connection completes the protocol handshake. */
|
|
19
|
+
readonly handshakeCompleted: Observable<[], void>;
|
|
20
|
+
/** Emitted once when this connection is torn down (close, error, or handshake failure). */
|
|
21
|
+
readonly closed: Observable<[], void>;
|
|
22
|
+
constructor(ws: WebSocket);
|
|
23
|
+
get id(): string;
|
|
24
|
+
get connected(): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Send a typed command to this client and wait for the response.
|
|
27
|
+
* Rejects if the connection is not ready or the client disconnects first.
|
|
28
|
+
*/
|
|
29
|
+
sendCommand<C extends BleProxyCommandName>(command: C, ...rest: BleProxyCommandMap[C]["args"] extends undefined ? [] : [args: BleProxyCommandMap[C]["args"]]): Promise<BleProxyCommandMap[C]["result"]>;
|
|
30
|
+
sendBinaryFrame(opcode: number, connectionHandle: number, payload: Uint8Array): void;
|
|
31
|
+
/** Close the socket if open, reject all pending commands, emit `closed` once. */
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=BleProxyConnection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BleProxyConnection.d.ts","sourceRoot":"","sources":["../../src/BleProxyConnection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAyB,UAAU,EAA8B,MAAM,cAAc,CAAC;AAE7F,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EAIH,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EAKzB,MAAM,uBAAuB,CAAC;AAiC/B;;;;GAIG;AACH,qBAAa,kBAAkB;;IAY3B,QAAQ,CAAC,mBAAmB,yCAA0C;IACtE,QAAQ,CAAC,aAAa,8EAA+E;IACrG,0EAA0E;IAC1E,QAAQ,CAAC,kBAAkB,uBAAwB;IACnD,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,uBAAwB;IAEvC,YAAY,EAAE,EAAE,SAAS,EA+BxB;IAED,IAAI,EAAE,IAAI,MAAM,CAEf;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;OAGG;IACG,WAAW,CAAC,CAAC,SAAS,mBAAmB,EAC3C,OAAO,EAAE,CAAC,EACV,GAAG,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,SAAS,GAAG,EAAE,GAAG,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,GACtG,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CA0B1C;IAED,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI,CAQnF;IAkGD,iFAAiF;IACjF,KAAK,IAAI,IAAI,CAKZ;CAeJ"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2025-2026 Open Home Foundation
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { createPromise, Logger, Observable, Seconds, Time, withTimeout } from "@matter/main";
|
|
7
|
+
import { Mark } from "@matter/main/protocol";
|
|
8
|
+
import { WebSocket } from "ws";
|
|
9
|
+
import {
|
|
10
|
+
BLE_PROXY_PROTOCOL_VERSION,
|
|
11
|
+
decodeBinaryFrame,
|
|
12
|
+
encodeBinaryFrame
|
|
13
|
+
} from "./BleProxyProtocol.js";
|
|
14
|
+
const logger = Logger.get("BleProxyConnection");
|
|
15
|
+
const frameHead = (payload) => Array.from(payload.subarray(0, 8)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
16
|
+
let connectionIdCounter = 0;
|
|
17
|
+
function generateConnectionId() {
|
|
18
|
+
const id = connectionIdCounter;
|
|
19
|
+
connectionIdCounter = connectionIdCounter + 1 & 65535;
|
|
20
|
+
return `ble${id.toString(16)}`;
|
|
21
|
+
}
|
|
22
|
+
const HANDSHAKE_TIMEOUT = Seconds(10);
|
|
23
|
+
const COMMAND_TIMEOUT = Seconds(60);
|
|
24
|
+
class BleProxyConnection {
|
|
25
|
+
#ws;
|
|
26
|
+
#id = generateConnectionId();
|
|
27
|
+
#handshakeComplete = false;
|
|
28
|
+
#closedEmitted = false;
|
|
29
|
+
#pendingCommands = /* @__PURE__ */ new Map();
|
|
30
|
+
/** Command ID counter. Rolls over at 0xFFFF to stay in safe integer range. */
|
|
31
|
+
#nextCommandId = 0;
|
|
32
|
+
binaryFrameReceived = new Observable();
|
|
33
|
+
eventReceived = new Observable();
|
|
34
|
+
/** Emitted once when this connection completes the protocol handshake. */
|
|
35
|
+
handshakeCompleted = new Observable();
|
|
36
|
+
/** Emitted once when this connection is torn down (close, error, or handshake failure). */
|
|
37
|
+
closed = new Observable();
|
|
38
|
+
constructor(ws) {
|
|
39
|
+
this.#ws = ws;
|
|
40
|
+
logger.info(`[${this.#id}] BLE proxy connection established, waiting for handshake`);
|
|
41
|
+
const handshakeTimer = Time.getTimer("BLE proxy handshake timeout", HANDSHAKE_TIMEOUT, () => {
|
|
42
|
+
if (!this.#handshakeComplete) {
|
|
43
|
+
logger.warn(`[${this.#id}] Handshake timeout - closing connection`);
|
|
44
|
+
ws.close(4001, "Handshake timeout");
|
|
45
|
+
this.#cleanup();
|
|
46
|
+
}
|
|
47
|
+
}).start();
|
|
48
|
+
ws.on("message", (data, isBinary) => {
|
|
49
|
+
if (isBinary) {
|
|
50
|
+
this.#handleBinaryMessage(data);
|
|
51
|
+
} else {
|
|
52
|
+
this.#handleTextMessage(data.toString(), handshakeTimer);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
ws.on("close", () => {
|
|
56
|
+
handshakeTimer.stop();
|
|
57
|
+
logger.info(`[${this.#id}] BLE proxy connection closed`);
|
|
58
|
+
this.#cleanup();
|
|
59
|
+
});
|
|
60
|
+
ws.on("error", (err) => {
|
|
61
|
+
logger.error(`[${this.#id}] BLE proxy WebSocket error`, err);
|
|
62
|
+
handshakeTimer.stop();
|
|
63
|
+
this.#cleanup();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
get id() {
|
|
67
|
+
return this.#id;
|
|
68
|
+
}
|
|
69
|
+
get connected() {
|
|
70
|
+
return this.#handshakeComplete && this.#ws.readyState === WebSocket.OPEN;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Send a typed command to this client and wait for the response.
|
|
74
|
+
* Rejects if the connection is not ready or the client disconnects first.
|
|
75
|
+
*/
|
|
76
|
+
async sendCommand(command, ...rest) {
|
|
77
|
+
if (!this.connected) {
|
|
78
|
+
throw new Error("BLE proxy client not connected");
|
|
79
|
+
}
|
|
80
|
+
const args = rest[0];
|
|
81
|
+
const id = this.#nextCommandId;
|
|
82
|
+
this.#nextCommandId = this.#nextCommandId + 1 & 65535;
|
|
83
|
+
const message = { id, command };
|
|
84
|
+
if (args !== void 0) {
|
|
85
|
+
message.args = args;
|
|
86
|
+
}
|
|
87
|
+
const { promise, resolver, rejecter } = createPromise();
|
|
88
|
+
this.#pendingCommands.set(id, { resolver, rejecter });
|
|
89
|
+
this.#ws.send(JSON.stringify(message), (err) => {
|
|
90
|
+
if (err) {
|
|
91
|
+
this.#pendingCommands.delete(id);
|
|
92
|
+
rejecter(new Error(`Failed to send command ${command}: ${err.message}`));
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
return withTimeout(COMMAND_TIMEOUT, promise, () => this.#pendingCommands.delete(id));
|
|
96
|
+
}
|
|
97
|
+
sendBinaryFrame(opcode, connectionHandle, payload) {
|
|
98
|
+
if (!this.connected) {
|
|
99
|
+
throw new Error("BLE proxy client not connected");
|
|
100
|
+
}
|
|
101
|
+
logger.debug(
|
|
102
|
+
`[${this.#id}] [FRAME] ${Mark.OUTBOUND} opcode=${opcode} handle=${connectionHandle} len=${payload.length} head=${frameHead(payload)}`
|
|
103
|
+
);
|
|
104
|
+
this.#ws.send(encodeBinaryFrame(opcode, connectionHandle, payload));
|
|
105
|
+
}
|
|
106
|
+
#handleTextMessage(raw, handshakeTimer) {
|
|
107
|
+
let parsed;
|
|
108
|
+
try {
|
|
109
|
+
parsed = JSON.parse(raw);
|
|
110
|
+
} catch {
|
|
111
|
+
logger.warn(`[${this.#id}] Received invalid JSON from BLE proxy client`);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
const msg = parsed;
|
|
115
|
+
if (!this.#handshakeComplete) {
|
|
116
|
+
this.#handleHandshake(msg, handshakeTimer);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
if ("id" in msg && "success" in msg) {
|
|
120
|
+
this.#handleResponse(msg);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if ("event" in msg && "data" in msg) {
|
|
124
|
+
const evt = msg;
|
|
125
|
+
this.eventReceived.emit(evt.event, evt.data);
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
logger.warn(`[${this.#id}] Received unknown message from BLE proxy client:`, msg);
|
|
129
|
+
}
|
|
130
|
+
#handleHandshake(msg, handshakeTimer) {
|
|
131
|
+
if (msg.type !== "hello") {
|
|
132
|
+
logger.warn(`[${this.#id}] Expected hello message, got: ${JSON.stringify(msg)}`);
|
|
133
|
+
this.#ws.close(4002, "Expected hello message");
|
|
134
|
+
this.#cleanup();
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
const hello = msg;
|
|
138
|
+
handshakeTimer.stop();
|
|
139
|
+
if (hello.version !== BLE_PROXY_PROTOCOL_VERSION) {
|
|
140
|
+
logger.warn(
|
|
141
|
+
`[${this.#id}] BLE proxy client version ${hello.version} not supported (server supports ${BLE_PROXY_PROTOCOL_VERSION})`
|
|
142
|
+
);
|
|
143
|
+
this.#ws.send(
|
|
144
|
+
JSON.stringify({
|
|
145
|
+
type: "hello_response",
|
|
146
|
+
version: BLE_PROXY_PROTOCOL_VERSION,
|
|
147
|
+
error: "unsupported_version",
|
|
148
|
+
message: `Server supports protocol version ${BLE_PROXY_PROTOCOL_VERSION}, client sent version ${hello.version}`
|
|
149
|
+
})
|
|
150
|
+
);
|
|
151
|
+
this.#ws.close(4003, "Unsupported protocol version");
|
|
152
|
+
this.#cleanup();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.#handshakeComplete = true;
|
|
156
|
+
this.#ws.send(JSON.stringify({ type: "hello_response", version: BLE_PROXY_PROTOCOL_VERSION }));
|
|
157
|
+
logger.info(`[${this.#id}] BLE proxy handshake complete (version ${BLE_PROXY_PROTOCOL_VERSION})`);
|
|
158
|
+
this.handshakeCompleted.emit();
|
|
159
|
+
}
|
|
160
|
+
#handleResponse(msg) {
|
|
161
|
+
const pending = this.#pendingCommands.get(msg.id);
|
|
162
|
+
if (!pending) {
|
|
163
|
+
logger.warn(`[${this.#id}] Received response for unknown command id ${msg.id}`);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.#pendingCommands.delete(msg.id);
|
|
167
|
+
if (msg.success) {
|
|
168
|
+
pending.resolver(msg.result);
|
|
169
|
+
} else {
|
|
170
|
+
pending.rejecter(new Error(`${msg.error}: ${msg.message}`));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
#handleBinaryMessage(data) {
|
|
174
|
+
if (!this.#handshakeComplete) {
|
|
175
|
+
logger.warn(`[${this.#id}] Received binary frame before handshake`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
try {
|
|
179
|
+
const frame = decodeBinaryFrame(new Uint8Array(data));
|
|
180
|
+
logger.debug(
|
|
181
|
+
`[${this.#id}] [FRAME] ${Mark.INBOUND} opcode=${frame.opcode} handle=${frame.connectionHandle} len=${frame.payload.length} head=${frameHead(frame.payload)}`
|
|
182
|
+
);
|
|
183
|
+
this.binaryFrameReceived.emit(frame);
|
|
184
|
+
} catch (err) {
|
|
185
|
+
logger.warn(`[${this.#id}] Failed to decode binary frame:`, err);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
/** Close the socket if open, reject all pending commands, emit `closed` once. */
|
|
189
|
+
close() {
|
|
190
|
+
if (this.#ws.readyState === WebSocket.OPEN) {
|
|
191
|
+
this.#ws.close();
|
|
192
|
+
}
|
|
193
|
+
this.#cleanup();
|
|
194
|
+
}
|
|
195
|
+
#cleanup() {
|
|
196
|
+
this.#handshakeComplete = false;
|
|
197
|
+
for (const [id, pending] of this.#pendingCommands) {
|
|
198
|
+
pending.rejecter(new Error("BLE proxy client disconnected"));
|
|
199
|
+
this.#pendingCommands.delete(id);
|
|
200
|
+
}
|
|
201
|
+
if (!this.#closedEmitted) {
|
|
202
|
+
this.#closedEmitted = true;
|
|
203
|
+
this.closed.emit();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export {
|
|
208
|
+
BleProxyConnection
|
|
209
|
+
};
|
|
210
|
+
//# sourceMappingURL=BleProxyConnection.js.map
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/BleProxyConnection.ts"],
|
|
4
|
+
"mappings": "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",
|
|
5
|
+
"names": []
|
|
6
|
+
}
|
|
@@ -6,36 +6,31 @@
|
|
|
6
6
|
import type { WebServerHandler } from "@matter-server/ws-controller";
|
|
7
7
|
import { Observable } from "@matter/main";
|
|
8
8
|
import type { createServer } from "node:http";
|
|
9
|
-
import {
|
|
9
|
+
import { BleProxyConnection } from "./BleProxyConnection.js";
|
|
10
|
+
import { type DeviceDiscoveredData, type StartScanArgs } from "./BleProxyProtocol.js";
|
|
10
11
|
type HttpServer = ReturnType<typeof createServer>;
|
|
11
12
|
/**
|
|
12
|
-
* WebSocket server handler for the
|
|
13
|
+
* WebSocket server handler (hub) for the `/ble` BLE proxy endpoint.
|
|
13
14
|
*
|
|
14
|
-
* Accepts
|
|
15
|
-
* and
|
|
16
|
-
*
|
|
15
|
+
* Accepts any number of proxy client connections, broadcasts scan commands to
|
|
16
|
+
* all of them, and tracks which client owns each discovered peripheral so that
|
|
17
|
+
* per-peripheral traffic routes to a single client.
|
|
17
18
|
*/
|
|
18
19
|
export declare class BleProxyHandler implements WebServerHandler {
|
|
19
20
|
#private;
|
|
20
|
-
/** Emitted
|
|
21
|
-
readonly eventReceived: Observable<[event: BleProxyEventName, data: Record<string, unknown>], void>;
|
|
22
|
-
/** Emitted when a binary frame is received from the BLE proxy client. */
|
|
23
|
-
readonly binaryFrameReceived: Observable<[frame: BinaryFrame], void>;
|
|
24
|
-
/** Emitted once a proxy client has completed the handshake and `connected` is true. */
|
|
21
|
+
/** Emitted whenever a connection completes its handshake. */
|
|
25
22
|
readonly connectionEstablished: Observable<[], void>;
|
|
23
|
+
/** Emitted for each `device_discovered`, after ownership is updated. */
|
|
24
|
+
readonly deviceDiscovered: Observable<[data: DeviceDiscoveredData, connection: BleProxyConnection], void>;
|
|
25
|
+
/** Emitted once no connection is scanning anymore. */
|
|
26
|
+
readonly scanStopped: Observable<[reason: string], void>;
|
|
26
27
|
get connected(): boolean;
|
|
27
28
|
register(server: HttpServer): Promise<void>;
|
|
28
29
|
unregister(): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*/
|
|
34
|
-
sendCommand<C extends BleProxyCommandName>(command: C, ...rest: BleProxyCommandMap[C]["args"] extends undefined ? [] : [args: BleProxyCommandMap[C]["args"]]): Promise<BleProxyCommandMap[C]["result"]>;
|
|
35
|
-
/**
|
|
36
|
-
* Send a binary frame to the BLE proxy client.
|
|
37
|
-
*/
|
|
38
|
-
sendBinaryFrame(opcode: number, connectionHandle: number, payload: Uint8Array): void;
|
|
30
|
+
/** Returns the connection that owns `address`, if it is still connected. */
|
|
31
|
+
getOwner(address: string): BleProxyConnection | undefined;
|
|
32
|
+
startScan(args: StartScanArgs): Promise<void>;
|
|
33
|
+
stopScan(): Promise<void>;
|
|
39
34
|
}
|
|
40
35
|
export {};
|
|
41
36
|
//# sourceMappingURL=BleProxyHandler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BleProxyHandler.d.ts","sourceRoot":"","sources":["../../src/BleProxyHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,
|
|
1
|
+
{"version":3,"file":"BleProxyHandler.d.ts","sourceRoot":"","sources":["../../src/BleProxyHandler.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AACrE,OAAO,EAAU,UAAU,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAkC,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtH,KAAK,UAAU,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AAIlD;;;;;;GAMG;AACH,qBAAa,eAAgB,YAAW,gBAAgB;;IAgBpD,6DAA6D;IAC7D,QAAQ,CAAC,qBAAqB,uBAAwB;IACtD,wEAAwE;IACxE,QAAQ,CAAC,gBAAgB,iFAAkF;IAC3G,sDAAsD;IACtD,QAAQ,CAAC,WAAW,qCAAsC;IAE1D,IAAI,SAAS,IAAI,OAAO,CAKvB;IAEK,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAyChD;IAED,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CA4B1B;IAED,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS,CAIxD;IAEK,SAAS,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBlD;IAEK,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAc9B;CAuEJ"}
|