{"_id":"@danidoble/webserial-pax","_rev":"2-adedf5e29767795170c21f671b32d45a","name":"@danidoble/webserial-pax","dist-tags":{"latest":"1.0.1"},"versions":{"1.0.0":{"name":"@danidoble/webserial-pax","version":"1.0.0","author":{"name":"Danidoble","email":"ddanidoble@gmail.com"},"license":"GPL-3.0-only","_id":"@danidoble/webserial-pax@1.0.0","maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"homepage":"https://github.com/danidoble/webserial-pax#readme","bugs":{"url":"https://github.com/danidoble/webserial-pax/issues"},"dist":{"shasum":"7e4e5727ecf3ad7b551a6fd8ece610af09c837b8","tarball":"https://registry.npmjs.org/@danidoble/webserial-pax/-/webserial-pax-1.0.0.tgz","fileCount":7,"integrity":"sha512-XfZbn5F8Rs/wEhJpT+AhjGodb/gO8CCliNcjO+ERT/tYPSEeP5tlYjX0+d+ontGrZBIXX/GDQ6txDeZBwYSZew==","signatures":[{"sig":"MEUCIEb4f5819cfL5rCbdi6VSzaj7NMqxuUGJ2BLbO7cutMCAiEArmBZn/RSne8BJRfV7fIxYn3uEJaNG4NaakThuSFDqY8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":83510},"main":"./dist/index.cjs","type":"module","_from":"file:danidoble-webserial-pax-1.0.0.tgz","types":"./dist/index.d.cts","module":"./dist/index.mjs","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./package.json":"./package.json"},"scripts":{"dev":"prettier --write ./src/**/*.ts && tsdown --watch","lint":"eslint ./src/**/*.ts ./tests/**/*.ts","test":"vitest","build":"tsdown","format":"prettier --write ./src/ ./tests/ ./README.md ./package.json","release":"bumpp","typecheck":"tsc --noEmit"},"_npmUser":{"name":"danidoble","email":"ddanidoble@gmail.com"},"_resolved":"/tmp/152662ee21e38f397b073cc45415a373/danidoble-webserial-pax-1.0.0.tgz","_integrity":"sha512-XfZbn5F8Rs/wEhJpT+AhjGodb/gO8CCliNcjO+ERT/tYPSEeP5tlYjX0+d+ontGrZBIXX/GDQ6txDeZBwYSZew==","repository":{"url":"git+https://github.com/danidoble/webserial-pax.git","type":"git"},"_npmVersion":"11.13.0","description":"A strongly-typed, event-driven USB pax driver for the Web Serial API, built on top of webserial-core.","directories":{},"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"devDependencies":{"bumpp":"^11.0.1","eslint":"^10.2.1","tsdown":"^0.21.10","vitest":"^4.1.5","globals":"^17.5.0","prettier":"3.8.3","@eslint/js":"^10.0.1","typescript":"^6.0.3","webserial-core":"^2.1.0","typescript-eslint":"^8.59.0","@typescript/native-preview":"7.0.0-dev.20260328.1"},"peerDependencies":{"webserial-core":"^2.1.0"},"_npmOperationalInternal":{"tmp":"tmp/webserial-pax_1.0.0_1776993705931_0.519888650289458","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"_id":"@danidoble/webserial-pax@1.0.1","bugs":{"url":"https://github.com/danidoble/webserial-pax/issues"},"dist":{"shasum":"336e51727c7f83ca491f12eb44022743f4f4794c","tarball":"https://registry.npmjs.org/@danidoble/webserial-pax/-/webserial-pax-1.0.1.tgz","fileCount":7,"integrity":"sha512-gD4XkdGAYhbv0oHr+dNxny+PFMyRLRgLoaNyRWIBid6YI/iEerGsCf7hfubBTLn9nDlkr/L9LCBOjXiA4hIwBw==","signatures":[{"sig":"MEYCIQD9YBUQHWQ1aUoSHrAW29dii57HCoW691FN8rvVKn4WJgIhAJki/ZFhbkZec/jSpmnFw8TMarRCe6f64q8wHwQS4rfM","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQC64m5w0eNg81gP+vAdHpnUIlp7kYSknAqSEioW7A3mzQIgFdLxJvP40prfbxNK1y51F6QBXsI+kSeSEC8rOzHngk8="}],"unpackedSize":84288},"main":"./dist/index.cjs","name":"@danidoble/webserial-pax","type":"module","types":"./dist/index.d.cts","author":"Danidoble <ddanidoble@gmail.com>","module":"./dist/index.mjs","exports":{".":{"import":"./dist/index.mjs","require":"./dist/index.cjs"},"./package.json":"./package.json"},"license":"GPL-3.0-only","scripts":{"dev":"prettier --write ./src/**/*.ts && tsdown --watch","lint":"eslint ./src/**/*.ts ./tests/**/*.ts","test":"vitest","build":"tsdown","format":"prettier --write ./src/ ./tests/ ./README.md ./package.json","release":"bumpp","typecheck":"tsc --noEmit"},"version":"1.0.1","_npmUser":{"name":"danidoble","email":"ddanidoble@gmail.com"},"homepage":"https://github.com/danidoble/webserial-pax#readme","repository":{"url":"git+https://github.com/danidoble/webserial-pax.git","type":"git"},"description":"A strongly-typed, event-driven USB pax driver for the Web Serial API, built on top of webserial-core.","directories":{},"maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"_nodeVersion":"24.19.0","_hasShrinkwrap":false,"devDependencies":{"bumpp":"^11.1.0","eslint":"^10.10.0","tsdown":"^0.21.10","vitest":"^4.1.11","globals":"^17.12.0","prettier":"3.8.3","@eslint/js":"^10.0.1","typescript":"^6.0.3","webserial-core":"^2.1.0","typescript-eslint":"^8.70.0","@typescript/native-preview":"7.0.0-dev.20260328.1"},"peerDependencies":{"webserial-core":"^2.1.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/webserial-pax_1.0.1_1789055163039_0.8516908593482904"}}},"time":{"created":"2026-04-24T01:21:45.835Z","modified":"2026-09-10T15:46:03.321Z","1.0.0":"2026-04-24T01:21:46.070Z","1.0.1":"2026-09-10T15:46:03.148Z"},"bugs":{"url":"https://github.com/danidoble/webserial-pax/issues"},"author":"Danidoble <ddanidoble@gmail.com>","license":"GPL-3.0-only","homepage":"https://github.com/danidoble/webserial-pax#readme","repository":{"url":"git+https://github.com/danidoble/webserial-pax.git","type":"git"},"description":"A strongly-typed, event-driven USB pax driver for the Web Serial API, built on top of webserial-core.","maintainers":[{"name":"danidoble","email":"ddanidoble@gmail.com"}],"readme":"# @danidoble/webserial-pax\n\nA strongly-typed, event-driven PAX payment terminal driver built on top of [`webserial-core`](https://github.com/danidoble/webserial-core).\n\nHandles the serial connection, JSON handshake, auto-reconnect, and message routing — so you only deal with clean, typed events.\n\nNot tied to a single transport: swap in the **WebUSB**, **Web Bluetooth**, or **WebSocket** provider from `webserial-core`, or implement your own `SerialProvider` for any platform.\n\n[![npm version](https://img.shields.io/npm/v/@danidoble/webserial-pax)](https://www.npmjs.com/package/@danidoble/webserial-pax)\n[![license](https://img.shields.io/npm/l/@danidoble/webserial-pax)](./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-pax webserial-core\n\n# pnpm\npnpm add @danidoble/webserial-pax webserial-core\n\n# yarn\nyarn add @danidoble/webserial-pax webserial-core\n\n# bun\nbun add @danidoble/webserial-pax 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 { Pax } from '@danidoble/webserial-pax';\nimport type { SerialPortFilter } from '@danidoble/webserial-pax';\n\nconst filters: SerialPortFilter[] = [{ usbVendorId: 0x0ed3 }];\nconst pax = new Pax({ filters });\n\n// Serial lifecycle\npax.on('serial:connecting', () => console.log('Opening port…'));\npax.on('serial:connected', () => console.log('Port open'));\npax.on('serial:disconnected', () => console.log('Disconnected'));\n\n// PAX events\npax.on('pax:connected', ({ name }) => console.log('PAX ready:', name));\npax.on('pax:payment', data => console.log('Payment event:', data));\npax.on('pax:error', data => console.error('PAX error:', data));\n\n// Opens a port picker dialog (requires a user gesture)\nawait pax.connect();\n\n// Set credentials for MIT login\npax.server = 'PROD';\npax.businessId = 'your-business-id';\npax.encryptionKey = 'your-encryption-key';\npax.apiKey = 'your-api-key';\n\n// Perform a sale (blocks until terminal responds)\nconst approved = await pax.sendSale({ amount: 100.0, reference: 'ORDER-001' });\nconsole.log('Approved:', approved);\n\n// Retrieve a voucher\nconst voucher = await pax.sendVoucher({ folio: 12345 });\nconsole.log('Voucher:', voucher);\n\n// Refund\nawait pax.sendRefund({ amount: 100.0, folio: '12345', auth: '654321' });\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          | 115 200            |\n| Data bits          | 8                  |\n| Stop bits          | 1                  |\n| Parity             | none               |\n| Flow control       | none               |\n| Buffer size        | 32 768 B           |\n| Parser             | delimiter (`\\r\\n`) |\n| Command timeout    | 5 000 ms           |\n| Auto-reconnect     | ✓                  |\n| Reconnect interval | 1 500 ms           |\n| Handshake timeout  | 4 000 ms           |\n\n---\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 { Pax } from '@danidoble/webserial-pax';\n\nconst pax = new Pax({ filters: [{ usbVendorId: 0x0ed3 }] });\nawait pax.connect();\n```\n\n### WebUSB (`WebUsbProvider`)\n\n```ts\nimport { Pax, WebUsbProvider } from '@danidoble/webserial-pax';\n\nconst pax = new Pax({\n  filters: [{ usbVendorId: 0x0ed3 }],\n  provider: new WebUsbProvider()\n});\n\nawait pax.connect();\n```\n\n### Web Bluetooth (`createBluetoothProvider`)\n\n```ts\nimport { Pax, createBluetoothProvider } from '@danidoble/webserial-pax';\n\nconst pax = new Pax({ provider: createBluetoothProvider() });\nawait pax.connect();\n```\n\n### WebSocket (`createWebSocketProvider`)\n\n```ts\nimport { Pax, createWebSocketProvider } from '@danidoble/webserial-pax';\n\nconst pax = new Pax({\n  provider: createWebSocketProvider('ws://localhost:8080')\n});\n\nawait pax.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 { Pax } from '@danidoble/webserial-pax';\n\nAbstractSerialDevice.setProvider(new WebUsbProvider());\n\nconst pax = new Pax({ filters: [{ usbVendorId: 0x0ed3 }] });\nawait pax.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-pax';\nimport { Pax } from '@danidoble/webserial-pax';\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 pax = new Pax({ provider: myProvider });\nawait pax.connect();\n```\n\n---\n\n## API\n\n### Constructor\n\n```ts\nnew Pax(options?: PaxOptions)\n```\n\n| Option            | Type                  | Default | Description                                       |\n| ----------------- | --------------------- | ------- | ------------------------------------------------- |\n| `filters`         | `SerialPortFilter[]`  | `[]`    | USB filters shown in the port picker dialog.      |\n| `provider`        | `SerialProvider`      | —       | Custom serial transport (WebUSB, WebSocket, etc). |\n| `polyfillOptions` | `SerialDeviceOptions` | —       | Low-level serial settings override.               |\n\n---\n\n### Properties\n\n```ts\npax.server; // get / set — 'PROD' | 'QA' | 'DEV'\npax.businessId; // get / set — string | null\npax.encryptionKey; // get / set — string | null\npax.apiKey; // get / set — string | null\n```\n\n---\n\n### Methods\n\n#### `pax.connect()`\n\nOpens the port picker and performs the handshake. Inherited from `AbstractSerialDevice`.\n\n#### `pax.sendConnectMessage()`\n\nSends a `CONNECT` command manually.\n\n#### `pax.sendSale(options)`\n\nPerforms a full sale flow: login → init → payment. Resolves to `true` if approved, `false` otherwise. Throws if a sale is already in progress.\n\n| Option      | Type             | Default | Description                                  |\n| ----------- | ---------------- | ------- | -------------------------------------------- |\n| `amount`    | `number`         | `0`     | Sale amount. Must be greater than 0.         |\n| `reference` | `string \\| null` | `null`  | Alphanumeric reference (allows `-` and `_`). |\n\n```ts\nconst approved = await pax.sendSale({ amount: 150.0, reference: 'ORDER-42' });\n```\n\n#### `pax.sendVoucher(options)`\n\nRequests the last voucher by folio. Resolves to the voucher data or rejects on timeout (10 s).\n\n| Option  | Type     | Description                                    |\n| ------- | -------- | ---------------------------------------------- |\n| `folio` | `number` | Folio number returned after a successful sale. |\n\n```ts\nconst voucher = await pax.sendVoucher({ folio: 12345 });\n```\n\n#### `pax.sendRefund(options?)`\n\n| Option   | Type                       | Default | Description                 |\n| -------- | -------------------------- | ------- | --------------------------- |\n| `amount` | `number`                   | `0`     | Refund amount (> 0).        |\n| `folio`  | `number \\| string \\| null` | `null`  | Original transaction folio. |\n| `auth`   | `number \\| string \\| null` | `null`  | Original transaction auth.  |\n\n```ts\nawait pax.sendRefund({ amount: 150.0, folio: '12345', auth: '654321' });\n```\n\n#### `pax.sendInfo()`\n\nRequests device information. Emits `pax:info`.\n\n#### `pax.sendKeepAlive()`\n\nSends a keep-alive ping. Emits `pax:keep-alive`.\n\n#### `pax.sendRestartApp()`\n\nRequests the PAX app to restart. Emits `pax:reset-app`.\n\n#### `pax.sendGetConfig()`\n\nRequests the device configuration. Emits `pax:get-config`.\n\n#### `pax.sendHideButtons()`\n\nHides the terminal's on-screen buttons.\n\n#### `pax.sendShowButtons()`\n\nShows the terminal's on-screen buttons.\n\n#### `pax.sendDemo()`\n\nTriggers demo mode on the terminal.\n\n#### `pax.sendProductionQR()`\n\nReads a QR code in production mode.\n\n#### `pax.sendQualityAssuranceQR()`\n\nReads a QR code in QA mode.\n\n#### `pax.sendExit()`\n\nSends an exit command to the terminal.\n\n#### `pax.stop()`\n\nSends an stop request to cancel sale if is posible\n\n#### `pax.verifyNetwork`\n\nCheck if pax device has connection to internet\n\n#### `pax.sendCustomCode(obj)`\n\nSends a raw JSON command. The object must have an `action` key.\n\n```ts\nawait pax.sendCustomCode({ action: 'CUSTOM_CMD', extra: 'value' });\n```\n\n#### `pax.softReload()`\n\nResets in-progress sale / voucher state without reconnecting. Useful for recovering from stale operations.\n\n#### `pax.cancelSaleRequestInProcess()`\n\nCancels an in-progress sale that is still awaiting a response.\n\n#### `pax.isConnected()`\n\nReturns `true` when the port is open and the handshake has completed.\n\n---\n\n### `Commands` (static helpers)\n\nAll commands are available as standalone static methods for building raw payloads:\n\n```ts\nimport { Commands } from '@danidoble/webserial-pax';\n\nCommands.connection(); // CONNECT handshake\nCommands.expectedResponseConnection(); // expected CONNECT response\nCommands.makeSale({ amount: 100 }); // PAYMENT\nCommands.makeSale({ amount: 100, reference: 'X' }); // PAYMENT with reference\nCommands.getVoucher({ folio: 12345 }); // GETVOUCHER\nCommands.refund({ amount: 100, folio: '1', auth: '2' }); // REFUND\nCommands.info(); // DEVICEINFO\nCommands.keepAlive(); // KEEPALIVE\nCommands.restartApp(); // RESETAPP\nCommands.getConfig(); // GETCONFIG\nCommands.hideButtons(); // HIDEBUTTONS\nCommands.showButtons(); // SHOWBUTTONS\nCommands.forceHide(); // FORCEHIDE\nCommands.forceShow(); // FORCESHOW\nCommands.demo(); // DEMO\nCommands.exit(); // EXIT\nCommands.init(); // INIT\nCommands.readQR(); // READQR (production)\nCommands.readQR({ type: 'QA' }); // READQR (QA)\nCommands.loginMit({ server, business_id, encryption_key, api_key });\nCommands.stop(); // STOP\nCommands.verifyNetwork(); // CHECKINTERNET\nCommands.custom({ action: 'MY_ACTION' }); // custom payload\n```\n\n---\n\n## Events\n\n### Core events (from `webserial-core`)\n\n| Event                    | Payload                       | Description                                       |\n| ------------------------ | ----------------------------- | ------------------------------------------------- |\n| `serial:connecting`      | `instance`                    | Port is being opened.                             |\n| `serial:connected`       | `instance`                    | Port opened successfully.                         |\n| `serial:disconnected`    | `instance`                    | Port closed or device unplugged.                  |\n| `serial:reconnecting`    | `instance`                    | Auto-reconnect attempt in progress.               |\n| `serial:data`            | `data: string`, `instance`    | Parsed string frame received from the device.     |\n| `serial:sent`            | `data: string`, `instance`    | Data written to the port.                         |\n| `serial:error`           | `error: Error`, `instance`    | An error occurred during communication.           |\n| `serial:need-permission` | `instance`                    | No authorised port found; user must grant access. |\n| `serial:timeout`         | `command: string`, `instance` | A queued command timed out.                       |\n\n### PAX events\n\n| Event                | Payload                                   | Description                                                |\n| -------------------- | ----------------------------------------- | ---------------------------------------------------------- |\n| `pax:connected`      | `{ name, request, status }`               | Handshake succeeded; PAX is ready.                         |\n| `pax:init`           | `{ name, request, status }`               | INIT response received.                                    |\n| `pax:init-app`       | `{ name, request, status }`               | INITAPP response received.                                 |\n| `pax:login`          | `Record<string, unknown>`                 | LOGIN response received.                                   |\n| `pax:voucher`        | `Record<string, unknown>`                 | LASTVOUCHER response received.                             |\n| `pax:info`           | `Record<string, unknown>`                 | DEVICEINFO response received.                              |\n| `pax:keep-alive`     | `{ name, request, status }`               | KEEPALIVE response received.                               |\n| `pax:reset-app`      | `{ name, request, status }`               | RESETAPP response received.                                |\n| `pax:get-config`     | `Record<string, unknown>`                 | GETCONFIG response received.                               |\n| `pax:buttons-status` | `{ name, request, hidden: boolean }`      | Button visibility changed.                                 |\n| `pax:payment`        | `Record<string, unknown>`                 | Any payment-related response (process, card data, result). |\n| `pax:error`          | `Record<string, unknown>`                 | An ERROR response was received.                            |\n| `pax:refund`         | `Record<string, unknown>`                 | REFUND response received.                                  |\n| `pax:message`        | `Record<string, unknown> \\| string`       | Emitted for every incoming message (parsed or raw string). |\n| `pax:network`        | `{ name, request, hasInternet, quality }` | Response to check CHECKINTERNET                            |\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\nimport {\n  Pax,\n  Commands,\n  WebUsbProvider,\n  createBluetoothProvider,\n  createWebSocketProvider\n} from '@danidoble/webserial-pax';\n\nimport type {\n  PaxOptions,\n  MakeSaleOptions,\n  GetVoucherOptions,\n  RefundOptions,\n  LoginMitOptions,\n  ServerType,\n  SerialPortFilter,\n  SerialDeviceOptions,\n  SerialEventMap,\n  SerialProvider,\n  SerialPolyfillOptions\n} from '@danidoble/webserial-pax';\n```\n\n---\n\n## License\n\n[GPL-3.0-only](./LICENSE.md) © [Danidoble](https://github.com/danidoble)\n","readmeFilename":""}