diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml new file mode 100644 index 0000000..655453b --- /dev/null +++ b/.github/workflows/checks.yml @@ -0,0 +1,22 @@ +on: + pull: + push: + branches: + - main + +name: checks + +jobs: + checks: + strategy: + matrix: + command: [typecheck, test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 18 + registry-url: 'https://registry.npmjs.org' + - run: npm ci + - run: npm run ${{ matrix.command }} \ No newline at end of file diff --git a/src/transport/errors.ts b/src/transport/errors.ts new file mode 100644 index 0000000..909ed97 --- /dev/null +++ b/src/transport/errors.ts @@ -0,0 +1,8 @@ +export class UserCancelledError extends Error { + constructor(m: string, opts: ErrorOptions) { + super(m, opts); + + // Set the prototype explicitly. + Object.setPrototypeOf(this, UserCancelledError.prototype); + } +} \ No newline at end of file diff --git a/src/transport/gatt.ts b/src/transport/gatt.ts index 59002c2..d41bddb 100644 --- a/src/transport/gatt.ts +++ b/src/transport/gatt.ts @@ -1,4 +1,5 @@ import type { RpcTransport } from './'; +import { UserCancelledError } from './errors'; const SERVICE_UUID = '00000000-0196-6107-c967-c5cfb1c2482a'; const RPC_CHRC_UUID = '00000001-0196-6107-c967-c5cfb1c2482a'; @@ -7,6 +8,12 @@ export async function connect(): Promise { let dev = await navigator.bluetooth.requestDevice({ filters: [{ services: [SERVICE_UUID] }], optionalServices: [SERVICE_UUID], + }).catch((e) => { + if (e instanceof DOMException && e.name == "NotFoundError") { + throw new UserCancelledError("User cancelled the connection attempt", { cause: e}); + } else { + throw e; + } }); if (!dev.gatt) { diff --git a/src/transport/index.ts b/src/transport/index.ts index e2366aa..4999e04 100644 --- a/src/transport/index.ts +++ b/src/transport/index.ts @@ -4,5 +4,3 @@ export interface RpcTransport { readable: ReadableStream; writable: WritableStream; } - -export * as Gatt from './gatt'; diff --git a/src/transport/serial.ts b/src/transport/serial.ts index 3a0f819..1060f09 100644 --- a/src/transport/serial.ts +++ b/src/transport/serial.ts @@ -4,7 +4,14 @@ export async function connect(): Promise { let abortController = new AbortController(); let port = await navigator.serial.requestPort({}); - await port.open({ baudRate: 12500 }); + await port.open({ baudRate: 12500 }) + .catch((e) => { + if (e instanceof DOMException && e.name === "NetworkError") { + throw new Error("Failed to open the serial port. Check the permissions of the device and verify it is not in use by another process.", { cause: e }); + } else { + throw e; + } + }); let info = port.getInfo(); let label = diff --git a/test/framing.spec.ts b/test/framing.spec.ts index 1a7a175..f35f6ec 100644 --- a/test/framing.spec.ts +++ b/test/framing.spec.ts @@ -13,12 +13,12 @@ describe('framing', () => { ); let reader = converted.getReader(); - let chunks = []; + let chunks: Array = []; try { while (true) { const { done, value } = await reader.read(); - if (done) break; + if (done || !value) break; chunks = chunks.concat(Array.from(value.values())); } } finally { @@ -38,12 +38,12 @@ describe('framing', () => { ); let reader = converted.getReader(); - let chunks = []; + let chunks: Array = []; try { while (true) { const { done, value } = await reader.read(); - if (done) break; + if (done || !value) break; chunks = chunks.concat(Array.from(value.values())); } } finally {