Version 0.0.1-1
Testing Polyfill NPM
My Polyfill Index page
Presently using this npm you might want to update the version
https://cdn.jsdelivr.net/npm/web-serial-polyfill@1.0.13/dist/serial.js
this page does nothing, just shows the NPM
'use strict';
Object.defineProperty(exports, "__esModule", { value: true });
exports.serial = exports.SerialPort = exports.SerialPolyfillProtocol = void 0;
var SerialPolyfillProtocol;
(function (SerialPolyfillProtocol) {
SerialPolyfillProtocol[SerialPolyfillProtocol["UsbCdcAcm"] = 0] = "UsbCdcAcm";
})(SerialPolyfillProtocol = exports.SerialPolyfillProtocol || (exports.SerialPolyfillProtocol = {}));
const kSetLineCoding = 0x20;
const kSetControlLineState = 0x22;
const kSendBreak = 0x23;
const kDefaultBufferSize = 255;
const kDefaultDataBits = 8;
const kDefaultParity = 'none';
const kDefaultStopBits = 1;
const kAcceptableDataBits = [16, 8, 7, 6, 5];
const kAcceptableStopBits = [1, 2];
const kAcceptableParity = ['none', 'even', 'odd'];
const kParityIndexMapping = ['none', 'odd', 'even'];
const kStopBitsIndexMapping = [1, 1.5, 2];
const kDefaultPolyfillOptions = {
protocol: SerialPolyfillProtocol.UsbCdcAcm,
usbControlInterfaceClass: 2,
usbTransferInterfaceClass: 10,
};
/**
* Utility function to get the interface implementing a desired class.
* @param {USBDevice} device The USB device.
* @param {number} classCode The desired interface class.
* @return {USBInterface} The first interface found that implements the desired
* class.
* @throws TypeError if no interface is found.
*/
function findInterface(device, classCode) {
const configuration = device.configurations[0];
for (const iface of configuration.interfaces) {
const alternate = iface.alternates[0];
if (alternate.interfaceClass === classCode) {
return iface;
}
}
throw new TypeError(`Unable to find interface with class ${classCode}.`);
}
/**
* Utility function to get an endpoint with a particular direction.
* @param {USBInterface} iface The interface to search.
* @param {USBDirection} direction The desired transfer direction.
* @return {USBEndpoint} The first endpoint with the desired transfer direction.
* @throws TypeError if no endpoint is found.
*/
function findEndpoint(iface, direction) {
const alternate = iface.alternates[0];
for (const endpoint of alternate.endpoints) {
if (endpoint.direction == direction) {
return endpoint;
}
}
throw new TypeError(`Interface ${iface.interfaceNumber} does not have an ` +
`${direction} endpoint.`);
}
/**
* Implementation of the underlying source API[1] which reads data from a USB
* endpoint. This can be used to construct a ReadableStream.
*
* [1]: https://streams.spec.whatwg.org/#underlying-source-api
*/
class UsbEndpointUnderlyingSource {
/**
* Constructs a new UnderlyingSource that will pull data from the specified
* endpoint on the given USB device.
*
* @param {USBDevice} device
* @param {USBEndpoint} endpoint
* @param {function} onError function to be called on error
*/
constructor(device, endpoint, onError) {
this.device_ = device;
this.endpoint_ = endpoint;
this.onError_ = onError;
}
/**
* Reads a chunk of data from the device.
*
* @param {ReadableStreamDefaultController} controller
*/
pull(controller) {
(async () => {
var _a;
let chunkSize;
if (controller.desiredSize) {
const d = controller.desiredSize / this.endpoint_.packetSize;
chunkSize = Math.ceil(d) * this.endpoint_.packetSize;
}
else {
chunkSize = this.endpoint_.packetSize;
}
try {
const result = await this.device_.transferIn(this.endpoint_.endpointNumber, chunkSize);
if (result.status != 'ok') {
controller.error(`USB error: ${result.status}`);
this.onError_();
}
if ((_a = result.data) === null || _a === void 0 ? void 0 : _a.buffer) {
const chunk = new Uint8Array(result.data.buffer, result.data.byteOffset, result.data.byteLength);
controller.enqueue(chunk);
}
}
catch (error) {
controller.error(error.toString());
this.onError_();
}
})();
}
}
/**
* Implementation of the underlying sink API[2] which writes data to a USB
* endpoint. This can be used to construct a WritableStream.
*
* [2]: https://streams.spec.whatwg.org/#underlying-sink-api
*/
class UsbEndpointUnderlyingSink {
/**
* Constructs a new UnderlyingSink that will write data to the specified
* endpoint on the given USB device.
*
* @param {USBDevice} device
* @param {USBEndpoint} endpoint
* @param {function} onError function to be called on error
*/
constructor(device, endpoint, onError) {
this.device_ = device;
this.endpoint_ = endpoint;
this.onError_ = onError;
}
/**
* Writes a chunk to the device.
*
* @param {Uint8Array} chunk
* @param {WritableStreamDefaultController} controller
*/
async write(chunk, controller) {
try {
const result = await this.device_.transferOut(this.endpoint_.endpointNumber, chunk);
if (result.status != 'ok') {
controller.error(result.status);
this.onError_();
}
}
catch (error) {
controller.error(error.toString());
this.onError_();
}
}
}
/** a class used to control serial devices over WebUSB */
class SerialPort {
/**
* constructor taking a WebUSB device that creates a SerialPort instance.
* @param {USBDevice} device A device acquired from the WebUSB API
* @param {SerialPolyfillOptions} polyfillOptions Optional options to
* configure the polyfill.
*/
constructor(device, polyfillOptions) {
this.polyfillOptions_ = Object.assign(Object.assign({}, kDefaultPolyfillOptions), polyfillOptions);
this.outputSignals_ = {
dataTerminalReady: false,
requestToSend: false,
break: false,
};
this.device_ = device;
this.controlInterface_ = findInterface(this.device_, this.polyfillOptions_.usbControlInterfaceClass);
this.transferInterface_ = findInterface(this.device_, this.polyfillOptions_.usbTransferInterfaceClass);
this.inEndpoint_ = findEndpoint(this.transferInterface_, 'in');
this.outEndpoint_ = findEndpoint(this.transferInterface_, 'out');
}
/**
* Getter for the readable attribute. Constructs a new ReadableStream as
* necessary.
* @return {ReadableStream} the current readable stream
*/
get readable() {
var _a;
if (!this.readable_ && this.device_.opened) {
this.readable_ = new ReadableStream(new UsbEndpointUnderlyingSource(this.device_, this.inEndpoint_, () => {
this.readable_ = null;
}), new ByteLengthQueuingStrategy({
highWaterMark: (_a = this.serialOptions_.bufferSize) !== null && _a !== void 0 ? _a : kDefaultBufferSize,
}));
}
return this.readable_;
}
/**
* Getter for the writable attribute. Constructs a new WritableStream as
* necessary.
* @return {WritableStream} the current writable stream
*/
get writable() {
var _a;
if (!this.writable_ && this.device_.opened) {
this.writable_ = new WritableStream(new UsbEndpointUnderlyingSink(this.device_, this.outEndpoint_, () => {
this.writable_ = null;
}), new ByteLengthQueuingStrategy({
highWaterMark: (_a = this.serialOptions_.bufferSize) !== null && _a !== void 0 ? _a : kDefaultBufferSize,
}));
}
return this.writable_;
}
/**
* a function that opens the device and claims all interfaces needed to
* control and communicate to and from the serial device
* @param {SerialOptions} options Object containing serial options
* @return {Promise} A promise that will resolve when device is ready
* for communication
*/
async open(options) {
this.serialOptions_ = options;
this.validateOptions();
try {
await this.device_.open();
if (this.device_.configuration === null) {
await this.device_.selectConfiguration(1);
}
await this.device_.claimInterface(this.controlInterface_.interfaceNumber);
if (this.controlInterface_ !== this.transferInterface_) {
await this.device_.claimInterface(this.transferInterface_.interfaceNumber);
}
await this.setLineCoding();
await this.setSignals({ dataTerminalReady: true });
}
catch (error) {
if (this.device_.opened) {
await this.device_.close();
}
throw new Error('Error setting up device: ' + error.toString());
}
}
/**
* Closes the port.
*
* @return {Promise} A promise that will resolve when the port is
* closed.
*/
async close() {
const promises = [];
if (this.readable_) {
promises.push(this.readable_.cancel());
}
if (this.writable_) {
promises.push(this.writable_.abort());
}
await Promise.all(promises);
this.readable_ = null;
this.writable_ = null;
if (this.device_.opened) {
await this.setSignals({ dataTerminalReady: false, requestToSend: false });
await this.device_.close();
}
}
/**
* A function that returns properties of the device.
* @return {SerialPortInfo} Device properties.
*/
getInfo() {
return {
usbVendorId: this.device_.vendorId,
usbProductId: this.device_.productId,
};
}
/**
* A function used to change the serial settings of the device
* @param {object} options the object which carries serial settings data
* @return {Promise} A promise that will resolve when the options are
* set
*/
reconfigure(options) {
this.serialOptions_ = Object.assign(Object.assign({}, this.serialOptions_), options);
this.validateOptions();
return this.setLineCoding();
}
/**
* Sets control signal state for the port.
* @param {SerialOutputSignals} signals The signals to enable or disable.
* @return {Promise} a promise that is resolved when the signal state
* has been changed.
*/
async setSignals(signals) {
this.outputSignals_ = Object.assign(Object.assign({}, this.outputSignals_), signals);
if (signals.dataTerminalReady !== undefined ||
signals.requestToSend !== undefined) {
// The Set_Control_Line_State command expects a bitmap containing the
// values of all output signals that should be enabled or disabled.
//
// Ref: USB CDC specification version 1.1 §6.2.14.
const value = (this.outputSignals_.dataTerminalReady ? 1 << 0 : 0) |
(this.outputSignals_.requestToSend ? 1 << 1 : 0);
await this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': kSetControlLineState,
'value': value,
'index': this.controlInterface_.interfaceNumber,
});
}
if (signals.break !== undefined) {
// The SendBreak command expects to be given a duration for how long the
// break signal should be asserted. Passing 0xFFFF enables the signal
// until 0x0000 is send.
//
// Ref: USB CDC specification version 1.1 §6.2.15.
const value = this.outputSignals_.break ? 0xFFFF : 0x0000;
await this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': kSendBreak,
'value': value,
'index': this.controlInterface_.interfaceNumber,
});
}
}
/**
* Checks the serial options for validity and throws an error if it is
* not valid
*/
validateOptions() {
if (!this.isValidBaudRate(this.serialOptions_.baudRate)) {
throw new RangeError('invalid Baud Rate ' + this.serialOptions_.baudRate);
}
if (!this.isValidDataBits(this.serialOptions_.dataBits)) {
throw new RangeError('invalid dataBits ' + this.serialOptions_.dataBits);
}
if (!this.isValidStopBits(this.serialOptions_.stopBits)) {
throw new RangeError('invalid stopBits ' + this.serialOptions_.stopBits);
}
if (!this.isValidParity(this.serialOptions_.parity)) {
throw new RangeError('invalid parity ' + this.serialOptions_.parity);
}
}
/**
* Checks the baud rate for validity
* @param {number} baudRate the baud rate to check
* @return {boolean} A boolean that reflects whether the baud rate is valid
*/
isValidBaudRate(baudRate) {
return baudRate % 1 === 0;
}
/**
* Checks the data bits for validity
* @param {number} dataBits the data bits to check
* @return {boolean} A boolean that reflects whether the data bits setting is
* valid
*/
isValidDataBits(dataBits) {
if (typeof dataBits === 'undefined') {
return true;
}
return kAcceptableDataBits.includes(dataBits);
}
/**
* Checks the stop bits for validity
* @param {number} stopBits the stop bits to check
* @return {boolean} A boolean that reflects whether the stop bits setting is
* valid
*/
isValidStopBits(stopBits) {
if (typeof stopBits === 'undefined') {
return true;
}
return kAcceptableStopBits.includes(stopBits);
}
/**
* Checks the parity for validity
* @param {string} parity the parity to check
* @return {boolean} A boolean that reflects whether the parity is valid
*/
isValidParity(parity) {
if (typeof parity === 'undefined') {
return true;
}
return kAcceptableParity.includes(parity);
}
/**
* sends the options alog the control interface to set them on the device
* @return {Promise} a promise that will resolve when the options are set
*/
async setLineCoding() {
var _a, _b, _c;
// Ref: USB CDC specification version 1.1 §6.2.12.
const buffer = new ArrayBuffer(7);
const view = new DataView(buffer);
view.setUint32(0, this.serialOptions_.baudRate, true);
view.setUint8(4, kStopBitsIndexMapping.indexOf((_a = this.serialOptions_.stopBits) !== null && _a !== void 0 ? _a : kDefaultStopBits));
view.setUint8(5, kParityIndexMapping.indexOf((_b = this.serialOptions_.parity) !== null && _b !== void 0 ? _b : kDefaultParity));
view.setUint8(6, (_c = this.serialOptions_.dataBits) !== null && _c !== void 0 ? _c : kDefaultDataBits);
const result = await this.device_.controlTransferOut({
'requestType': 'class',
'recipient': 'interface',
'request': kSetLineCoding,
'value': 0x00,
'index': this.controlInterface_.interfaceNumber,
}, buffer);
if (result.status != 'ok') {
throw new DOMException('NetworkError', 'Failed to set line coding.');
}
}
}
exports.SerialPort = SerialPort;
/** implementation of the global navigator.serial object */
class Serial {
/**
* Requests permission to access a new port.
*
* @param {SerialPortRequestOptions} options
* @param {SerialPolyfillOptions} polyfillOptions
* @return {Promise}
*/
async requestPort(options, polyfillOptions) {
polyfillOptions = Object.assign(Object.assign({}, kDefaultPolyfillOptions), polyfillOptions);
const usbFilters = [];
if (options && options.filters) {
for (const filter of options.filters) {
const usbFilter = {
classCode: polyfillOptions.usbControlInterfaceClass,
};
if (filter.usbVendorId !== undefined) {
usbFilter.vendorId = filter.usbVendorId;
}
if (filter.usbProductId !== undefined) {
usbFilter.productId = filter.usbProductId;
}
usbFilters.push(usbFilter);
}
}
if (usbFilters.length === 0) {
usbFilters.push({
classCode: polyfillOptions.usbControlInterfaceClass,
});
}
const device = await navigator.usb.requestDevice({ 'filters': usbFilters });
const port = new SerialPort(device, polyfillOptions);
return port;
}
/**
* Get the set of currently available ports.
*
* @param {SerialPolyfillOptions} polyfillOptions Polyfill configuration that
* should be applied to these ports.
* @return {Promise} a promise that is resolved with a list of
* ports.
*/
async getPorts(polyfillOptions) {
polyfillOptions = Object.assign(Object.assign({}, kDefaultPolyfillOptions), polyfillOptions);
const devices = await navigator.usb.getDevices();
const ports = [];
devices.forEach((device) => {
try {
const port = new SerialPort(device, polyfillOptions);
ports.push(port);
}
catch (e) {
// Skip unrecognized port.
}
});
return ports;
}
}
/* an object to be used for starting the serial workflow */
exports.serial = new Serial();
//# sourceMappingURL=serial.js.map
Main Github web-serial-polyfill
My Fork of the above github my WebSerial Polyfill