import type { PrintableArea } from './printable-area.js'; import type { DeviceVerifications } from './verifications.js'; /** * Wire-protocol-only transport types. * * Distinct runtime APIs (node-usb vs WebUSB, node serialport vs Web * Serial, classic Bluetooth SPP vs BLE GATT) are *implementations* of * these transport keys, not separate keys. Per-platform packages * declare which transport types their implementations satisfy; the * registry stays wire-protocol-honest. * * The two Bluetooth keys stay split — BR/EDR vs BLE radios, SDP vs * advertisement discovery, classic vs BLE pairing flows, and Web * Bluetooth being GATT-only by spec all force the separation. */ export type TransportType = 'usb' | 'tcp' | 'serial' | 'bluetooth-spp' | 'bluetooth-gatt'; /** * Legacy four-state verification status backing `DeviceSupport.status` * and `DeviceReport.result`. * * Superseded by `SupportStatus` (3-state stored) + `EffectiveStatus` * (5-state rendered) in `./verifications.js`. Retained so the existing * `support: { status: 'untested' }` authoring shape keeps type-checking * during the alias transition; codegen maps the legacy rungs to the * new ones (`'broken'` → `'unsupported'`, `'untested'` → absent). * * @deprecated Use `SupportStatus` from `./verifications.js` for stored * rungs and `EffectiveStatus` for rendered status. Removed once all * drivers have migrated their JSON5 to `verifications`. */ export type LegacySupportStatus = 'verified' | 'partial' | 'broken' | 'untested'; /** * USB transport parameters. * * VID/PID stored as hex strings (e.g. `'0x0922'`) matching what every * datasheet, lsusb output, and forum post uses. Consumers that need * numbers parseInt at the boundary. */ export interface UsbTransport { /** Hex string, e.g. `'0x0922'`. */ vid: string; /** Hex string, e.g. `'0x0020'`. */ pid: string; } /** TCP transport parameters. */ export interface TcpTransport { /** TCP port (JetDirect printers use 9100). */ port: number; /** mDNS service type for zero-config discovery. */ mdns?: { serviceType: string; subtypes?: readonly string[]; }; } /** * Physical serial transport parameters (UART / USB-serial). * * For Bluetooth SPP printers — even though most platforms surface the * RFCOMM channel as a /dev/rfcomm or COM port — use the * `bluetooth-spp` key instead so the runtime picker can present it as * "Bluetooth" rather than "Serial port". */ export interface SerialTransport { defaultBaud: number; supportedBauds?: readonly number[]; /** Optional flow-control hint; most label printers want 'none'. */ flowControl?: 'none' | 'hardware' | 'software'; } /** * Bluetooth SPP (Serial Port Profile, classic Bluetooth). * * Verified working on Windows and Linux via the OS-paired RFCOMM * device path (the runtime's serial implementation satisfies both * `serial` and `bluetooth-spp` transport keys). macOS still untested. * Baud rate is deliberately absent: SPP negotiates its own framing, * and the value on /dev/rfcomm0 is fictional. */ export interface BluetoothSppTransport { /** Bluetooth name prefix for OS pickers. */ namePrefix?: string; /** RFCOMM channel; some printers publish via SDP, others fix it. */ rfcommChannel?: number; } /** * Bluetooth Low Energy GATT. * * UUIDs are typically discovered by sniffing GATT traffic from the * manufacturer's mobile app (nRF Connect, LightBlue) — they are * rarely published in printer documentation. */ export interface BluetoothGattTransport { /** Primary GATT service UUID for this printer family. */ serviceUuid: string; /** GATT characteristic UUID for write (TX to printer). */ txCharacteristicUuid: string; /** * GATT characteristic UUID for read/notify (RX from printer). * Omit if the TX characteristic also handles notifications. */ rxCharacteristicUuid?: string; /** Device name prefix for the browser picker filter, e.g. `'QL-820'`. */ namePrefix?: string; /** Negotiable BLE MTU; default 20 (BLE 4.0 minimum). */ mtu?: number; } /** * Per-transport schema for a device. * * Only the keys this device actually supports are present. Each key * carries the parameters its transport needs — VID/PID under `usb`, * port under `tcp`, etc. — instead of bunching them at the top level. */ export interface DeviceTransports { usb?: UsbTransport; tcp?: TcpTransport; serial?: SerialTransport; 'bluetooth-spp'?: BluetoothSppTransport; 'bluetooth-gatt'?: BluetoothGattTransport; } /** * Engine-level capability flags. * * Mirrors `DeviceEntry.capabilities` but for properties of the * printhead / sensor / cutter on this specific engine. Open shape — * drivers extend with family-specific keys via the index signature * without touching the contracts package. * * **Promotion rule:** a capability earns a named key here iff (a) it * is implemented by ≥2 active drivers AND (b) at least one registry * consumer (picker, rasterizer, docs badge, runtime UX) actually * branches on it. Today: `mediaDetection` and `autocut`. * Single-vendor (e.g. `twoColor`, `genuineMediaRequired`) lands on * the index signature until a second vendor adopts. */ export interface PrintEngineCapabilities { /** * Whether this engine reports loaded media via `getStatus()`. * * What apps do on mismatch is an app-level decision; the contracts * library does not block prints. See `hardwareQuirks` on entries * where the printer's mismatch behaviour is non-obvious (Brother * QL hard-rejects, Dymo 5xx silently misprints). */ mediaDetection?: boolean; /** Auto-cutter on this engine's paper path. */ autocut?: boolean; /** * Driver-specific capability keys land here. Examples today: * `twoColor` (Brother-only, two-colour ribbon path) and * `genuineMediaRequired` (Dymo-only). Promote to a named key when * a second active driver implements with compatible semantics. */ [k: string]: unknown; } /** * Per-engine routing hints. * * Transport-layer routing is keyed by transport (`bind.usb`, future * `bind.tcp`) and consumed by the transport implementation. * Protocol-layer routing (`bind.address`) sits as a flat sibling and * is consumed by the protocol implementation — opaque to the * registry. * * **USB-composite example (LabelWriter Duo):** each engine binds to * its own USB interface via `bind.usb.bInterfaceNumber`. * * **Protocol-addressed example (LabelWriter Twin Turbo):** both * engines share the chassis USB endpoint and select via * `bind.address` — for `lw-450`, `bind.address: 1` is encoded as * `ESC q 0x01` prepended to the job. `'auto'` is not a stored value * — it is a routing mode in `PrintOptions.engine`. * * If a future protocol grows more than one in-band routing * dimension, promote `bind.address` to `bind.protocol: { ... }`. */ export interface EngineBind { /** USB-composite routing — engine bound to a specific USB interface. */ usb?: { bInterfaceNumber: number; }; /** Opaque protocol-layer address; protocol module knows how to encode. */ address?: number; } /** * A print engine — one printhead with one protocol. * * Most devices have a single engine. The LabelWriter Duo has two * (label + tape) with different protocols and different USB * interfaces. The Twin Turbo also has two (left + right) sharing one * transport with in-band protocol-level addressing. */ export interface PrintEngine { /** * Semantic role identifier — used as the lookup key on the runtime * adapter (`printer.engines[role]`). For single-engine devices: * `'primary'`. For composite devices: descriptive (`'label'`, * `'tape'`, `'left'`, `'right'`). */ role: string; /** Driver-family-specific wire-protocol tag. */ protocol: string; dpi: number; /** Native dot count across the head. */ headDots: number; /** * Per-engine routing hints. Omit on single-engine devices. * See `EngineBind` for transport-layer vs protocol-layer routing. */ bind?: EngineBind; /** * Filter for which entries from the driver's media registry this * engine accepts. Resolved against `MediaDescriptor.targetModels`. * Driver-defined string set; `undefined` = engine accepts every * media in the driver's registry. */ mediaCompatibility?: readonly string[]; /** Engine-level capability flags. See `PrintEngineCapabilities`. */ capabilities?: PrintEngineCapabilities; /** * Chassis-physical dead zones around the printable rectangle (mm). * * Insets the head physically cannot reach — head-to-cutter offsets, * head-vs-tape-width geometry, sensor-window keep-outs. Encoders * use this to crop / shift the bitmap so authored content lands * where the user expects. * * Distinct from `MediaDescriptor.printMargins` (per-media * design-tool inset), from `forcedTrailingFeedMm` (post-print tape * advance), and from any wire-protocol "feed margin" command the * firmware enacts on its own (e.g. Brother QL/PT `ESC i d`). * * Absent means "not measured" rather than "measured to zero". Use * `getPrintableArea(engine, media?)` from * `@thermal-label/contracts` to resolve a fully-populated value * with the standard zero defaults and per-roll media-tag override * applied. */ printableArea?: PrintableArea; /** * Post-print tape advance the printer (or this driver's encoder) * forces after the printed bitmap, in mm. * * Distinct from `printableArea`: * - `printableArea` describes where the head can't reach during * the print; * - `forcedTrailingFeedMm` describes tape eaten *after* the print * so content clears the cutter / tear bar. * * Populated where the suite has a known fixed post-print feed * (cat-printer's `DEFAULT_FEED_LINES`, labelmanager's encoder-side * trailing pad, LabelManager PnP's firmware-enforced advance). * Absent / `0` when the trailing feed is variable (e.g. labelwriter * `ESC E` advances to the next tear bar — distance depends on the * label gap-sensor position) or when the suite has no measurement. * * Use `getForcedTrailingFeedMm(engine)` from * `@thermal-label/contracts` to resolve with the zero default. */ forcedTrailingFeedMm?: number; } /** * A single accepted verification report against a device. * * Mirrors the fields the org-level `hardware-status.yaml` schema * already records — issue number, reporter, date, result. Folded * inline into the device entry so there is one source of truth per * driver instead of a parallel YAML overlay. * * @deprecated Superseded by `VerificationCell` in `./verifications.js`. * The new shape drops `notes`, `reporter`, `os`, `selfVerified`, `result` * — the linked GitHub issue carries those. Retained during the alias * transition; removed in the cleanup PR once all drivers have migrated. */ export interface DeviceReport { /** Issue / PR number where the report was accepted. */ issue: number; /** Reporter's GitHub handle or attribution string. */ reporter: string; /** ISO date (YYYY-MM-DD) the report was filed. */ date: string; /** Verification outcome from this report. */ result: LegacySupportStatus; os?: 'Linux' | 'macOS' | 'Windows'; /** Free-form notes (markdown allowed). */ notes?: string; /** True if the reporter is also the implementer / maintainer. */ selfVerified?: boolean; } /** * Verification state for a device. * * Always present on `DeviceEntry` (defaults to `{ status: 'untested' }`) * so consumer types stay unconditional. * * @deprecated Superseded by `DeviceVerifications` in * `./verifications.js` (per-transport `VerificationCell`s, no * `reports`/`lastVerified`/`packageVersion`/`quirks`/engine axis). * Codegen synthesises this from `verifications` and maps legacy * `status` values to the new rungs (`'broken'` → `'unsupported'`, * `'untested'` → absent). Retained during the alias transition; * removed in the cleanup PR once all drivers have migrated. */ export interface DeviceSupport { /** Worst-case status across declared transports and engines. */ status: LegacySupportStatus; /** Per-transport status, where the data records it. */ transports?: Partial>; /** * Per-engine status — useful for the Duo's "label works, tape * doesn't" case. Keys must match `engines[].role`. */ engines?: Record; /** ISO date of the most recent accepted report. */ lastVerified?: string; /** Driver package version the most recent reports were filed against. */ packageVersion?: string; /** Editorial caveats. Markdown. Changes with firmware revisions. */ quirks?: string; /** Accepted verification reports backing the status above. */ reports?: readonly DeviceReport[]; } /** * A device entry in a driver's registry. * * Each driver's `data/devices.json` lists entries of this shape. The * driver still owns the data; contracts owns only the shape. */ export interface DeviceEntry { /** Stable key used as the registry export name (e.g. `'LW_450'`). */ key: string; /** Human-readable model name, e.g. `'LabelWriter 450'`. */ name: string; /** * Self-reported identity, network-management side: the model * strings the device reports about itself, without vendor prefix. * Sources are the IEEE-1284 device ID `MDL:` field, mDNS TXT * `usb_MDL`, IPP `printer-device-id`, and SNMP `hrDeviceDescr` / * `sysDescr`; the vendor word in front of them (`Brother QL-820NWB`) * is stripped by the matcher, not stored here. * * Defaults to `[name]`. Set it only when the marketing name differs * from the wire name: Brother reports `QL-820NWB` for the entry * named `QL-820NWBc`, so that entry carries * `modelNames: ['QL-820NWB', 'QL-820NWBc']`. Matching is by whole * token, longest candidate first, so `QL-800` never matches a * `QL-8000`-style longer name. Consumed by * `matchModelName` in `@thermal-label/transport`. * * A sibling field for the *wire-protocol* side (the model code a * printer answers with to a driver-specific identity query, e.g. * niimbot's `PrinterInfo(0x08)`) is a separate concern and lands as * its own field when a driver needs it; do not encode numeric * protocol codes here. */ modelNames?: readonly string[]; /** Driver family this device belongs to, e.g. `'labelwriter'`. */ family: string; /** Wire-protocol transports this device exposes. */ transports: DeviceTransports; /** * Print engines in this device. Always an array, never empty — * single-engine devices fabricate a `'primary'` entry. Composite * devices (Duo, Twin) carry one entry per independent engine. */ engines: readonly PrintEngine[]; /** * Chassis-level capability flags — properties of the box, not the * printhead. Most boolean capabilities are engine-level; this bag * is for genuinely chassis-y things (Brother's `editorLite` * USB-Mass-Storage trick, eventual battery / display flags). Open * shape so drivers can extend without touching contracts. */ capabilities?: Readonly>; /** * In-source hardware quirks — immutable facts about the chassis. * * Distinct from `support.quirks`, which is editorial and changes * with firmware revisions. Example: "PID collides with the * LabelManager PnP variant; needs usb_modeswitch on Linux". */ hardwareQuirks?: string; /** * Always defined; defaults to `{ status: 'untested' }`. * * @deprecated Author `verifications` instead. Kept populated by * codegen (synthesised from `verifications` if present, else mapped * from legacy authoring) so existing consumers keep working * unchanged. Removed in the cleanup PR once all drivers migrate. */ support: DeviceSupport; /** * Per-transport stored verifications. Authored by hardware-report * PRs; expanded at codegen time into a derived grid (see * `expandVerifications` in `./expand.js`). When absent, codegen * falls back to legacy `support.status`. */ verifications?: DeviceVerifications; } /** * A driver's full device registry. * * `schemaVersion: 1` is the initial published shape. Bump when a * future change is genuinely incompatible; the aggregator and * cross-driver consumers refuse unknown values rather than silently * mishandle shape divergence. */ export interface DeviceRegistry { schemaVersion: 1; /** Driver family identifier — matches `DeviceEntry.family`. */ driver: string; devices: readonly DeviceEntry[]; } //# sourceMappingURL=device.d.ts.map