diff --git a/package.json b/package.json index 0b576a5..132f465 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "module": "./dist/index.mjs", "name": "chameleon-ultra.js", "type": "commonjs", - "version": "0.3.4", + "version": "0.3.5", "bugs": { "url": "https://github.com/taichunmin/chameleon-ultra.js/issues" }, @@ -23,7 +23,9 @@ "@taichunmin/buffer": "^0.13.4", "debug": "^4.3.4", "lodash": "^4.17.21", - "serialport": "^12.0.0" + "serialport": "^12.0.0", + "web-serial-polyfill": "^1.0.15", + "webbluetooth": "^3.2.1" }, "devDependencies": { "@tsconfig/node-lts": "^20.1.3", @@ -73,9 +75,7 @@ "typedoc-plugin-rename-defaults": "^0.7.0", "typedoc-plugin-zod": "^1.1.2", "typescript": "^5.4.5", - "utility-types": "^3.11.0", - "web-serial-polyfill": "^1.0.15", - "webbluetooth": "^3.2.1" + "utility-types": "^3.11.0" }, "exports": { "./package.json": "./package.json", diff --git a/src/ChameleonUltra.ts b/src/ChameleonUltra.ts index b7572ed..e274561 100644 --- a/src/ChameleonUltra.ts +++ b/src/ChameleonUltra.ts @@ -37,7 +37,7 @@ const READ_DEFAULT_TIMEOUT = 5e3 const START_OF_FRAME = new Buffer(2).writeUInt16BE(0x11EF) const VERSION_SUPPORTED = { gte: '2.0', lt: '3.0' } as const -const WritableStream1: typeof WritableStream = (globalThis as any).WritableStream ?? WritableStream +const WritableStream1: typeof WritableStream = (globalThis as any)?.WritableStream ?? WritableStream function isMf1BlockNo (block: any): boolean { return _.isInteger(block) && block >= 0 && block <= 0xFF @@ -711,9 +711,7 @@ export class ChameleonUltra { const cmd = Cmd.GET_DEVICE_ADDRESS // cmd = 1012 await this._writeCmd({ cmd }) const data = (await this._readRespTimeout({ cmd }))?.data - const arr = [] - for (let i = 0; i < data.length; i++) arr.push(data.subarray(i, i + 1).toString('hex')) - return _.toUpper(arr.join(':')) + return (_.toUpper(data.toString('hex')).match(/.{2}/g) ?? []).join(':') } /** diff --git a/src/enums.ts b/src/enums.ts index db7ba40..9247cf2 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -6,7 +6,7 @@ export interface EnumLike { } export function createIsEnum (e: T): (val: any) => val is T[keyof T] { - const ev = new Set(_.chain(e).toPairs().filter(([k, v]) => !_.isNumber(e[v])).map(1).value()) + const ev = new Set(_.values(_.pickBy(e, (v, k) => !_.isNumber(e[v])))) return (val: any): val is T[keyof T] => ev.has(val) } @@ -276,5 +276,4 @@ export const isMf1VblockOperator = createIsEnum(Mf1VblockOperator) export const isRespStatus = createIsEnum(RespStatus) export const isSlot = createIsEnum(Slot) export const isTagType = createIsEnum(TagType) - export const isValidFreqType = createIsEnum(_.pick(FreqType, ['HF', 'LF'])) diff --git a/src/plugin/BufferMockAdapter.ts b/src/plugin/BufferMockAdapter.ts index de752fd..96d289e 100644 --- a/src/plugin/BufferMockAdapter.ts +++ b/src/plugin/BufferMockAdapter.ts @@ -3,8 +3,8 @@ import { ReadableStream, WritableStream, type ReadableStreamController } from 'n import { type Buffer } from '@taichunmin/buffer' import { type ChameleonPlugin, type ChameleonSerialPort, type PluginInstallContext } from '../ChameleonUltra' -const ReadableStream1: typeof ReadableStream = (globalThis as any).ReadableStream ?? ReadableStream -const WritableStream1: typeof WritableStream = (globalThis as any).WritableStream ?? WritableStream +const ReadableStream1: typeof ReadableStream = (globalThis as any)?.ReadableStream ?? ReadableStream +const WritableStream1: typeof WritableStream = (globalThis as any)?.WritableStream ?? WritableStream type AdapterInstallContext = PluginInstallContext & { ultra: PluginInstallContext['ultra'] & { $adapter?: any } diff --git a/src/plugin/WebbleAdapter.ts b/src/plugin/WebbleAdapter.ts index 1da4cc3..7cd247e 100644 --- a/src/plugin/WebbleAdapter.ts +++ b/src/plugin/WebbleAdapter.ts @@ -1,14 +1,13 @@ import _ from 'lodash' -import { type bluetooth } from 'webbluetooth' -import { Buffer } from '@taichunmin/buffer' import { ReadableStream, type ReadableStreamDefaultController, type UnderlyingSink, type UnderlyingSource, WritableStream } from 'node:stream/web' import { sleep } from '../helper' +import { type bluetooth } from 'webbluetooth' +import { type Buffer } from '@taichunmin/buffer' import { type ChameleonPlugin, type Logger, type PluginInstallContext } from '../ChameleonUltra' const bluetooth1: typeof bluetooth = (globalThis as any)?.navigator?.bluetooth -const ReadableStream1: typeof ReadableStream = (globalThis as any).ReadableStream ?? ReadableStream -const WritableStream1: typeof WritableStream = (globalThis as any).WritableStream ?? WritableStream -console.log({ bluetooth1, ReadableStream1, WritableStream1 }) +const ReadableStream1: typeof ReadableStream = (globalThis as any)?.ReadableStream ?? ReadableStream +const WritableStream1: typeof WritableStream = (globalThis as any)?.WritableStream ?? WritableStream const BLESERIAL_FILTERS = [ { name: 'ChameleonUltra' }, @@ -159,23 +158,26 @@ class ChameleonWebbleAdapterRxSource implements UnderlyingSource { } class ChameleonWebbleAdapterTxSink implements UnderlyingSink { - adapter: WebbleAdapter - isFirstEsc = true - - constructor (adapter: WebbleAdapter) { this.adapter = adapter } + readonly #adapter: WebbleAdapter + readonly #Buffer: typeof Buffer + + constructor (adapter: WebbleAdapter) { + this.#adapter = adapter + if (_.isNil(this.#adapter.send)) throw new Error('this.adapter.send can not be null') + if (_.isNil(this.#adapter.Buffer)) throw new Error('this.adapter.Buffer can not be null') + this.#Buffer = this.#adapter.Buffer + } async write (chunk: Buffer): Promise { - if (_.isNil(this.adapter.send)) throw new Error('this.adapter.send can not be null') - // 20 bytes are left for the attribute data // https://stackoverflow.com/questions/38913743/maximum-packet-length-for-bluetooth-le let buf1: Buffer | null = null for (let i = 0; i < chunk.length; i += 20) { const buf2 = chunk.subarray(i, i + 20) - if (_.isNil(buf1) || buf1.length !== buf2.length) buf1 = new Buffer(buf2.length) + if (_.isNil(buf1) || buf1.length !== buf2.length) buf1 = new this.#Buffer(buf2.length) buf1.set(buf2) - this.adapter.logger.webble(`bleWrite = ${buf1.toString('hex')}`) - await this.adapter.send?.writeValueWithoutResponse(buf1.buffer) + this.#adapter.logger.webble(`bleWrite = ${buf1.toString('hex')}`) + await this.#adapter.send?.writeValueWithoutResponse(buf1.buffer) } } } diff --git a/src/plugin/WebserialAdapter.ts b/src/plugin/WebserialAdapter.ts index c97817e..cd79818 100644 --- a/src/plugin/WebserialAdapter.ts +++ b/src/plugin/WebserialAdapter.ts @@ -12,22 +12,23 @@ type SerialPort1 = SerialPort & { } ) => any } -const serial1: typeof serial = (globalThis as any)?.navigator?.serial ?? serial +const navigator = (globalThis as any)?.navigator ?? {} +const serial1: typeof serial = navigator.serial ?? ('usb' in navigator ? serial : null) const WEBSERIAL_FILTERS = [ { usbVendorId: 0x6868, usbProductId: 0x8686 }, // Chameleon Tiny ] +function u16ToHex (num: number): string { + return _.toUpper(`000${num.toString(16)}`.slice(-4)) +} + export default class WebserialAdapter implements ChameleonPlugin { isOpen: boolean = false logger: Record = {} name = 'adapter' port?: SerialPort1 - static u16ToHex (num: number): string { - return _.toUpper(`000${num.toString(16)}`.slice(-4)) - } - async install (context: AdapterInstallContext, pluginOption: any): Promise { const { ultra } = context this.logger.webserial = ultra.createDebugger('webserial') @@ -51,10 +52,10 @@ export default class WebserialAdapter implements ChameleonPlugin { this.isOpen = true const info = await this.port.getInfo() as { usbVendorId: number, usbProductId: number } - this.logger.webserial(`port selected, usbVendorId = 0x${WebserialAdapter.u16ToHex(info.usbVendorId)}, usbProductId = 0x${WebserialAdapter.u16ToHex(info.usbProductId)}`) - this.port.addEventListener?.('disconnect', () => { void ultra.disconnect(new Error('Webserial disconnect')) }) + this.logger.webserial(`port selected, usbVendorId = 0x${u16ToHex(info.usbVendorId)}, usbProductId = 0x${u16ToHex(info.usbProductId)}`) + this.port.addEventListener('disconnect', () => { void ultra.disconnect(new Error('Webserial disconnect')) }) ultra.port = _.merge(this.port, { - isOpen: () => { return this.isOpen }, + isOpen: () => this.isOpen, }) return await next() } catch (err) { diff --git a/tsup.config.ts b/tsup.config.ts index 6e10da3..e500192 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -4,10 +4,8 @@ import pkg from './package.json' assert { type: 'json' } const sharedConfig: Options = { cjsInterop: true, - clean: true, dts: true, env: { VERSION: pkg.version }, - external: ['module'], format: ['cjs', 'esm', 'iife'], keepNames: true, plugins: [], @@ -31,6 +29,7 @@ const sharedConfig: Options = { export default defineConfig((options): Options[] => [ { ...sharedConfig, + clean: !options.watch, // only clean once when not watching minify: !options.watch, globalName: 'ChameleonUltraJS', entry: ['src/index.ts'],