{"_id":"@danidoble/webserial-jsd","name":"@danidoble/webserial-jsd","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@danidoble/webserial-jsd","type":"module","version":"1.0.0","description":"A strongly-typed, event-driven JSD Jofemar vending machine serial device driver built on top of webserial-core.","author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"license":"GPL-3.0-only","homepage":"https://github.com/danidoble/webserial-jsd#readme","repository":{"type":"git","url":"git+https://github.com/danidoble/webserial-jsd.git"},"bugs":{"url":"https://github.com/danidoble/webserial-jsd/issues"},"exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./package.json":"./package.json"},"devDependencies":{"@eslint/js":"^10.0.1","@types/node":"^25.6.0","@typescript/native-preview":"7.0.0-dev.20260328.1","bumpp":"^11.0.1","eslint":"^10.2.1","globals":"^17.5.0","prettier":"3.8.3","tsdown":"^0.21.10","typescript":"^6.0.3","typescript-eslint":"^8.59.1","vitest":"^4.1.5","webserial-core":"^2.1.0"},"peerDependencies":{"webserial-core":"^2.1.0"},"main":"./dist/index.cjs","module":"./dist/index.mjs","types":"./dist/index.d.cts","scripts":{"build":"tsdown","dev":"prettier --write ./src/**/*.ts && tsdown --watch","test":"vitest","typecheck":"tsc --noEmit","release":"bumpp","lint":"eslint ./src/**/*.ts ./tests/**/*.ts","format":"prettier --write ./src/ ./tests/ ./README.md ./package.json"},"_id":"@danidoble/webserial-jsd@1.0.0","_integrity":"sha512-6cEUsY7XkuZ4spHqRzsdem+Wou/+s7jYKYumUWElXkTLVl8D4DOeqdQf+YENR3M8btSHiXgVDVTi/ENwa4vwzA==","_resolved":"/tmp/7a051aef9772f62b57b82fe5a23ac344/danidoble-webserial-jsd-1.0.0.tgz","_from":"file:danidoble-webserial-jsd-1.0.0.tgz","_nodeVersion":"24.12.0","_npmVersion":"11.13.0","dist":{"integrity":"sha512-6cEUsY7XkuZ4spHqRzsdem+Wou/+s7jYKYumUWElXkTLVl8D4DOeqdQf+YENR3M8btSHiXgVDVTi/ENwa4vwzA==","shasum":"ab5a43e345170ce97a48a74bfb2a1a69f283576f","tarball":"https://registry.npmjs.org/@danidoble/webserial-jsd/-/webserial-jsd-1.0.0.tgz","fileCount":7,"unpackedSize":221362,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAxGfHxsvq80zDo85lbA0/EX2ILi/CyQzor8eFA5M4UAAiEAhfp+ftmG0NhYTW9tDvQQ+5cmLFaom3eXtZ+k874ET4c="}]},"_npmUser":{"name":"danidoble","email":"ddanidoble@gmail.com"},"directories":{},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webserial-jsd_1.0.0_1777315658161_0.5946856149055799"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-27T18:47:38.049Z","1.0.0":"2026-04-27T18:47:38.309Z","modified":"2026-04-27T18:47:38.518Z"},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"description":"A strongly-typed, event-driven JSD Jofemar vending machine serial device driver built on top of webserial-core.","homepage":"https://github.com/danidoble/webserial-jsd#readme","repository":{"type":"git","url":"git+https://github.com/danidoble/webserial-jsd.git"},"author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"bugs":{"url":"https://github.com/danidoble/webserial-jsd/issues"},"license":"GPL-3.0-only","readme":"# @danidoble/webserial-jsd\n\nTypeScript driver for Jofemar JSD vending machines over serial port, built on top of [`webserial-core`](https://github.com/danidoble/webserial-core) v2.\n\nHandles serial connection, binary handshake, automatic reconnection and message routing — exposing only clean, typed events.\n\nNot tied to a single transport: use the **WebUSB**, **Web Bluetooth** or **WebSocket** provider from `webserial-core`, or implement your own `SerialProvider`.\n\n[![npm version](https://img.shields.io/npm/v/@danidoble/webserial-jsd)](https://www.npmjs.com/package/@danidoble/webserial-jsd)\n[![license](https://img.shields.io/npm/l/@danidoble/webserial-jsd)](./LICENSE.md)\n\n---\n\n## Requirements\n\n- [`webserial-core`](https://www.npmjs.com/package/webserial-core) `^2.1.0` (peer dependency)\n- A compatible transport (see [Providers](#providers)):\n  - **Web Serial API** — Chrome / Edge 89+ (default, no extra setup)\n  - **WebUSB** — Chrome / Edge (via `WebUsbProvider`)\n  - **Web Bluetooth** — Chrome / Edge (via `createBluetoothProvider`, Nordic UART Service)\n  - **WebSocket** — any environment (via `createWebSocketProvider` + a bridge server)\n  - **Custom** — any platform via your own `SerialProvider` implementation\n\n---\n\n## Installation\n\n```bash\n# npm\nnpm install @danidoble/webserial-jsd webserial-core\n\n# pnpm\npnpm add @danidoble/webserial-jsd webserial-core\n\n# yarn\nyarn add @danidoble/webserial-jsd webserial-core\n\n# bun\nbun add @danidoble/webserial-jsd webserial-core\n```\n\n> `webserial-core` is a **peer dependency** — it must be installed alongside this package.\n\n---\n\n## Quick start\n\n```ts\nimport { JSD } from '@danidoble/webserial-jsd';\n\nconst jsd = new JSD({ transport: 'rs232' });\n\njsd.on('vision:machine-status', ({ machine, status }) => {\n  console.log(`Machine ${machine}: ${status}`);\n});\n\nawait jsd.connect();\n```\n\n---\n\n## Serial settings\n\nThe constructor pre-configures the following defaults — no extra setup needed:\n\n| Setting            | Value                    |\n| ------------------ | ------------------------ |\n| Baud rate          | 9600                     |\n| Data bits          | 8                        |\n| Stop bits          | 1                        |\n| Parity             | none                     |\n| Flow control       | none                     |\n| Buffer size        | 255 B                    |\n| Parser             | interByteTimeout (40 ms) |\n| Command timeout    | 1 000 ms                 |\n| Auto-reconnect     | ✓                        |\n| Reconnect interval | 1 500 ms                 |\n| Handshake timeout  | 2 000 ms                 |\n\n## Providers\n\nBy default the library uses the browser's native **Web Serial API** (`navigator.serial`). You can replace this with any of the built-in providers from `webserial-core`, or write your own.\n\n### Web Serial API (default)\n\nNo setup required — works out of the box in Chrome / Edge 89+.\n\n```ts\nimport { jsd } from '@danidoble/webserial-jsd';\n\nconst jsd = new jsd({ filters: [{ usbVendorId: 0x2341 }] });\nawait jsd.connect();\n```\n\n### WebUSB (`WebUsbProvider`)\n\nUse the **WebUSB API** as the transport. Useful for devices or platforms where the native Web Serial API is unavailable, or when targeting CP210x / vendor-specific USB chips.\n\n```ts\nimport { jsd, WebUsbProvider } from '@danidoble/webserial-jsd';\n\nconst jsd = new jsd({\n  filters: [{ usbVendorId: 0x2341 }],\n  provider: new WebUsbProvider()\n});\n\nawait jsd.connect();\n```\n\n### Web Bluetooth (`createBluetoothProvider`)\n\nCommunicate over **Bluetooth Low Energy** using the Nordic UART Service (NUS). The device must expose NUS characteristics.\n\n```ts\nimport { jsd, createBluetoothProvider } from '@danidoble/webserial-jsd';\n\nconst jsd = new jsd({\n  provider: createBluetoothProvider()\n});\n\nawait jsd.connect(); // shows the browser Bluetooth picker\n```\n\n### WebSocket (`createWebSocketProvider`)\n\nRoute serial communication through a **WebSocket bridge server** — ideal for Node.js environments or remote devices. A reference bridge implementation is available in the [`webserial-core` demos](https://github.com/danidoble/webserial-core).\n\n```ts\nimport { jsd, createWebSocketProvider } from '@danidoble/webserial-jsd';\n\nconst jsd = new jsd({\n  filters: [{ usbVendorId: 0x2341 }],\n  provider: createWebSocketProvider('ws://localhost:8080')\n});\n\nawait jsd.connect();\n```\n\n### Global provider (`AbstractSerialDevice.setProvider`)\n\nSet a provider once for **all** device instances instead of per-instance. Import `AbstractSerialDevice` directly from `webserial-core`:\n\n```ts\nimport { AbstractSerialDevice, WebUsbProvider } from 'webserial-core';\nimport { jsd } from '@danidoble/webserial-jsd';\n\nAbstractSerialDevice.setProvider(new WebUsbProvider());\n\nconst jsd = new jsd({ filters: [{ usbVendorId: 0x2341 }] });\nawait jsd.connect();\n```\n\n### Custom provider\n\nImplement the `SerialProvider` interface to target any platform:\n\n```ts\nimport type { SerialProvider, SerialPortFilter } from '@danidoble/webserial-jsd';\nimport { jsd } from '@danidoble/webserial-jsd';\n\nconst myProvider: SerialProvider = {\n  async requestPort(options?: { filters?: SerialPortFilter[] }): Promise<SerialPort> {\n    // return a SerialPort-compatible object\n  },\n  async getPorts(): Promise<SerialPort[]> {\n    // return previously authorised ports\n  }\n};\n\nconst jsd = new jsd({\n  filters: [{ usbVendorId: 0x2341 }],\n  provider: myProvider\n});\n```\n\n---\n\n## Constructor\n\n```ts\nnew JSD(options?: JSDOptions)\n```\n\n### `JSDOptions`\n\n| Property          | Type                              | Default   | Description                                             |\n| ----------------- | --------------------------------- | --------- | ------------------------------------------------------- |\n| `filters`         | `SerialPortFilter[]`              | `[]`      | Serial port filters to narrow the port picker.          |\n| `transport`       | `'rs232' \\| 'tcpip'`              | `'rs232'` | Transport strategy.                                     |\n| `provider`        | `SerialProvider`                  | —         | Alternative provider (WebUSB, WebSocket, Bluetooth).    |\n| `polyfillOptions` | `SerialDeviceOptions<Uint8Array>` | —         | Extra options that override the internal configuration. |\n\n---\n\n## Connection API\n\nInherited from `AbstractSerialDevice` in `webserial-core`.\n\n```ts\nawait jsd.connect(); // Opens the port and runs the handshake.\nawait jsd.disconnect(); // Closes the port.\nawait jsd.forget(); // Releases the port permission.\njsd.isConnected(); // boolean — true if the port is open.\njsd.isDisconnected(); // boolean — true if the port is closed.\n```\n\n---\n\n## Sub-modules\n\n### `jsd.vision`\n\nVision machine management. Handles statuses, dispensing, channels and configuration.\n\n#### Machine status\n\n```ts\njsd.vision.getMachineStatus(machine: 1|2|3|4): MachineStatus | null\n```\n\nReturns the last known status of the machine.\n\n```ts\njsd.vision.isAvailableToDispense({ machine?: 1|2|3|4 }): boolean\n```\n\n`true` if the machine is connected, in service, door closed and available to dispense.\n\n```ts\nawait jsd.vision.requestMachineStatus({ machine?: 1|2|3|4 }): Promise<void>\n```\n\nRequests the current machine status. Emits `vision:machine-status`.\n\n#### Channels and selections\n\n```ts\nawait jsd.vision.requestStatusChannel({ machine, selection }): Promise<void>\n```\n\nRequests the status of a channel (selection 1-80 is mapped to tray/channel internally). Emits `vision:channel-status`.\n\n```ts\nawait jsd.vision.assignChannels({ machine }): Promise<SelectionStatus[]>\n```\n\nRequests the status of all 80 channels of a machine sequentially. Returns an array with each selection's status when complete. **Blocks concurrent re-assignment.**\n\n```ts\nawait jsd.vision.requestStatusSelection({ selection }): Promise<void>\n```\n\nRequests the status of a selection (1-240). Emits `vision:status-selection`.\n\n```ts\nawait jsd.vision.requestChannelsLinkedToSelection({ selection }): Promise<void>\n```\n\nRequests the channels linked to a selection. Emits `vision:channels-linked-to-selection`.\n\n#### Dispensing\n\n```ts\nawait jsd.vision.dispense({ machine, selection, speed?, timePostRun?, cart? }): Promise<boolean>\n```\n\nDispenses from a selection (1-81). `cart` groups the result with other cart commands. Resolves `true` on success, `false` on failure.\n\n```ts\nawait jsd.vision.dispenseCart(data: { machine, selection }[]): Promise<{ selection, dispensed }[]>\n```\n\nDispenses multiple selections in batches of 10. Waits for machines to become available between batches. Emits `vision:waiting-collection` if a machine is waiting for collection.\n\n```ts\nawait jsd.vision.dispenseFromChannel({ machine, tray, channel, speed, timePostRun, cart? }): Promise<boolean>\n```\n\nDispenses directly by tray and channel. Emits `vision:dispense-status`.\n\n```ts\nawait jsd.vision.dispenseFromChannelExtended({\n  machine, tray, channel, speed, timePostRun,\n  fragileOrHeavy, typeAdjustElevator, timeAdjustElevator, cart?\n}): Promise<boolean>\n```\n\nExtended version with elevator adjustment and fragile/heavy product options. Emits `vision:extended-dispense-status-data`.\n\n```ts\nawait jsd.vision.dispenseFromSelection({ selection, cart? }): Promise<boolean>\n```\n\nDispenses directly by JSD system selection number. Emits `vision:dispense-status`.\n\n```ts\nawait jsd.vision.requestDispenseStatusFromChannel({ token }): Promise<void>\nawait jsd.vision.requestDispenseStatusFromSelection({ token }): Promise<void>\nawait jsd.vision.requestDispenseStatusFromChannelExtended({ token }): Promise<void>\n```\n\nQuery the status of an ongoing dispense by token.\n\n#### Selection configuration\n\n```ts\nawait jsd.vision.configureSelectionDispense({ selection, speed, timePostRun }): Promise<void>\n```\n\nConfigures the dispense parameters for a selection. Emits `vision:selection-dispense-config`.\n\n```ts\nawait jsd.vision.requestSelectionDispenseConfig({ machine }): Promise<void>\n```\n\nRequests the current dispense configuration of a machine's selections. Emits `vision:selection-dispense-config`.\n\n```ts\nawait jsd.vision.configureAddChannelToSelection({ selection, machine, tray, channel }): Promise<void>\n```\n\nAdds a channel to a selection.\n\n#### Identification and version\n\n```ts\nawait jsd.vision.requestMachineIdentification({ machine }): Promise<void>\n```\n\nRequests the machine identifier. Emits `vision:machine-id`.\n\n```ts\nawait jsd.vision.requestJSDVersion(): Promise<void>\n```\n\nRequests the JSD firmware version. Emits `vision:jsd-version`.\n\n#### Temperature\n\n```ts\nawait jsd.vision.configureWorkingTemperature({ machine, temperature, enable }): Promise<void>\nawait jsd.vision.requestWorkingTemperature({ machine }): Promise<void>\n```\n\nConfigures or requests the working temperature. Emits `vision:current-temperature`.\n\n#### Timings\n\n```ts\nawait jsd.vision.configureWaitingTimings({ collectPosition, dispenseManoeuvres, afterPickup }): Promise<void>\nawait jsd.vision.requestWaitingTimings(): Promise<void>\n```\n\nConfigures or requests wait timings. Emits `vision:time-waiting-for-product-collection`.\n\n```ts\nawait jsd.vision.configureTimeWaitingAfterPickup({ time }): Promise<void>\nawait jsd.vision.requestTimeWaitingAfterPickup(): Promise<void>\n```\n\nConfigures or requests the post-pickup wait time. Emits `vision:time-waiting-after-product-collection`.\n\n#### Lights\n\n```ts\nawait jsd.vision.lightsOn({ machine }): Promise<void>\nawait jsd.vision.lightsOff({ machine }): Promise<void>\n```\n\n#### Faults and reset\n\n```ts\nawait jsd.vision.requestReportActiveFaults({ machine }): Promise<void>\n```\n\nRequests the active faults report. Emits `vision:active-faults`.\n\n```ts\nawait jsd.vision.requestReportInactiveFaults({ machine }): Promise<void>\n```\n\nRequests the inactive faults report. Emits `vision:alarm-faults-events`.\n\n```ts\nawait jsd.vision.clearInactiveFaults({ machine }): Promise<void>\nawait jsd.vision.resetSoldOutChannels({ machine }): Promise<void>\nawait jsd.vision.resetFaultsAndSelfTest({ machine }): Promise<void>\n```\n\nClears inactive faults, resets sold-out channels or runs a self-test.\n\n```ts\nawait jsd.vision.resetAllErrors({ machine }): Promise<void[]>\n```\n\nRuns `clearInactiveFaults`, `resetSoldOutChannels` and `resetFaultsAndSelfTest` in parallel.\n\n```ts\nawait jsd.vision.restartJSD(): Promise<void>\n```\n\n⚠️ **DANGEROUS** — Fully restarts the JSD device. Emits `vision:jsd-status-reset`.\n\n#### Collection\n\n```ts\nawait jsd.vision.collect({ machine }): Promise<void>\n```\n\nStarts the product collection cycle. Emits `vision:collect`.\n\n---\n\n### `jsd.manifest`\n\nDevice manifest and log management.\n\n```ts\nawait jsd.manifest.requestLogsEvent({ previous: boolean }): Promise<void>\n```\n\nRequests log events. `previous: true` retrieves earlier logs.\n\n```ts\nawait jsd.manifest.requestLogs(): Promise<string[]>\n```\n\nRequests and returns all logs as a string array. Handles batches automatically. Throws if a request is already in progress.\n\n```ts\nawait jsd.manifest.requestLogsByDate({ since: Date, until: Date }): Promise<void>\n```\n\nRequests logs in a date range. Emits `manifest:log`.\n\n```ts\nawait jsd.manifest.requestForSendingManifest({ fileSizeBytes: number, crc: string }): Promise<void>\n```\n\nPrepares the device to receive a manifest file.\n\n```ts\nawait jsd.manifest.sendManifestDataBlock({ prevBlockId: number, dataBlock: Uint8Array }): Promise<void>\n```\n\nSends a manifest data block. Emits `manifest:block` and `manifest:completed` when finished.\n\n---\n\n### `jsd.licensing`\n\nDevice license management.\n\n```ts\nawait jsd.licensing.requestFeatureStatus({ feature: number }): Promise<void>\n```\n\nQueries the status of a licensed feature. Emits `licensing:feature-status`.\n\n```ts\nawait jsd.licensing.requestTemporaryLicenseStatus(): Promise<void>\n```\n\nQueries the temporary license status. Emits `licensing:temporary-license-status`.\n\n```ts\nawait jsd.licensing.requestSeedData(): Promise<void>\n```\n\nRequests the seed data needed to generate a license. Emits `licensing:seed`.\n\n```ts\nawait jsd.licensing.requestLicenseActivation({ license: string }): Promise<void>\n```\n\nActivates a license by passing the base64 string. Emits `licensing:feature-status`.\n\n---\n\n## Additional getter\n\n```ts\njsd.transport: string  // 'rs232' | 'tcpip'\n```\n\n---\n\n## Events\n\nAll events are listened to with `jsd.on(event, handler)`.\n\n### Connection events (inherited from webserial-core)\n\n| Event                    | Payload                | When                              |\n| ------------------------ | ---------------------- | --------------------------------- |\n| `serial:connecting`      | `instance`             | `connect()` started               |\n| `serial:connected`       | `instance`             | Port open and handshake completed |\n| `serial:disconnected`    | `instance`             | Port closed                       |\n| `serial:reconnecting`    | `instance`             | Automatic reconnect cycle started |\n| `serial:data`            | `Uint8Array, instance` | Frame received from device        |\n| `serial:sent`            | `Uint8Array, instance` | Bytes sent to device              |\n| `serial:error`           | `Error, instance`      | Read/write error                  |\n| `serial:need-permission` | `instance`             | User cancelled the port picker    |\n| `serial:queue-empty`     | `instance`             | Command queue empty               |\n| `serial:timeout`         | `Uint8Array, instance` | Command timeout                   |\n\n### JSD protocol events\n\n| Event              | Payload                | Description                    |\n| ------------------ | ---------------------- | ------------------------------ |\n| `serial:jsd-data`  | `VisionResponse`       | Parsed frame received          |\n| `serial:jsd-ack`   | `{ packetId: number }` | ACK received                   |\n| `serial:jsd-nack`  | `{ packetId: number }` | NACK received                  |\n| `serial:jsd-error` | `{ message: string }`  | Protocol error                 |\n| `serial:message`   | `{ message: string }`  | Informational protocol message |\n\n### Vision events\n\n| Event                                              | Payload                                    | Description                         |\n| -------------------------------------------------- | ------------------------------------------ | ----------------------------------- |\n| `vision:machine-status`                            | `MachineStatus & { machine }`              | Machine status updated              |\n| `vision:channels`                                  | `{ machine, channels: SelectionStatus[] }` | Status of all channels              |\n| `vision:channels-progress`                         | `{ machine, progress: number }`            | Channel assignment progress (0-100) |\n| `vision:channel-status`                            | `SelectionStatus & { machine }`            | Status of an individual channel     |\n| `vision:status-selection`                          | `{ selection, status }`                    | Status of a selection               |\n| `vision:dispense-status`                           | `DispenseToken & { dispensed: boolean }`   | Dispense result                     |\n| `vision:selection-dispense-config`                 | `{ machine, config }`                      | Selection dispense configuration    |\n| `vision:channels-linked-to-selection`              | `StatusChannelsLinkedToSelection`          | Channels linked to a selection      |\n| `vision:machine-id`                                | `{ machine, id: string }`                  | Machine identifier                  |\n| `vision:current-temperature`                       | `{ machine, temperature: string }`         | Current temperature                 |\n| `vision:alarm-faults-events`                       | `{ machine, faults }`                      | Alarms and faults (inactive)        |\n| `vision:active-faults`                             | `{ machine, faults }`                      | Active faults                       |\n| `vision:active-faults-list`                        | `{ machine, faults }`                      | Active faults list                  |\n| `vision:time-waiting-for-product-collection`       | `{ collectPosition, dispenseManoeuvres }`  | Wait timings                        |\n| `vision:time-waiting-after-product-collection`     | `{ time }`                                 | Post-pickup wait time               |\n| `vision:collect`                                   | `{ machine }`                              | Collection cycle completed          |\n| `vision:reset-sold-out-channels`                   | `{ machine }`                              | Sold-out channels reset             |\n| `vision:jsd-version`                               | `{ version: string }`                      | Firmware version                    |\n| `vision:jsd-dispensing-queue`                      | `{ queue }`                                | JSD dispense queue                  |\n| `vision:special-characteristics-selection`         | `{ selection, characteristics }`           | Special selection characteristics   |\n| `vision:perishable-products-config`                | `{ config }`                               | Perishable products configuration   |\n| `vision:jsd-status-reset`                          | `JsdResetStatus`                           | JSD reset status                    |\n| `vision:extended-dispense-status-data`             | `{ token, data }`                          | Extended dispense data              |\n| `vision:trays-positioning-phototransistors-status` | `{ machine, status }`                      | Phototransistors status             |\n| `vision:jsd-license-error`                         | `{ error }`                                | JSD license error                   |\n| `vision:dispensing`                                | `{ machine, token }`                       | Dispense in progress                |\n| `vision:waiting-collection`                        | `{ machine }`                              | Machine waiting for collection      |\n| `vision:door`                                      | `{ machine, open: boolean }`               | Door status                         |\n| `vision:connection`                                | `{ machine, connected: boolean }`          | Connection with machine             |\n| `vision:wrong-cmd`                                 | `{ opcode, params }`                       | Wrong command received              |\n\n### Manifest events\n\n| Event                | Payload               | Description                  |\n| -------------------- | --------------------- | ---------------------------- |\n| `manifest:log`       | `{ log: string }`     | Log entry received           |\n| `manifest:block`     | `{ blockId: number }` | Manifest block sent/received |\n| `manifest:completed` | `{ logs: string[] }`  | Manifest transfer completed  |\n| `manifest:wrong-cmd` | `{ opcode, params }`  | Wrong command received       |\n\n### Licensing events\n\n| Event                                | Payload                                | Description                     |\n| ------------------------------------ | -------------------------------------- | ------------------------------- |\n| `licensing:feature-status`           | `{ feature: number, active: boolean }` | Licensed feature status         |\n| `licensing:seed`                     | `{ seed: string }`                     | Seed data to generate a license |\n| `licensing:temporary-license-status` | `{ active: boolean, remaining }`       | Temporary license status        |\n| `licensing:wrong-cmd`                | `{ opcode, params }`                   | Wrong command received          |\n\n---\n\n## Full example\n\n```ts\nimport { JSD } from '@danidoble/webserial-jsd';\n\nconst jsd = new JSD({\n  transport: 'rs232',\n  filters: [{ usbVendorId: 0x0403 }] // FTDI\n});\n\njsd.on('vision:machine-status', ({ machine, status, availabilityToDispense }) => {\n  console.log(`Machine ${machine}: ${status} / ${availabilityToDispense}`);\n});\n\njsd.on('vision:dispense-status', ({ dispensed }) => {\n  console.log(dispensed ? 'Dispensed OK' : 'Dispense failed');\n});\n\njsd.on('serial:error', err => console.error(err));\n\nawait jsd.connect();\n\nif (jsd.vision.isAvailableToDispense({ machine: 1 })) {\n  const ok = await jsd.vision.dispense({ machine: 1, selection: 5 });\n  console.log('Result:', ok);\n}\n```\n\n---\n\n## TypeScript\n\nAll events and method signatures are fully typed. The package ships with `.d.mts` / `.d.cts` declaration files — no extra `@types` package required.\n\nCommonly used types and all built-in providers are re-exported so you do not need to import directly from `webserial-core`:\n\n```ts\n// Main class\nimport { JSD, WebUsbProvider, createBluetoothProvider, createWebSocketProvider } from '@danidoble/webserial-jsd';\n\nexport type {\n  JSDOptions,\n  SerialPortFilter,\n  SerialDeviceOptions,\n  SerialEventMap,\n  SerialParser,\n  SerialProvider,\n  SerialPolyfillOptions\n} from '@danidoble/webserial-jsd';\n```\n\n---\n\n## License\n\n[GPL-3.0-only](./LICENSE.md) © [Danidoble](https://github.com/danidoble)\n","readmeFilename":"README.md","_rev":"1-541ed609a40c2e92a56e09eb9d04b0f1"}