Skip to content

Commit c64b51d

Browse files
committed
fix: dist
1 parent 4323f98 commit c64b51d

10 files changed

Lines changed: 467 additions & 3 deletions

File tree

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
node_modules/
2-
dist/
32
*.log
43
.DS_Store
54
*.tsbuildinfo

dist/constants.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export declare const SERVICE_UUID = "00001999-0000-1000-8000-00805f9b34fb";
2+
export declare const RX_CHAR_UUID = "4963505f-5258-4000-8000-00805f9b34fb";
3+
export declare const TX_CHAR_UUID = "4963505f-5458-4000-8000-00805f9b34fb";
4+
export declare const TYPE_SINGLE = 0;
5+
export declare const TYPE_START = 64;
6+
export declare const TYPE_CONT = 128;
7+
export declare const TYPE_END = 192;
8+
export declare const HEADER_TYPE_MASK = 192;
9+
export declare const HEADER_SEQ_MASK = 63;
10+
export declare const MAX_GATT_VALUE_LEN = 512;
11+
export declare const ATT_OVERHEAD = 3;
12+
export declare const MIN_PAYLOAD = 20;

dist/constants.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
export const SERVICE_UUID = "00001999-0000-1000-8000-00805f9b34fb";
2+
export const RX_CHAR_UUID = "4963505f-5258-4000-8000-00805f9b34fb";
3+
export const TX_CHAR_UUID = "4963505f-5458-4000-8000-00805f9b34fb";
4+
export const TYPE_SINGLE = 0x00;
5+
export const TYPE_START = 0x40;
6+
export const TYPE_CONT = 0x80;
7+
export const TYPE_END = 0xc0;
8+
export const HEADER_TYPE_MASK = 0xc0;
9+
export const HEADER_SEQ_MASK = 0x3f;
10+
export const MAX_GATT_VALUE_LEN = 512;
11+
export const ATT_OVERHEAD = 3;
12+
export const MIN_PAYLOAD = 20;

dist/framing.d.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
export declare class Framer {
2+
private buffer;
3+
private totalLength;
4+
private inProgress;
5+
private expectedSeq;
6+
feed(packet: Uint8Array): string | null;
7+
private reset;
8+
}
9+
export interface PacketizeResult {
10+
packets: Uint8Array[];
11+
maxPayload: number;
12+
}
13+
export declare function computeMaxPayload(mtu: number, maxGattValueLen?: number, minPayload?: number): number;
14+
export declare function packetizeJson(message: string, maxPayload: number): PacketizeResult;

dist/framing.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { TYPE_SINGLE, TYPE_START, TYPE_CONT, TYPE_END, HEADER_TYPE_MASK, HEADER_SEQ_MASK, ATT_OVERHEAD, MIN_PAYLOAD, MAX_GATT_VALUE_LEN, } from "./constants.js";
2+
export class Framer {
3+
buffer = new Uint8Array(0);
4+
totalLength = 0;
5+
inProgress = false;
6+
expectedSeq = 0;
7+
feed(packet) {
8+
if (packet.length === 0) {
9+
return null;
10+
}
11+
const header = packet[0];
12+
const packetType = header & HEADER_TYPE_MASK;
13+
const seqId = header & HEADER_SEQ_MASK;
14+
const payload = packet.slice(1);
15+
if (packetType === TYPE_SINGLE) {
16+
return new TextDecoder().decode(payload);
17+
}
18+
if (packetType === TYPE_START) {
19+
if (payload.length < 4) {
20+
this.reset();
21+
return null;
22+
}
23+
const dataView = new DataView(payload.buffer, payload.byteOffset, payload.byteLength);
24+
this.totalLength = dataView.getUint32(0, false);
25+
this.buffer = payload.slice(4);
26+
this.inProgress = true;
27+
this.expectedSeq = (seqId + 1) & HEADER_SEQ_MASK;
28+
return null;
29+
}
30+
if (!this.inProgress) {
31+
return null;
32+
}
33+
if (seqId !== this.expectedSeq) {
34+
this.reset();
35+
return null;
36+
}
37+
this.expectedSeq = (this.expectedSeq + 1) & HEADER_SEQ_MASK;
38+
if (packetType === TYPE_CONT) {
39+
const newBuffer = new Uint8Array(this.buffer.length + payload.length);
40+
newBuffer.set(this.buffer);
41+
newBuffer.set(payload, this.buffer.length);
42+
this.buffer = newBuffer;
43+
return null;
44+
}
45+
if (packetType === TYPE_END) {
46+
const newBuffer = new Uint8Array(this.buffer.length + payload.length);
47+
newBuffer.set(this.buffer);
48+
newBuffer.set(payload, this.buffer.length);
49+
this.buffer = newBuffer;
50+
if (this.buffer.length !== this.totalLength) {
51+
this.reset();
52+
return null;
53+
}
54+
const message = new TextDecoder().decode(this.buffer);
55+
this.reset();
56+
return message;
57+
}
58+
return null;
59+
}
60+
reset() {
61+
this.buffer = new Uint8Array(0);
62+
this.totalLength = 0;
63+
this.inProgress = false;
64+
this.expectedSeq = 0;
65+
}
66+
}
67+
export function computeMaxPayload(mtu, maxGattValueLen = MAX_GATT_VALUE_LEN, minPayload = MIN_PAYLOAD) {
68+
let maxPayload = mtu - ATT_OVERHEAD;
69+
if (maxPayload < minPayload) {
70+
maxPayload = minPayload;
71+
}
72+
if (maxPayload > maxGattValueLen) {
73+
maxPayload = maxGattValueLen;
74+
}
75+
return maxPayload;
76+
}
77+
export function packetizeJson(message, maxPayload) {
78+
const data = new TextEncoder().encode(message);
79+
const totalLen = data.length;
80+
if (totalLen + 1 <= maxPayload) {
81+
const packet = new Uint8Array(1 + totalLen);
82+
packet[0] = TYPE_SINGLE | 0;
83+
packet.set(data, 1);
84+
return { packets: [packet], maxPayload };
85+
}
86+
if (maxPayload <= 5) {
87+
throw new Error("MTU too small");
88+
}
89+
const packets = [];
90+
let offset = 0;
91+
let seq = 0;
92+
const startChunkSize = maxPayload - 5;
93+
const startChunk = data.slice(offset, offset + startChunkSize);
94+
const startPacket = new Uint8Array(1 + 4 + startChunk.length);
95+
startPacket[0] = TYPE_START | (seq & HEADER_SEQ_MASK);
96+
const totalLenView = new DataView(startPacket.buffer, startPacket.byteOffset + 1, 4);
97+
totalLenView.setUint32(0, totalLen, false);
98+
startPacket.set(startChunk, 5);
99+
packets.push(startPacket);
100+
offset += startChunk.length;
101+
seq = (seq + 1) & 0xff;
102+
const contChunkSize = maxPayload - 1;
103+
while (offset < totalLen) {
104+
const remaining = totalLen - offset;
105+
if (remaining > contChunkSize) {
106+
const chunk = data.slice(offset, offset + contChunkSize);
107+
const packet = new Uint8Array(1 + chunk.length);
108+
packet[0] = TYPE_CONT | (seq & HEADER_SEQ_MASK);
109+
packet.set(chunk, 1);
110+
packets.push(packet);
111+
offset += chunk.length;
112+
}
113+
else {
114+
const chunk = data.slice(offset, offset + remaining);
115+
const packet = new Uint8Array(1 + chunk.length);
116+
packet[0] = TYPE_END | (seq & HEADER_SEQ_MASK);
117+
packet.set(chunk, 1);
118+
packets.push(packet);
119+
offset += chunk.length;
120+
}
121+
seq = (seq + 1) & 0xff;
122+
}
123+
return { packets, maxPayload };
124+
}

dist/index.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { BleTransport, type BleTransportOptions } from "./transport.js";
2+
export { Framer, computeMaxPayload, packetizeJson, type PacketizeResult } from "./framing.js";
3+
export { SERVICE_UUID, RX_CHAR_UUID, TX_CHAR_UUID, TYPE_SINGLE, TYPE_START, TYPE_CONT, TYPE_END, HEADER_TYPE_MASK, HEADER_SEQ_MASK, MAX_GATT_VALUE_LEN, } from "./constants.js";

dist/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { BleTransport } from "./transport.js";
2+
export { Framer, computeMaxPayload, packetizeJson } from "./framing.js";
3+
export { SERVICE_UUID, RX_CHAR_UUID, TX_CHAR_UUID, TYPE_SINGLE, TYPE_START, TYPE_CONT, TYPE_END, HEADER_TYPE_MASK, HEADER_SEQ_MASK, MAX_GATT_VALUE_LEN, } from "./constants.js";

dist/transport.d.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { Transport, TransportSendOptions } from "@modelcontextprotocol/sdk/shared/transport.js";
2+
import type { JSONRPCMessage } from "@modelcontextprotocol/sdk/types.js";
3+
export interface BleTransportOptions {
4+
serviceUuid?: string;
5+
rxCharUuid?: string;
6+
txCharUuid?: string;
7+
namePrefix?: string;
8+
scanTimeout?: number;
9+
}
10+
export declare class BleTransport implements Transport {
11+
private serviceUuid;
12+
private rxCharUuid;
13+
private txCharUuid;
14+
private namePrefix?;
15+
private scanTimeout;
16+
private peripheral;
17+
private rxCharacteristic;
18+
private txCharacteristic;
19+
private framer;
20+
private currentMtu;
21+
private started;
22+
private discovering;
23+
sessionId?: string;
24+
onclose?: () => void;
25+
onerror?: (error: Error) => void;
26+
onmessage?: <T extends JSONRPCMessage>(message: T) => void;
27+
constructor(options?: BleTransportOptions);
28+
start(): Promise<void>;
29+
send(message: JSONRPCMessage, _options?: TransportSendOptions): Promise<void>;
30+
close(): Promise<void>;
31+
setProtocolVersion(_version: string): void;
32+
private waitForPowerOn;
33+
private discoverDevice;
34+
private connectDevice;
35+
private setupCharacteristics;
36+
private discoverServices;
37+
private subscribeCharacteristic;
38+
private writeCharacteristic;
39+
private disconnectPeripheral;
40+
private handleNotification;
41+
private handleDisconnect;
42+
private cleanup;
43+
}

0 commit comments

Comments
 (0)