Next.js website for Rocky Mountain Vending company featuring: - Product catalog with Stripe integration - Service areas and parts pages - Admin dashboard with Clerk authentication - SEO optimized pages with JSON-LD structured data Co-authored-by: Cursor <cursoragent@cursor.com>
411 lines
No EOL
19 KiB
Text
411 lines
No EOL
19 KiB
Text
"use strict";
|
|
/**
|
|
* Copyright 2024 Google LLC.
|
|
* Copyright (c) Microsoft Corporation.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.BluetoothProcessor = void 0;
|
|
const protocol_js_1 = require("../../../protocol/protocol.js");
|
|
/** Represents a base Bluetooth GATT item. */
|
|
class BluetoothGattItem {
|
|
id;
|
|
uuid;
|
|
constructor(id, uuid) {
|
|
this.id = id;
|
|
this.uuid = uuid;
|
|
}
|
|
}
|
|
/** Represents a Bluetooth descriptor. */
|
|
class BluetoothDescriptor extends BluetoothGattItem {
|
|
characteristic;
|
|
constructor(id, uuid, characteristic) {
|
|
super(id, uuid);
|
|
this.characteristic = characteristic;
|
|
}
|
|
}
|
|
/** Represents a Bluetooth characteristic. */
|
|
class BluetoothCharacteristic extends BluetoothGattItem {
|
|
descriptors = new Map();
|
|
service;
|
|
constructor(id, uuid, service) {
|
|
super(id, uuid);
|
|
this.service = service;
|
|
}
|
|
}
|
|
/** Represents a Bluetooth service. */
|
|
class BluetoothService extends BluetoothGattItem {
|
|
characteristics = new Map();
|
|
device;
|
|
constructor(id, uuid, device) {
|
|
super(id, uuid);
|
|
this.device = device;
|
|
}
|
|
}
|
|
/** Represents a Bluetooth device. */
|
|
class BluetoothDevice {
|
|
address;
|
|
services = new Map();
|
|
constructor(address) {
|
|
this.address = address;
|
|
}
|
|
}
|
|
class BluetoothProcessor {
|
|
#eventManager;
|
|
#browsingContextStorage;
|
|
#bluetoothDevices = new Map();
|
|
// A map from a characteristic id from CDP to its BluetoothCharacteristic object.
|
|
#bluetoothCharacteristics = new Map();
|
|
// A map from a descriptor id from CDP to its BluetoothDescriptor object.
|
|
#bluetoothDescriptors = new Map();
|
|
constructor(eventManager, browsingContextStorage) {
|
|
this.#eventManager = eventManager;
|
|
this.#browsingContextStorage = browsingContextStorage;
|
|
}
|
|
#getDevice(address) {
|
|
const device = this.#bluetoothDevices.get(address);
|
|
if (!device) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Bluetooth device with address ${address} does not exist`);
|
|
}
|
|
return device;
|
|
}
|
|
#getService(device, serviceUuid) {
|
|
const service = device.services.get(serviceUuid);
|
|
if (!service) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Service with UUID ${serviceUuid} on device ${device.address} does not exist`);
|
|
}
|
|
return service;
|
|
}
|
|
#getCharacteristic(service, characteristicUuid) {
|
|
const characteristic = service.characteristics.get(characteristicUuid);
|
|
if (!characteristic) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Characteristic with UUID ${characteristicUuid} does not exist for service ${service.uuid} on device ${service.device.address}`);
|
|
}
|
|
return characteristic;
|
|
}
|
|
#getDescriptor(characteristic, descriptorUuid) {
|
|
const descriptor = characteristic.descriptors.get(descriptorUuid);
|
|
if (!descriptor) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Descriptor with UUID ${descriptorUuid} does not exist for characteristic ${characteristic.uuid} on service ${characteristic.service.uuid} on device ${characteristic.service.device.address}`);
|
|
}
|
|
return descriptor;
|
|
}
|
|
async simulateAdapter(params) {
|
|
if (params.state === undefined) {
|
|
// The bluetooth.simulateAdapter Command
|
|
// Step 4.2. If params["state"] does not exist, return error with error code invalid argument.
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-simulateAdapter-command
|
|
throw new protocol_js_1.InvalidArgumentException(`Parameter "state" is required for creating a Bluetooth adapter`);
|
|
}
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
// Bluetooth spec requires overriding the existing adapter (step 6). From the CDP
|
|
// perspective, we need to disable the emulation first.
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#bluetooth-simulateAdapter-command
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.disable');
|
|
this.#bluetoothDevices.clear();
|
|
this.#bluetoothCharacteristics.clear();
|
|
this.#bluetoothDescriptors.clear();
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.enable', {
|
|
state: params.state,
|
|
leSupported: params.leSupported ?? true,
|
|
});
|
|
return {};
|
|
}
|
|
async disableSimulation(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.disable');
|
|
this.#bluetoothDevices.clear();
|
|
this.#bluetoothCharacteristics.clear();
|
|
this.#bluetoothDescriptors.clear();
|
|
return {};
|
|
}
|
|
async simulatePreconnectedPeripheral(params) {
|
|
if (this.#bluetoothDevices.has(params.address)) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Bluetooth device with address ${params.address} already exists`);
|
|
}
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulatePreconnectedPeripheral', {
|
|
address: params.address,
|
|
name: params.name,
|
|
knownServiceUuids: params.knownServiceUuids,
|
|
manufacturerData: params.manufacturerData,
|
|
});
|
|
this.#bluetoothDevices.set(params.address, new BluetoothDevice(params.address));
|
|
return {};
|
|
}
|
|
async simulateAdvertisement(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulateAdvertisement', {
|
|
entry: params.scanEntry,
|
|
});
|
|
return {};
|
|
}
|
|
async simulateCharacteristic(params) {
|
|
const device = this.#getDevice(params.address);
|
|
const service = this.#getService(device, params.serviceUuid);
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
switch (params.type) {
|
|
case 'add': {
|
|
if (params.characteristicProperties === undefined) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Parameter "characteristicProperties" is required for adding a Bluetooth characteristic`);
|
|
}
|
|
if (service.characteristics.has(params.characteristicUuid)) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Characteristic with UUID ${params.characteristicUuid} already exists`);
|
|
}
|
|
const response = await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.addCharacteristic', {
|
|
serviceId: service.id,
|
|
characteristicUuid: params.characteristicUuid,
|
|
properties: params.characteristicProperties,
|
|
});
|
|
const characteristic = new BluetoothCharacteristic(response.characteristicId, params.characteristicUuid, service);
|
|
service.characteristics.set(params.characteristicUuid, characteristic);
|
|
this.#bluetoothCharacteristics.set(characteristic.id, characteristic);
|
|
return {};
|
|
}
|
|
case 'remove': {
|
|
if (params.characteristicProperties !== undefined) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Parameter "characteristicProperties" should not be provided for removing a Bluetooth characteristic`);
|
|
}
|
|
const characteristic = this.#getCharacteristic(service, params.characteristicUuid);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.removeCharacteristic', {
|
|
characteristicId: characteristic.id,
|
|
});
|
|
service.characteristics.delete(params.characteristicUuid);
|
|
this.#bluetoothCharacteristics.delete(characteristic.id);
|
|
return {};
|
|
}
|
|
default:
|
|
throw new protocol_js_1.InvalidArgumentException(`Parameter "type" of ${params.type} is not supported`);
|
|
}
|
|
}
|
|
async simulateCharacteristicResponse(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
const device = this.#getDevice(params.address);
|
|
const service = this.#getService(device, params.serviceUuid);
|
|
const characteristic = this.#getCharacteristic(service, params.characteristicUuid);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulateCharacteristicOperationResponse', {
|
|
characteristicId: characteristic.id,
|
|
type: params.type,
|
|
code: params.code,
|
|
...(params.data && {
|
|
data: btoa(String.fromCharCode(...params.data)),
|
|
}),
|
|
});
|
|
return {};
|
|
}
|
|
async simulateDescriptor(params) {
|
|
const device = this.#getDevice(params.address);
|
|
const service = this.#getService(device, params.serviceUuid);
|
|
const characteristic = this.#getCharacteristic(service, params.characteristicUuid);
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
switch (params.type) {
|
|
case 'add': {
|
|
if (characteristic.descriptors.has(params.descriptorUuid)) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Descriptor with UUID ${params.descriptorUuid} already exists`);
|
|
}
|
|
const response = await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.addDescriptor', {
|
|
characteristicId: characteristic.id,
|
|
descriptorUuid: params.descriptorUuid,
|
|
});
|
|
const descriptor = new BluetoothDescriptor(response.descriptorId, params.descriptorUuid, characteristic);
|
|
characteristic.descriptors.set(params.descriptorUuid, descriptor);
|
|
this.#bluetoothDescriptors.set(descriptor.id, descriptor);
|
|
return {};
|
|
}
|
|
case 'remove': {
|
|
const descriptor = this.#getDescriptor(characteristic, params.descriptorUuid);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.removeDescriptor', {
|
|
descriptorId: descriptor.id,
|
|
});
|
|
characteristic.descriptors.delete(params.descriptorUuid);
|
|
this.#bluetoothDescriptors.delete(descriptor.id);
|
|
return {};
|
|
}
|
|
default:
|
|
throw new protocol_js_1.InvalidArgumentException(`Parameter "type" of ${params.type} is not supported`);
|
|
}
|
|
}
|
|
async simulateDescriptorResponse(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
const device = this.#getDevice(params.address);
|
|
const service = this.#getService(device, params.serviceUuid);
|
|
const characteristic = this.#getCharacteristic(service, params.characteristicUuid);
|
|
const descriptor = this.#getDescriptor(characteristic, params.descriptorUuid);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulateDescriptorOperationResponse', {
|
|
descriptorId: descriptor.id,
|
|
type: params.type,
|
|
code: params.code,
|
|
...(params.data && {
|
|
data: btoa(String.fromCharCode(...params.data)),
|
|
}),
|
|
});
|
|
return {};
|
|
}
|
|
async simulateGattConnectionResponse(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulateGATTOperationResponse', {
|
|
address: params.address,
|
|
type: 'connection',
|
|
code: params.code,
|
|
});
|
|
return {};
|
|
}
|
|
async simulateGattDisconnection(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulateGATTDisconnection', {
|
|
address: params.address,
|
|
});
|
|
return {};
|
|
}
|
|
async simulateService(params) {
|
|
const device = this.#getDevice(params.address);
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
switch (params.type) {
|
|
case 'add': {
|
|
if (device.services.has(params.uuid)) {
|
|
throw new protocol_js_1.InvalidArgumentException(`Service with UUID ${params.uuid} already exists`);
|
|
}
|
|
const response = await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.addService', {
|
|
address: params.address,
|
|
serviceUuid: params.uuid,
|
|
});
|
|
device.services.set(params.uuid, new BluetoothService(response.serviceId, params.uuid, device));
|
|
return {};
|
|
}
|
|
case 'remove': {
|
|
const service = this.#getService(device, params.uuid);
|
|
await context.cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.removeService', {
|
|
serviceId: service.id,
|
|
});
|
|
device.services.delete(params.uuid);
|
|
return {};
|
|
}
|
|
default:
|
|
throw new protocol_js_1.InvalidArgumentException(`Parameter "type" of ${params.type} is not supported`);
|
|
}
|
|
}
|
|
onCdpTargetCreated(cdpTarget) {
|
|
cdpTarget.cdpClient.on('DeviceAccess.deviceRequestPrompted', (event) => {
|
|
this.#eventManager.registerEvent({
|
|
type: 'event',
|
|
method: 'bluetooth.requestDevicePromptUpdated',
|
|
params: {
|
|
context: cdpTarget.id,
|
|
prompt: event.id,
|
|
devices: event.devices,
|
|
},
|
|
}, cdpTarget.id);
|
|
});
|
|
cdpTarget.browserCdpClient.on('BluetoothEmulation.gattOperationReceived', async (event) => {
|
|
switch (event.type) {
|
|
case 'connection':
|
|
this.#eventManager.registerEvent({
|
|
type: 'event',
|
|
method: 'bluetooth.gattConnectionAttempted',
|
|
params: {
|
|
context: cdpTarget.id,
|
|
address: event.address,
|
|
},
|
|
}, cdpTarget.id);
|
|
return;
|
|
case 'discovery':
|
|
// Chromium Web Bluetooth simulation generates this GATT discovery event when
|
|
// a page attempts to get services for a given Bluetooth device for the first time.
|
|
// This 'get services' operation is put on hold until a GATT discovery response
|
|
// is sent to the simulation.
|
|
// Note: Web Bluetooth automation (see https://webbluetoothcg.github.io/web-bluetooth/#automated-testing)
|
|
// does not support simulating a GATT discovery response. This is because simulated services, characteristics,
|
|
// or descriptors are immediately visible to the simulation, meaning it doesn't have a distinct
|
|
// DISCOVERY state. Therefore, this code simulates a successful GATT discovery
|
|
// response upon receiving this event.
|
|
await cdpTarget.browserCdpClient.sendCommand('BluetoothEmulation.simulateGATTOperationResponse', {
|
|
address: event.address,
|
|
type: 'discovery',
|
|
code: 0x0,
|
|
});
|
|
}
|
|
});
|
|
cdpTarget.browserCdpClient.on('BluetoothEmulation.characteristicOperationReceived', (event) => {
|
|
if (!this.#bluetoothCharacteristics.has(event.characteristicId)) {
|
|
return;
|
|
}
|
|
let type;
|
|
if (event.type === 'write') {
|
|
// write-default-deprecated comes from
|
|
// https://webbluetoothcg.github.io/web-bluetooth/#dom-bluetoothremotegattcharacteristic-writevalue,
|
|
// which is deprecated so not supported.
|
|
if (event.writeType === 'write-default-deprecated') {
|
|
return;
|
|
}
|
|
type = event.writeType;
|
|
}
|
|
else {
|
|
type = event.type;
|
|
}
|
|
const characteristic = this.#bluetoothCharacteristics.get(event.characteristicId);
|
|
this.#eventManager.registerEvent({
|
|
type: 'event',
|
|
method: 'bluetooth.characteristicEventGenerated',
|
|
params: {
|
|
context: cdpTarget.id,
|
|
address: characteristic.service.device.address,
|
|
serviceUuid: characteristic.service.uuid,
|
|
characteristicUuid: characteristic.uuid,
|
|
type,
|
|
...(event.data && {
|
|
data: Array.from(atob(event.data), (c) => c.charCodeAt(0)),
|
|
}),
|
|
},
|
|
}, cdpTarget.id);
|
|
});
|
|
cdpTarget.browserCdpClient.on('BluetoothEmulation.descriptorOperationReceived', (event) => {
|
|
if (!this.#bluetoothDescriptors.has(event.descriptorId)) {
|
|
return;
|
|
}
|
|
const descriptor = this.#bluetoothDescriptors.get(event.descriptorId);
|
|
this.#eventManager.registerEvent({
|
|
type: 'event',
|
|
method: 'bluetooth.descriptorEventGenerated',
|
|
params: {
|
|
context: cdpTarget.id,
|
|
address: descriptor.characteristic.service.device.address,
|
|
serviceUuid: descriptor.characteristic.service.uuid,
|
|
characteristicUuid: descriptor.characteristic.uuid,
|
|
descriptorUuid: descriptor.uuid,
|
|
type: event.type,
|
|
...(event.data && {
|
|
data: Array.from(atob(event.data), (c) => c.charCodeAt(0)),
|
|
}),
|
|
},
|
|
}, cdpTarget.id);
|
|
});
|
|
}
|
|
async handleRequestDevicePrompt(params) {
|
|
const context = this.#browsingContextStorage.getContext(params.context);
|
|
if (params.accept) {
|
|
await context.cdpTarget.cdpClient.sendCommand('DeviceAccess.selectPrompt', {
|
|
id: params.prompt,
|
|
deviceId: params.device,
|
|
});
|
|
}
|
|
else {
|
|
await context.cdpTarget.cdpClient.sendCommand('DeviceAccess.cancelPrompt', {
|
|
id: params.prompt,
|
|
});
|
|
}
|
|
return {};
|
|
}
|
|
}
|
|
exports.BluetoothProcessor = BluetoothProcessor;
|
|
//# sourceMappingURL=BluetoothProcessor.js.map |