@sudobility/tapayoka_bluetooth 0.0.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.
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Protocol-level connection to a Tapayoka device.
3
+ * Wraps a BleTransport with Tapayoka-specific command/response semantics.
4
+ */
5
+ import type { BleTransport } from './transport/BleTransport';
6
+ import type { BleCommand, BleDeviceResponse, BleDeviceInfo, BleDeviceChallenge } from '@sudobility/tapayoka_types';
7
+ export type ResponseListener = (response: BleDeviceResponse) => void;
8
+ export declare class TapayokaDeviceConnection {
9
+ private readonly transport;
10
+ private responseListeners;
11
+ private unsubscribeResponse;
12
+ private _peripheralId;
13
+ constructor(transport: BleTransport);
14
+ get peripheralId(): string | null;
15
+ get connected(): boolean;
16
+ /** Connect to a device and subscribe to the response characteristic. */
17
+ connect(peripheralId: string): Promise<void>;
18
+ /** Disconnect and clean up subscriptions. */
19
+ disconnect(): Promise<void>;
20
+ /** Read device info and signed challenge from the device info characteristic. */
21
+ getDeviceInfo(): Promise<BleDeviceInfo & BleDeviceChallenge>;
22
+ /**
23
+ * Send a command and wait for a single response.
24
+ * Rejects after timeoutMs if no response arrives.
25
+ */
26
+ sendCommand(command: BleCommand, timeoutMs?: number): Promise<BleDeviceResponse>;
27
+ /** Register a persistent listener for all responses. Returns an unsubscribe function. */
28
+ onResponse(listener: ResponseListener): () => void;
29
+ setupServer(serverWalletAddress: string): Promise<BleDeviceResponse>;
30
+ authorize(payload: string, signature: string): Promise<BleDeviceResponse>;
31
+ turnOn(seconds?: number): Promise<BleDeviceResponse>;
32
+ turnOff(): Promise<BleDeviceResponse>;
33
+ getStatus(): Promise<BleDeviceResponse>;
34
+ private removeResponseListener;
35
+ }
36
+ //# sourceMappingURL=TapayokaDeviceConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TapayokaDeviceConnection.d.ts","sourceRoot":"","sources":["../src/TapayokaDeviceConnection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EACV,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EACnB,MAAM,4BAA4B,CAAC;AAOpC,MAAM,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,iBAAiB,KAAK,IAAI,CAAC;AAErE,qBAAa,wBAAwB;IAKvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAJtC,OAAO,CAAC,iBAAiB,CAA0B;IACnD,OAAO,CAAC,mBAAmB,CAA6B;IACxD,OAAO,CAAC,aAAa,CAAuB;gBAEf,SAAS,EAAE,YAAY;IAEpD,IAAI,YAAY,IAAI,MAAM,GAAG,IAAI,CAEhC;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,wEAAwE;IAClE,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBlD,6CAA6C;IACvC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC,iFAAiF;IAC3E,aAAa,IAAI,OAAO,CAAC,aAAa,GAAG,kBAAkB,CAAC;IAKlE;;;OAGG;IACG,WAAW,CACf,OAAO,EAAE,UAAU,EACnB,SAAS,GAAE,MAAc,GACxB,OAAO,CAAC,iBAAiB,CAAC;IAwB7B,yFAAyF;IACzF,UAAU,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,IAAI;IAO5C,WAAW,CAAC,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIpE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAIzE,MAAM,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAOpD,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAIrC,SAAS,IAAI,OAAO,CAAC,iBAAiB,CAAC;IAI7C,OAAO,CAAC,sBAAsB;CAI/B"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Protocol-level connection to a Tapayoka device.
3
+ * Wraps a BleTransport with Tapayoka-specific command/response semantics.
4
+ */
5
+ import { BLE_CHAR_DEVICE_INFO_UUID, BLE_CHAR_COMMAND_UUID, BLE_CHAR_RESPONSE_UUID, } from '@sudobility/tapayoka_types';
6
+ export class TapayokaDeviceConnection {
7
+ constructor(transport) {
8
+ this.transport = transport;
9
+ this.responseListeners = [];
10
+ this.unsubscribeResponse = null;
11
+ this._peripheralId = null;
12
+ }
13
+ get peripheralId() {
14
+ return this._peripheralId;
15
+ }
16
+ get connected() {
17
+ return this.transport.connected;
18
+ }
19
+ /** Connect to a device and subscribe to the response characteristic. */
20
+ async connect(peripheralId) {
21
+ await this.transport.connect(peripheralId);
22
+ this._peripheralId = peripheralId;
23
+ this.unsubscribeResponse = await this.transport.subscribe(BLE_CHAR_RESPONSE_UUID, (value) => {
24
+ try {
25
+ const response = JSON.parse(value);
26
+ for (const listener of this.responseListeners) {
27
+ listener(response);
28
+ }
29
+ }
30
+ catch {
31
+ /* ignore malformed responses */
32
+ }
33
+ });
34
+ }
35
+ /** Disconnect and clean up subscriptions. */
36
+ async disconnect() {
37
+ this.unsubscribeResponse?.();
38
+ this.unsubscribeResponse = null;
39
+ this.responseListeners = [];
40
+ this._peripheralId = null;
41
+ await this.transport.disconnect();
42
+ }
43
+ /** Read device info and signed challenge from the device info characteristic. */
44
+ async getDeviceInfo() {
45
+ const raw = await this.transport.read(BLE_CHAR_DEVICE_INFO_UUID);
46
+ return JSON.parse(raw);
47
+ }
48
+ /**
49
+ * Send a command and wait for a single response.
50
+ * Rejects after timeoutMs if no response arrives.
51
+ */
52
+ async sendCommand(command, timeoutMs = 10000) {
53
+ return new Promise((resolve, reject) => {
54
+ const timer = setTimeout(() => {
55
+ this.removeResponseListener(listener);
56
+ reject(new Error(`Command '${command.command}' timed out after ${timeoutMs}ms`));
57
+ }, timeoutMs);
58
+ const listener = (response) => {
59
+ clearTimeout(timer);
60
+ this.removeResponseListener(listener);
61
+ resolve(response);
62
+ };
63
+ this.responseListeners.push(listener);
64
+ this.transport
65
+ .write(BLE_CHAR_COMMAND_UUID, JSON.stringify(command))
66
+ .catch((err) => {
67
+ clearTimeout(timer);
68
+ this.removeResponseListener(listener);
69
+ reject(err);
70
+ });
71
+ });
72
+ }
73
+ /** Register a persistent listener for all responses. Returns an unsubscribe function. */
74
+ onResponse(listener) {
75
+ this.responseListeners.push(listener);
76
+ return () => this.removeResponseListener(listener);
77
+ }
78
+ // ---- Convenience methods ----
79
+ async setupServer(serverWalletAddress) {
80
+ return this.sendCommand({ command: 'SETUP_SERVER', payload: serverWalletAddress });
81
+ }
82
+ async authorize(payload, signature) {
83
+ return this.sendCommand({ command: 'AUTHORIZE', payload, signature });
84
+ }
85
+ async turnOn(seconds) {
86
+ return this.sendCommand({
87
+ command: 'ON',
88
+ payload: seconds != null ? JSON.stringify({ seconds }) : undefined,
89
+ });
90
+ }
91
+ async turnOff() {
92
+ return this.sendCommand({ command: 'OFF' });
93
+ }
94
+ async getStatus() {
95
+ return this.sendCommand({ command: 'STATUS' });
96
+ }
97
+ removeResponseListener(listener) {
98
+ const idx = this.responseListeners.indexOf(listener);
99
+ if (idx >= 0)
100
+ this.responseListeners.splice(idx, 1);
101
+ }
102
+ }
103
+ //# sourceMappingURL=TapayokaDeviceConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TapayokaDeviceConnection.js","sourceRoot":"","sources":["../src/TapayokaDeviceConnection.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Device discovery for Tapayoka BLE devices.
3
+ * Wraps BleTransport.startScan with Tapayoka-specific filtering and deduplication.
4
+ */
5
+ import type { BleTransport, StopScanFn } from './transport/BleTransport';
6
+ export interface TapayokaDevice {
7
+ /** Transport-specific device ID. */
8
+ id: string;
9
+ /** Full advertised name, e.g. "tapayoka-a1b2c3d4". */
10
+ name: string;
11
+ /** Wallet address prefix extracted from the name. */
12
+ walletAddressPrefix: string;
13
+ /** Signal strength (dBm). -1 for non-BLE transports. */
14
+ rssi: number;
15
+ }
16
+ export declare class TapayokaScanner {
17
+ private readonly transport;
18
+ constructor(transport: BleTransport);
19
+ /**
20
+ * Scan for Tapayoka devices. Calls onFound for each unique device whose name
21
+ * starts with the Tapayoka prefix. Returns a stop function.
22
+ */
23
+ scan(onFound: (device: TapayokaDevice) => void, timeoutMs?: number): Promise<StopScanFn>;
24
+ /**
25
+ * Convenience: scan until a device matching the given wallet address prefix is found.
26
+ * The walletAddress is matched loosely against the device name suffix.
27
+ */
28
+ findByWalletAddress(walletAddress: string, timeoutMs?: number): Promise<TapayokaDevice | null>;
29
+ }
30
+ //# sourceMappingURL=TapayokaScanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TapayokaScanner.d.ts","sourceRoot":"","sources":["../src/TapayokaScanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAGzE,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,sDAAsD;IACtD,IAAI,EAAE,MAAM,CAAC;IACb,qDAAqD;IACrD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAAT,SAAS,EAAE,YAAY;IAEpD;;;OAGG;IACG,IAAI,CACR,OAAO,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,EACzC,SAAS,GAAE,MAAc,GACxB,OAAO,CAAC,UAAU,CAAC;IAgCtB;;;OAGG;IACG,mBAAmB,CACvB,aAAa,EAAE,MAAM,EACrB,SAAS,GAAE,MAAc,GACxB,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC;CAsClC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Device discovery for Tapayoka BLE devices.
3
+ * Wraps BleTransport.startScan with Tapayoka-specific filtering and deduplication.
4
+ */
5
+ import { BLE_DEVICE_NAME_PREFIX } from '@sudobility/tapayoka_types';
6
+ export class TapayokaScanner {
7
+ constructor(transport) {
8
+ this.transport = transport;
9
+ }
10
+ /**
11
+ * Scan for Tapayoka devices. Calls onFound for each unique device whose name
12
+ * starts with the Tapayoka prefix. Returns a stop function.
13
+ */
14
+ async scan(onFound, timeoutMs = 10000) {
15
+ const seen = new Set();
16
+ let timer;
17
+ const stop = await this.transport.startScan((peripheral) => {
18
+ if (peripheral.name?.startsWith(BLE_DEVICE_NAME_PREFIX) &&
19
+ !seen.has(peripheral.id)) {
20
+ seen.add(peripheral.id);
21
+ const prefix = peripheral.name.slice(BLE_DEVICE_NAME_PREFIX.length);
22
+ onFound({
23
+ id: peripheral.id,
24
+ name: peripheral.name,
25
+ walletAddressPrefix: prefix,
26
+ rssi: peripheral.rssi,
27
+ });
28
+ }
29
+ });
30
+ const wrappedStop = () => {
31
+ if (timer)
32
+ clearTimeout(timer);
33
+ stop();
34
+ };
35
+ if (timeoutMs > 0) {
36
+ timer = setTimeout(wrappedStop, timeoutMs);
37
+ }
38
+ return wrappedStop;
39
+ }
40
+ /**
41
+ * Convenience: scan until a device matching the given wallet address prefix is found.
42
+ * The walletAddress is matched loosely against the device name suffix.
43
+ */
44
+ async findByWalletAddress(walletAddress, timeoutMs = 15000) {
45
+ const needle = walletAddress.toLowerCase().replace('0x', '').slice(0, 8);
46
+ return new Promise((resolve) => {
47
+ let resolved = false;
48
+ let stopFn;
49
+ const timer = setTimeout(() => {
50
+ if (!resolved) {
51
+ resolved = true;
52
+ stopFn?.();
53
+ resolve(null);
54
+ }
55
+ }, timeoutMs);
56
+ this.scan((device) => {
57
+ if (!resolved && device.walletAddressPrefix === needle) {
58
+ resolved = true;
59
+ clearTimeout(timer);
60
+ stopFn?.();
61
+ resolve(device);
62
+ }
63
+ }, timeoutMs)
64
+ .then((stop) => {
65
+ stopFn = stop;
66
+ if (resolved)
67
+ stop();
68
+ })
69
+ .catch(() => {
70
+ if (!resolved) {
71
+ resolved = true;
72
+ clearTimeout(timer);
73
+ resolve(null);
74
+ }
75
+ });
76
+ });
77
+ }
78
+ }
79
+ //# sourceMappingURL=TapayokaScanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TapayokaScanner.js","sourceRoot":"","sources":["../src/TapayokaScanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ export type { BleTransport, DiscoveredPeripheral, ScanCallback, StopScanFn, } from './transport/BleTransport';
2
+ export { WebBleTransport } from './transport/WebBleTransport';
3
+ export { ReactNativeBleTransport } from './transport/ReactNativeBleTransport';
4
+ export type { RNBleManager, RNBleDevice } from './transport/ReactNativeBleTransport';
5
+ export { WebSocketTransport } from './transport/WebSocketTransport';
6
+ export { TapayokaScanner } from './TapayokaScanner';
7
+ export type { TapayokaDevice } from './TapayokaScanner';
8
+ export { TapayokaDeviceConnection } from './TapayokaDeviceConnection';
9
+ export type { ResponseListener } from './TapayokaDeviceConnection';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,YAAY,EACZ,UAAU,GACX,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,qCAAqC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAGpE,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC;AACtE,YAAY,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ // Transport implementations
2
+ export { WebBleTransport } from './transport/WebBleTransport';
3
+ export { ReactNativeBleTransport } from './transport/ReactNativeBleTransport';
4
+ export { WebSocketTransport } from './transport/WebSocketTransport';
5
+ // Protocol layer
6
+ export { TapayokaScanner } from './TapayokaScanner';
7
+ export { TapayokaDeviceConnection } from './TapayokaDeviceConnection';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,4BAA4B;AAC5B,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qCAAqC,CAAC;AAE9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAEpE,iBAAiB;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAC"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Abstract BLE transport interface.
3
+ * Each implementation adapts a platform-specific BLE stack (Web Bluetooth,
4
+ * react-native-ble-plx, or WebSocket simulator) to this common interface.
5
+ */
6
+ /** A discovered BLE peripheral (or simulated equivalent). */
7
+ export interface DiscoveredPeripheral {
8
+ /** Platform-specific device identifier (BLE device ID, or WS URL). */
9
+ id: string;
10
+ /** Advertised device name, e.g. "tapayoka-a1b2c3d4". */
11
+ name: string;
12
+ /** Signal strength (dBm). -1 for non-BLE transports. */
13
+ rssi: number;
14
+ }
15
+ /** Callback for scan results. */
16
+ export type ScanCallback = (peripheral: DiscoveredPeripheral) => void;
17
+ /** Function to stop an active scan. */
18
+ export type StopScanFn = () => void;
19
+ /**
20
+ * Minimal transport abstraction over BLE GATT operations.
21
+ *
22
+ * The Tapayoka BLE service UUID is baked into each transport implementation —
23
+ * callers only specify characteristic UUIDs for read/write/subscribe.
24
+ *
25
+ * All characteristic I/O is JSON-over-UTF8 strings (the Pi serializes
26
+ * everything as JSON).
27
+ */
28
+ export interface BleTransport {
29
+ /** Scan for peripherals advertising the Tapayoka BLE service. Returns a stop function. */
30
+ startScan(onFound: ScanCallback): Promise<StopScanFn>;
31
+ /** Connect to a peripheral by its platform-specific ID. */
32
+ connect(peripheralId: string): Promise<void>;
33
+ /** Disconnect from the currently connected peripheral. */
34
+ disconnect(): Promise<void>;
35
+ /** Whether a peripheral is currently connected. */
36
+ readonly connected: boolean;
37
+ /** Read a characteristic value as a UTF-8 string. */
38
+ read(characteristicUuid: string): Promise<string>;
39
+ /** Write a UTF-8 string to a characteristic. */
40
+ write(characteristicUuid: string, value: string): Promise<void>;
41
+ /** Subscribe to notifications on a characteristic. Returns an unsubscribe function. */
42
+ subscribe(characteristicUuid: string, onNotification: (value: string) => void): Promise<() => void>;
43
+ }
44
+ //# sourceMappingURL=BleTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BleTransport.d.ts","sourceRoot":"","sources":["../../src/transport/BleTransport.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,6DAA6D;AAC7D,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,EAAE,EAAE,MAAM,CAAC;IACX,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iCAAiC;AACjC,MAAM,MAAM,YAAY,GAAG,CAAC,UAAU,EAAE,oBAAoB,KAAK,IAAI,CAAC;AAEtE,uCAAuC;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,WAAW,YAAY;IAC3B,0FAA0F;IAC1F,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;IAEtD,2DAA2D;IAC3D,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE7C,0DAA0D;IAC1D,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B,mDAAmD;IACnD,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B,qDAAqD;IACrD,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAElD,gDAAgD;IAChD,KAAK,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEhE,uFAAuF;IACvF,SAAS,CACP,kBAAkB,EAAE,MAAM,EAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,MAAM,IAAI,CAAC,CAAC;CACxB"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Abstract BLE transport interface.
3
+ * Each implementation adapts a platform-specific BLE stack (Web Bluetooth,
4
+ * react-native-ble-plx, or WebSocket simulator) to this common interface.
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=BleTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BleTransport.js","sourceRoot":"","sources":["../../src/transport/BleTransport.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * BLE transport for React Native using react-native-ble-plx.
3
+ *
4
+ * The consumer must create a BleManager from react-native-ble-plx and pass it
5
+ * to the constructor. This avoids importing react-native-ble-plx at the
6
+ * library level (it would fail in non-RN environments).
7
+ */
8
+ import type { BleTransport, ScanCallback, StopScanFn } from './BleTransport';
9
+ /** Minimal interface for the parts of BleManager from react-native-ble-plx we use. */
10
+ export interface RNBleManager {
11
+ startDeviceScan(serviceUUIDs: string[] | null, options: {
12
+ allowDuplicates?: boolean;
13
+ } | null, callback: (error: unknown, device: RNBleDevice | null) => void): void;
14
+ stopDeviceScan(): void;
15
+ connectToDevice(deviceId: string): Promise<RNBleDevice>;
16
+ cancelDeviceConnection(deviceId: string): Promise<RNBleDevice>;
17
+ }
18
+ export interface RNBleDevice {
19
+ id: string;
20
+ name: string | null;
21
+ rssi: number | null;
22
+ discoverAllServicesAndCharacteristics(): Promise<RNBleDevice>;
23
+ readCharacteristicForService(serviceUuid: string, charUuid: string): Promise<{
24
+ value: string | null;
25
+ }>;
26
+ writeCharacteristicWithResponseForService(serviceUuid: string, charUuid: string, value: string): Promise<unknown>;
27
+ monitorCharacteristicForService(serviceUuid: string, charUuid: string, callback: (error: unknown, char: {
28
+ value: string | null;
29
+ } | null) => void): {
30
+ remove(): void;
31
+ };
32
+ }
33
+ export declare class ReactNativeBleTransport implements BleTransport {
34
+ private readonly bleManager;
35
+ private device;
36
+ constructor(bleManager: RNBleManager);
37
+ get connected(): boolean;
38
+ startScan(onFound: ScanCallback): Promise<StopScanFn>;
39
+ connect(peripheralId: string): Promise<void>;
40
+ disconnect(): Promise<void>;
41
+ read(characteristicUuid: string): Promise<string>;
42
+ write(characteristicUuid: string, value: string): Promise<void>;
43
+ subscribe(characteristicUuid: string, onNotification: (value: string) => void): Promise<() => void>;
44
+ }
45
+ //# sourceMappingURL=ReactNativeBleTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativeBleTransport.d.ts","sourceRoot":"","sources":["../../src/transport/ReactNativeBleTransport.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAK7E,sFAAsF;AACtF,MAAM,WAAW,YAAY;IAC3B,eAAe,CACb,YAAY,EAAE,MAAM,EAAE,GAAG,IAAI,EAC7B,OAAO,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,EAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,GAC7D,IAAI,CAAC;IACR,cAAc,IAAI,IAAI,CAAC;IACvB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACxD,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qCAAqC,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;IAC9D,4BAA4B,CAC1B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;IACrC,yCAAyC,CACvC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,+BAA+B,CAC7B,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,GACxE;QAAE,MAAM,IAAI,IAAI,CAAA;KAAE,CAAC;CACvB;AAED,qBAAa,uBAAwB,YAAW,YAAY;IAG9C,OAAO,CAAC,QAAQ,CAAC,UAAU;IAFvC,OAAO,CAAC,MAAM,CAA4B;gBAEb,UAAU,EAAE,YAAY;IAErD,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAgBrD,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUjD,KAAK,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D,SAAS,CACb,kBAAkB,EAAE,MAAM,EAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,MAAM,IAAI,CAAC;CAYvB"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * BLE transport for React Native using react-native-ble-plx.
3
+ *
4
+ * The consumer must create a BleManager from react-native-ble-plx and pass it
5
+ * to the constructor. This avoids importing react-native-ble-plx at the
6
+ * library level (it would fail in non-RN environments).
7
+ */
8
+ import { BLE_SERVICE_UUID } from '@sudobility/tapayoka_types';
9
+ const SERVICE_UUID = BLE_SERVICE_UUID.toLowerCase();
10
+ export class ReactNativeBleTransport {
11
+ constructor(bleManager) {
12
+ this.bleManager = bleManager;
13
+ this.device = null;
14
+ }
15
+ get connected() {
16
+ return this.device !== null;
17
+ }
18
+ async startScan(onFound) {
19
+ this.bleManager.startDeviceScan([SERVICE_UUID], { allowDuplicates: false }, (error, device) => {
20
+ if (error || !device)
21
+ return;
22
+ onFound({
23
+ id: device.id,
24
+ name: device.name ?? '',
25
+ rssi: device.rssi ?? -1,
26
+ });
27
+ });
28
+ return () => this.bleManager.stopDeviceScan();
29
+ }
30
+ async connect(peripheralId) {
31
+ const device = await this.bleManager.connectToDevice(peripheralId);
32
+ this.device = await device.discoverAllServicesAndCharacteristics();
33
+ }
34
+ async disconnect() {
35
+ if (this.device) {
36
+ await this.bleManager.cancelDeviceConnection(this.device.id);
37
+ this.device = null;
38
+ }
39
+ }
40
+ async read(characteristicUuid) {
41
+ if (!this.device)
42
+ throw new Error('Not connected');
43
+ const result = await this.device.readCharacteristicForService(SERVICE_UUID, characteristicUuid.toLowerCase());
44
+ // react-native-ble-plx returns base64-encoded values
45
+ return atob(result.value ?? '');
46
+ }
47
+ async write(characteristicUuid, value) {
48
+ if (!this.device)
49
+ throw new Error('Not connected');
50
+ // react-native-ble-plx expects base64-encoded values
51
+ const encoded = btoa(value);
52
+ await this.device.writeCharacteristicWithResponseForService(SERVICE_UUID, characteristicUuid.toLowerCase(), encoded);
53
+ }
54
+ async subscribe(characteristicUuid, onNotification) {
55
+ if (!this.device)
56
+ throw new Error('Not connected');
57
+ const subscription = this.device.monitorCharacteristicForService(SERVICE_UUID, characteristicUuid.toLowerCase(), (error, char) => {
58
+ if (error || !char?.value)
59
+ return;
60
+ onNotification(atob(char.value));
61
+ });
62
+ return () => subscription.remove();
63
+ }
64
+ }
65
+ //# sourceMappingURL=ReactNativeBleTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReactNativeBleTransport.js","sourceRoot":"","sources":["../../src/transport/ReactNativeBleTransport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * BLE transport using the Web Bluetooth API.
3
+ *
4
+ * Web Bluetooth has a special scanning model: navigator.bluetooth.requestDevice()
5
+ * shows a browser picker, not a programmatic scan. This transport adapts that
6
+ * model into the BleTransport interface by treating the picker as a "scan that
7
+ * returns exactly one result."
8
+ */
9
+ import type { BleTransport, ScanCallback, StopScanFn } from './BleTransport';
10
+ export declare class WebBleTransport implements BleTransport {
11
+ private device;
12
+ private server;
13
+ private service;
14
+ get connected(): boolean;
15
+ startScan(onFound: ScanCallback): Promise<StopScanFn>;
16
+ connect(peripheralId: string): Promise<void>;
17
+ disconnect(): Promise<void>;
18
+ read(characteristicUuid: string): Promise<string>;
19
+ write(characteristicUuid: string, value: string): Promise<void>;
20
+ subscribe(characteristicUuid: string, onNotification: (value: string) => void): Promise<() => void>;
21
+ }
22
+ //# sourceMappingURL=WebBleTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebBleTransport.d.ts","sourceRoot":"","sources":["../../src/transport/WebBleTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAK7E,qBAAa,eAAgB,YAAW,YAAY;IAClD,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,MAAM,CAA0C;IACxD,OAAO,CAAC,OAAO,CAA2C;IAE1D,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAqBrD,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ5C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAM3B,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAMjD,KAAK,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAM/D,SAAS,CACb,kBAAkB,EAAE,MAAM,EAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,MAAM,IAAI,CAAC;CAqBvB"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * BLE transport using the Web Bluetooth API.
3
+ *
4
+ * Web Bluetooth has a special scanning model: navigator.bluetooth.requestDevice()
5
+ * shows a browser picker, not a programmatic scan. This transport adapts that
6
+ * model into the BleTransport interface by treating the picker as a "scan that
7
+ * returns exactly one result."
8
+ */
9
+ import { BLE_SERVICE_UUID } from '@sudobility/tapayoka_types';
10
+ const SERVICE_UUID = BLE_SERVICE_UUID.toLowerCase();
11
+ export class WebBleTransport {
12
+ constructor() {
13
+ this.device = null;
14
+ this.server = null;
15
+ this.service = null;
16
+ }
17
+ get connected() {
18
+ return this.server?.connected ?? false;
19
+ }
20
+ async startScan(onFound) {
21
+ const device = await navigator.bluetooth.requestDevice({
22
+ filters: [{ namePrefix: 'tapayoka-' }],
23
+ optionalServices: [SERVICE_UUID],
24
+ });
25
+ this.device = device;
26
+ if (device) {
27
+ onFound({
28
+ id: device.id,
29
+ name: device.name ?? '',
30
+ rssi: -1,
31
+ });
32
+ }
33
+ return () => {
34
+ /* picker already closed */
35
+ };
36
+ }
37
+ async connect(peripheralId) {
38
+ if (!this.device || this.device.id !== peripheralId) {
39
+ throw new Error('Device not found. Call startScan first to select a device.');
40
+ }
41
+ this.server = (await this.device.gatt.connect()) ?? null;
42
+ this.service = await this.server.getPrimaryService(SERVICE_UUID);
43
+ }
44
+ async disconnect() {
45
+ this.server?.disconnect();
46
+ this.server = null;
47
+ this.service = null;
48
+ }
49
+ async read(characteristicUuid) {
50
+ const char = await this.service.getCharacteristic(characteristicUuid.toLowerCase());
51
+ const value = await char.readValue();
52
+ return new TextDecoder().decode(value);
53
+ }
54
+ async write(characteristicUuid, value) {
55
+ const char = await this.service.getCharacteristic(characteristicUuid.toLowerCase());
56
+ const encoded = new TextEncoder().encode(value);
57
+ await char.writeValue(encoded);
58
+ }
59
+ async subscribe(characteristicUuid, onNotification) {
60
+ const char = await this.service.getCharacteristic(characteristicUuid.toLowerCase());
61
+ await char.startNotifications();
62
+ const handler = (event) => {
63
+ const target = event.target;
64
+ if (target.value) {
65
+ onNotification(new TextDecoder().decode(target.value));
66
+ }
67
+ };
68
+ char.addEventListener('characteristicvaluechanged', handler);
69
+ return () => {
70
+ char.removeEventListener('characteristicvaluechanged', handler);
71
+ try {
72
+ char.stopNotifications();
73
+ }
74
+ catch {
75
+ /* may already be disconnected */
76
+ }
77
+ };
78
+ }
79
+ }
80
+ //# sourceMappingURL=WebBleTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebBleTransport.js","sourceRoot":"","sources":["../../src/transport/WebBleTransport.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * WebSocket transport that talks to a tapayoka_pi simulator.
3
+ * Speaks the protocol defined by tapayoka_pi/src/ws_peripheral.py:
4
+ *
5
+ * On connect → server sends {"type": "announce", "data": {deviceName, walletAddress}}
6
+ * read_device_info → client sends {"type": "read_device_info"}, server replies {"type": "device_info", "data": {...}}
7
+ * command → client sends {"type": "command", "data": {...}}, server replies {"type": "response", "data": {...}}
8
+ */
9
+ import type { BleTransport, ScanCallback, StopScanFn } from './BleTransport';
10
+ export declare class WebSocketTransport implements BleTransport {
11
+ private readonly wsUrl;
12
+ private ws;
13
+ private responseListeners;
14
+ private _connected;
15
+ constructor(wsUrl?: string);
16
+ get connected(): boolean;
17
+ startScan(onFound: ScanCallback): Promise<StopScanFn>;
18
+ connect(_peripheralId: string): Promise<void>;
19
+ disconnect(): Promise<void>;
20
+ read(characteristicUuid: string): Promise<string>;
21
+ write(characteristicUuid: string, value: string): Promise<void>;
22
+ subscribe(characteristicUuid: string, onNotification: (value: string) => void): Promise<() => void>;
23
+ /** Send a WS message and wait for a response of the expected type. */
24
+ private sendAndReceive;
25
+ }
26
+ //# sourceMappingURL=WebSocketTransport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketTransport.d.ts","sourceRoot":"","sources":["../../src/transport/WebSocketTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAY7E,qBAAa,kBAAmB,YAAW,YAAY;IAKzC,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJlC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,iBAAiB,CAAsC;IAC/D,OAAO,CAAC,UAAU,CAAS;gBAEE,KAAK,GAAE,MAA8B;IAElE,IAAI,SAAS,IAAI,OAAO,CAEvB;IAEK,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC;IAoCrD,OAAO,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B7C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAO3B,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAQjD,KAAK,CAAC,kBAAkB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/D,SAAS,CACb,kBAAkB,EAAE,MAAM,EAC1B,cAAc,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GACtC,OAAO,CAAC,MAAM,IAAI,CAAC;IAYtB,sEAAsE;IACtE,OAAO,CAAC,cAAc;CA8BvB"}
@@ -0,0 +1,137 @@
1
+ /**
2
+ * WebSocket transport that talks to a tapayoka_pi simulator.
3
+ * Speaks the protocol defined by tapayoka_pi/src/ws_peripheral.py:
4
+ *
5
+ * On connect → server sends {"type": "announce", "data": {deviceName, walletAddress}}
6
+ * read_device_info → client sends {"type": "read_device_info"}, server replies {"type": "device_info", "data": {...}}
7
+ * command → client sends {"type": "command", "data": {...}}, server replies {"type": "response", "data": {...}}
8
+ */
9
+ import { BLE_CHAR_DEVICE_INFO_UUID, BLE_CHAR_COMMAND_UUID, BLE_CHAR_RESPONSE_UUID, } from '@sudobility/tapayoka_types';
10
+ export class WebSocketTransport {
11
+ constructor(wsUrl = 'ws://localhost:8765') {
12
+ this.wsUrl = wsUrl;
13
+ this.ws = null;
14
+ this.responseListeners = [];
15
+ this._connected = false;
16
+ }
17
+ get connected() {
18
+ return this._connected;
19
+ }
20
+ async startScan(onFound) {
21
+ return new Promise((resolve, reject) => {
22
+ const ws = new WebSocket(this.wsUrl);
23
+ let resolved = false;
24
+ ws.onmessage = (event) => {
25
+ try {
26
+ const msg = JSON.parse(typeof event.data === 'string' ? event.data : '');
27
+ if (msg.type === 'announce' && msg.data) {
28
+ onFound({
29
+ id: this.wsUrl,
30
+ name: msg.data.deviceName ?? '',
31
+ rssi: -1,
32
+ });
33
+ }
34
+ }
35
+ catch {
36
+ /* ignore */
37
+ }
38
+ };
39
+ ws.onopen = () => {
40
+ this.ws = ws;
41
+ resolved = true;
42
+ resolve(() => {
43
+ /* stop scanning is a no-op for WS */
44
+ });
45
+ };
46
+ ws.onerror = (e) => {
47
+ if (!resolved)
48
+ reject(e);
49
+ };
50
+ });
51
+ }
52
+ async connect(_peripheralId) {
53
+ // If startScan wasn't called or the connection dropped, create a new one.
54
+ if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
55
+ await new Promise((resolve, reject) => {
56
+ this.ws = new WebSocket(this.wsUrl);
57
+ this.ws.onopen = () => resolve();
58
+ this.ws.onerror = (e) => reject(e);
59
+ });
60
+ }
61
+ // Wire up response listener dispatch
62
+ this.ws.addEventListener('message', (event) => {
63
+ try {
64
+ const msg = JSON.parse(typeof event.data === 'string' ? event.data : '');
65
+ if (msg.type === 'response' && msg.data) {
66
+ const value = JSON.stringify(msg.data);
67
+ for (const listener of this.responseListeners) {
68
+ listener(value);
69
+ }
70
+ }
71
+ }
72
+ catch {
73
+ /* ignore */
74
+ }
75
+ });
76
+ this._connected = true;
77
+ }
78
+ async disconnect() {
79
+ this.ws?.close();
80
+ this.ws = null;
81
+ this._connected = false;
82
+ this.responseListeners = [];
83
+ }
84
+ async read(characteristicUuid) {
85
+ const normalized = characteristicUuid.toLowerCase();
86
+ if (normalized === BLE_CHAR_DEVICE_INFO_UUID.toLowerCase()) {
87
+ return this.sendAndReceive({ type: 'read_device_info' }, 'device_info');
88
+ }
89
+ throw new Error(`WS transport: unsupported read for characteristic ${characteristicUuid}`);
90
+ }
91
+ async write(characteristicUuid, value) {
92
+ const normalized = characteristicUuid.toLowerCase();
93
+ if (normalized === BLE_CHAR_COMMAND_UUID.toLowerCase()) {
94
+ const commandData = JSON.parse(value);
95
+ this.ws.send(JSON.stringify({ type: 'command', data: commandData }));
96
+ return;
97
+ }
98
+ throw new Error(`WS transport: unsupported write for characteristic ${characteristicUuid}`);
99
+ }
100
+ async subscribe(characteristicUuid, onNotification) {
101
+ const normalized = characteristicUuid.toLowerCase();
102
+ if (normalized === BLE_CHAR_RESPONSE_UUID.toLowerCase()) {
103
+ this.responseListeners.push(onNotification);
104
+ return () => {
105
+ const idx = this.responseListeners.indexOf(onNotification);
106
+ if (idx >= 0)
107
+ this.responseListeners.splice(idx, 1);
108
+ };
109
+ }
110
+ throw new Error(`WS transport: unsupported subscribe for characteristic ${characteristicUuid}`);
111
+ }
112
+ /** Send a WS message and wait for a response of the expected type. */
113
+ sendAndReceive(message, expectedType, timeoutMs = 10000) {
114
+ return new Promise((resolve, reject) => {
115
+ const timer = setTimeout(() => {
116
+ this.ws?.removeEventListener('message', handler);
117
+ reject(new Error(`WS timeout waiting for ${expectedType}`));
118
+ }, timeoutMs);
119
+ const handler = (event) => {
120
+ try {
121
+ const msg = JSON.parse(typeof event.data === 'string' ? event.data : '');
122
+ if (msg.type === expectedType) {
123
+ this.ws?.removeEventListener('message', handler);
124
+ clearTimeout(timer);
125
+ resolve(JSON.stringify(msg.data));
126
+ }
127
+ }
128
+ catch {
129
+ /* ignore */
130
+ }
131
+ };
132
+ this.ws.addEventListener('message', handler);
133
+ this.ws.send(JSON.stringify(message));
134
+ });
135
+ }
136
+ }
137
+ //# sourceMappingURL=WebSocketTransport.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WebSocketTransport.js","sourceRoot":"","sources":["../../src/transport/WebSocketTransport.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@sudobility/tapayoka_bluetooth",
3
+ "version": "0.0.1",
4
+ "description": "Platform-agnostic BLE communication library for Tapayoka devices",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "import": "./dist/index.js",
10
+ "types": "./dist/index.d.ts"
11
+ }
12
+ },
13
+ "scripts": {
14
+ "build": "tsc -p tsconfig.esm.json",
15
+ "clean": "rm -rf dist",
16
+ "dev": "bunx tsc --watch",
17
+ "typecheck": "bunx tsc --noEmit",
18
+ "lint": "bunx eslint src/",
19
+ "lint:fix": "bunx eslint src/ --fix",
20
+ "format": "bunx prettier --write \"src/**/*.ts\"",
21
+ "format:check": "bunx prettier --check \"src/**/*.ts\"",
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "verify": "bun run typecheck && bun run lint && bun run build",
25
+ "prepublishOnly": "bun run clean && bun run verify"
26
+ },
27
+ "files": [
28
+ "dist/**/*"
29
+ ],
30
+ "keywords": [
31
+ "typescript",
32
+ "bluetooth",
33
+ "ble",
34
+ "tapayoka",
35
+ "iot",
36
+ "websocket"
37
+ ],
38
+ "author": "Sudobility",
39
+ "license": "BUSL-1.1",
40
+ "peerDependencies": {
41
+ "@sudobility/tapayoka_types": "^0.0.7"
42
+ },
43
+ "devDependencies": {
44
+ "@eslint/js": "^9.39.2",
45
+ "@sudobility/tapayoka_types": "^0.0.7",
46
+ "@types/web-bluetooth": "^0.0.21",
47
+ "@typescript-eslint/eslint-plugin": "^8.50.0",
48
+ "@typescript-eslint/parser": "^8.50.0",
49
+ "eslint": "^9.39.2",
50
+ "globals": "^16.5.0",
51
+ "prettier": "^3.7.4",
52
+ "typescript": "^5.9.3",
53
+ "vitest": "^4.0.16"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ }
58
+ }