@gohcltech/edge-print-client 2.0.50-develop → 2.0.54-develop
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +65 -0
- package/dist/edge-print.d.ts +37 -0
- package/dist/edge-print.js +133 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,6 +80,11 @@ asked to trust an "Unknown client".
|
|
|
80
80
|
|
|
81
81
|
Throws if the agent is unreachable or the token is rejected after all retries.
|
|
82
82
|
|
|
83
|
+
The client sends its wire protocol version automatically as part of the auth
|
|
84
|
+
handshake; it is not a `connect()` option. If the agent speaks a different
|
|
85
|
+
version, `connect()` throws a message naming both versions and which side to
|
|
86
|
+
update — see [`protocolVersion`](#protocolversion).
|
|
87
|
+
|
|
83
88
|
Calling `connect()` on an already-connected client **replaces** the connection —
|
|
84
89
|
the existing socket is torn down before the new one is attempted, and its
|
|
85
90
|
in-flight requests reject. If the new attempt then fails there is no connection
|
|
@@ -117,6 +122,15 @@ Each `PrinterInfo` object includes:
|
|
|
117
122
|
| `orientations` | `string[]?` | Supported orientations (e.g. `["portrait", "landscape"]`) |
|
|
118
123
|
| `virtualPrinter` | `boolean?` | `true` for Developer (virtual) printers injected by the agent |
|
|
119
124
|
|
|
125
|
+
1.x agents sent `is_default`, `share_name`, `copies_max` and `virtual_printer`.
|
|
126
|
+
Those spellings are still accepted on read and mapped onto the camelCase fields
|
|
127
|
+
above, so nothing downstream sees the old names. **Removed in 3.0.**
|
|
128
|
+
|
|
129
|
+
This is not a promise that a 1.x agent works: `connect()` refuses a protocol-1
|
|
130
|
+
agent before `printers()` is ever reached. It is there so that a 2.x agent which
|
|
131
|
+
regresses a field name degrades to a missing value rather than a silent
|
|
132
|
+
`undefined`.
|
|
133
|
+
|
|
120
134
|
`TrayInfo`:
|
|
121
135
|
|
|
122
136
|
| Field | Type | Description |
|
|
@@ -238,6 +252,41 @@ if (!ep.isConnected()) {
|
|
|
238
252
|
|
|
239
253
|
---
|
|
240
254
|
|
|
255
|
+
### `agentVersion`
|
|
256
|
+
|
|
257
|
+
Read-only. Version of the agent this client is connected to, as the agent
|
|
258
|
+
reported it during `connect()`.
|
|
259
|
+
|
|
260
|
+
```ts
|
|
261
|
+
console.log(`Edge Printing agent ${ep.agentVersion}`) // "2.0.7"
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
`undefined` until a connection is authenticated, and again once it closes —
|
|
265
|
+
including the gap during a reconnect, where the old socket is torn down before
|
|
266
|
+
the new one authenticates. It is also `undefined` on a live connection if the
|
|
267
|
+
agent did not report a version, so treat it as optional even when
|
|
268
|
+
`isConnected()` is `true`. Use it for diagnostics and support output, not to
|
|
269
|
+
branch behaviour on.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
### `protocolVersion`
|
|
274
|
+
|
|
275
|
+
Read-only. Wire protocol version the connected agent speaks.
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
console.log(ep.protocolVersion) // 2
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
`undefined` until a connection is authenticated, and again once it closes. When
|
|
282
|
+
it is set it always equals this client's own protocol version — `connect()`
|
|
283
|
+
refuses any other — so it is a diagnostic rather than a capability check.
|
|
284
|
+
|
|
285
|
+
The protocol version is independent of the package version: 2.0 and 2.4 of this
|
|
286
|
+
library both speak protocol `2`.
|
|
287
|
+
|
|
288
|
+
---
|
|
289
|
+
|
|
241
290
|
### `onClose(fn)`
|
|
242
291
|
|
|
243
292
|
Registers a callback that fires whenever the connection closes — network drop, agent restart, or an explicit `disconnect()` call. Multiple listeners are supported.
|
|
@@ -334,6 +383,22 @@ If you see a security warning, the certificate is not yet trusted.
|
|
|
334
383
|
|
|
335
384
|
The token was rejected. Generate a new one from the Edge Printing settings window.
|
|
336
385
|
|
|
386
|
+
### `connect()` throws "Protocol version mismatch"
|
|
387
|
+
|
|
388
|
+
The agent and this library speak different wire protocol versions. The message
|
|
389
|
+
names both and says which side to update:
|
|
390
|
+
|
|
391
|
+
- **"Update the Edge Printing agent"** — the agent is older than this library.
|
|
392
|
+
Install a newer agent build on the user's machine.
|
|
393
|
+
- **"Update the Edge Printing client library"** — the agent is newer. Upgrade
|
|
394
|
+
`@gohcltech/edge-print-client` in the web app.
|
|
395
|
+
|
|
396
|
+
An agent older than 2.0 has no handshake at all and reports as protocol `1`.
|
|
397
|
+
|
|
398
|
+
A mismatch is deterministic, so `connect()`'s retries cannot clear it — it still
|
|
399
|
+
exhausts them (about 3 seconds by default) before throwing. Pass `retries: 0`
|
|
400
|
+
when probing for a usable agent. One side has to be upgraded.
|
|
401
|
+
|
|
337
402
|
### Requests time out after 30 seconds
|
|
338
403
|
|
|
339
404
|
The agent accepted the connection but stopped responding. Restart the agent. If
|
package/dist/edge-print.d.ts
CHANGED
|
@@ -268,6 +268,8 @@ export declare class EdgePrintClient {
|
|
|
268
268
|
private established;
|
|
269
269
|
private authenticated;
|
|
270
270
|
private closeListeners;
|
|
271
|
+
private agentVersionValue;
|
|
272
|
+
private protocolVersionValue;
|
|
271
273
|
/**
|
|
272
274
|
* Open a WebSocket connection to the Edge Printing agent and authenticate.
|
|
273
275
|
*
|
|
@@ -379,6 +381,31 @@ export declare class EdgePrintClient {
|
|
|
379
381
|
* connection is established.
|
|
380
382
|
*/
|
|
381
383
|
isConnected(): boolean;
|
|
384
|
+
/**
|
|
385
|
+
* Version of the Edge Printing agent this client is connected to, as the
|
|
386
|
+
* agent reported it during {@link connect}.
|
|
387
|
+
*
|
|
388
|
+
* `undefined` until a connection is authenticated, and again once it closes —
|
|
389
|
+
* including for the gap during a reconnect, where the old socket is torn down
|
|
390
|
+
* before the new one authenticates. Also `undefined` on a live connection if
|
|
391
|
+
* the agent reported no version, so it stays optional even when
|
|
392
|
+
* {@link isConnected} is `true`. Read it for diagnostics and support output,
|
|
393
|
+
* not to branch behaviour on: the agent's *protocol* version, which
|
|
394
|
+
* {@link protocolVersion} reports, is what determines what the wire supports.
|
|
395
|
+
*/
|
|
396
|
+
get agentVersion(): string | undefined;
|
|
397
|
+
/**
|
|
398
|
+
* Wire protocol version the connected agent speaks.
|
|
399
|
+
*
|
|
400
|
+
* `undefined` until a connection is authenticated, and again once it closes.
|
|
401
|
+
* When it is set it always equals this client's own protocol version —
|
|
402
|
+
* {@link connect} refuses any other — so it is a diagnostic, not a
|
|
403
|
+
* capability check.
|
|
404
|
+
*
|
|
405
|
+
* This client sends its protocol version automatically; it is not a
|
|
406
|
+
* {@link ConnectOptions} field.
|
|
407
|
+
*/
|
|
408
|
+
get protocolVersion(): number | undefined;
|
|
382
409
|
/**
|
|
383
410
|
* Register a callback invoked whenever the connection closes — whether from
|
|
384
411
|
* a network drop, an agent restart, or an explicit {@link disconnect} call.
|
|
@@ -431,6 +458,16 @@ export declare class EdgePrintClient {
|
|
|
431
458
|
private openSocket;
|
|
432
459
|
private handleMessage;
|
|
433
460
|
private request;
|
|
461
|
+
/**
|
|
462
|
+
* Clears the state that belongs to one socket rather than to the client.
|
|
463
|
+
*
|
|
464
|
+
* Invariant: when {@link isConnected} is `false`, both version getters read
|
|
465
|
+
* `undefined`. Two paths tear a socket down — {@link discardSocket} and
|
|
466
|
+
* `openSocket`'s `onclose`, which does *not* route through it — and both must
|
|
467
|
+
* call this. Clearing in only one leaves a client whose agent restarted still
|
|
468
|
+
* reporting a version for a connection that is gone.
|
|
469
|
+
*/
|
|
470
|
+
private clearSessionState;
|
|
434
471
|
private rejectPending;
|
|
435
472
|
}
|
|
436
473
|
/**
|
package/dist/edge-print.js
CHANGED
|
@@ -16,6 +16,55 @@
|
|
|
16
16
|
* )
|
|
17
17
|
* ```
|
|
18
18
|
*/
|
|
19
|
+
/**
|
|
20
|
+
* The wire protocol this client speaks.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately independent of the package version and hand-maintained: 2.0 and
|
|
23
|
+
* 2.4 of this library both speak protocol 2, and a patch release must not read
|
|
24
|
+
* as a protocol change. Bump it only when the wire format itself changes, in
|
|
25
|
+
* lockstep with the agent's `PROTOCOL_VERSION`.
|
|
26
|
+
*/
|
|
27
|
+
const PROTOCOL_VERSION = 2;
|
|
28
|
+
/**
|
|
29
|
+
* An `auth_ok` from an agent that predates the handshake carries no version.
|
|
30
|
+
* Such an agent is, by definition, speaking protocol 1.
|
|
31
|
+
*/
|
|
32
|
+
const LEGACY_PROTOCOL_VERSION = 1;
|
|
33
|
+
/**
|
|
34
|
+
* Maps a printer object off the wire onto {@link PrinterInfo}.
|
|
35
|
+
*
|
|
36
|
+
* Belt-and-braces, not a promise of 1.x agent support: the version handshake in
|
|
37
|
+
* {@link EdgePrintClient.connect} refuses a protocol-1 agent outright, so a real
|
|
38
|
+
* 1.x agent never reaches this function. It exists so that a *2.x* agent which
|
|
39
|
+
* regresses a field name — or a future decision to tolerate older agents —
|
|
40
|
+
* degrades to a missing value rather than a silent `undefined` in a UI. Do not
|
|
41
|
+
* read support for old agents into it and relax the handshake.
|
|
42
|
+
*
|
|
43
|
+
* Deprecated on arrival: the snake_case half is removed in 3.0.
|
|
44
|
+
*/
|
|
45
|
+
function normalisePrinter(p) {
|
|
46
|
+
// Destructured out rather than spread over, so the result carries no
|
|
47
|
+
// snake_case keys. `{ ...p, isDefault }` would leave `is_default` sitting on
|
|
48
|
+
// an object whose declared type says it cannot be there.
|
|
49
|
+
const { is_default, share_name, copies_max, virtual_printer, ...rest } = p;
|
|
50
|
+
const shareName = p.shareName ?? share_name;
|
|
51
|
+
const copiesMax = p.copiesMax ?? copies_max;
|
|
52
|
+
const virtualPrinter = p.virtualPrinter ?? virtual_printer;
|
|
53
|
+
return {
|
|
54
|
+
...rest,
|
|
55
|
+
// Required and a boolean, so it gets a default — `undefined` reaching a
|
|
56
|
+
// template renders "default: undefined".
|
|
57
|
+
isDefault: p.isDefault ?? is_default ?? false,
|
|
58
|
+
// Spread conditionally, not assigned. `shareName: undefined` still creates
|
|
59
|
+
// an own key, so a plain assignment would make `'virtualPrinter' in p` true
|
|
60
|
+
// for every printer — and a caller uses that to tell "the agent did not
|
|
61
|
+
// report this" from "reported false". The agent omits absent optionals, so
|
|
62
|
+
// this keeps the shim's output the same shape as an unshimmed payload.
|
|
63
|
+
...(shareName !== undefined && { shareName }),
|
|
64
|
+
...(copiesMax !== undefined && { copiesMax }),
|
|
65
|
+
...(virtualPrinter !== undefined && { virtualPrinter }),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
19
68
|
/**
|
|
20
69
|
* WebSocket client for the Edge Printing agent.
|
|
21
70
|
*
|
|
@@ -103,9 +152,44 @@ export class EdgePrintClient {
|
|
|
103
152
|
}
|
|
104
153
|
try {
|
|
105
154
|
await this.openSocket(`wss://${host}:${port}`);
|
|
106
|
-
//
|
|
107
|
-
// the agent treats a missing key as "no name given".
|
|
108
|
-
|
|
155
|
+
// `clientName` is omitted entirely when unset, rather than sent as
|
|
156
|
+
// undefined/null — the agent treats a missing key as "no name given".
|
|
157
|
+
// Built as one object rather than a ternary over two literals, so
|
|
158
|
+
// `protocolVersion` cannot end up on only one of the branches.
|
|
159
|
+
const auth = {
|
|
160
|
+
token,
|
|
161
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
162
|
+
...(clientName ? { clientName } : {}),
|
|
163
|
+
};
|
|
164
|
+
const ack = await this.request('auth', auth);
|
|
165
|
+
// An agent that predates the handshake answers with a bare `auth_ok`.
|
|
166
|
+
// A non-integer version folds to the same place: `"2"` is a client bug
|
|
167
|
+
// we want reported, not coerced into a match.
|
|
168
|
+
const agentProtocol = Number.isInteger(ack['protocolVersion'])
|
|
169
|
+
? ack['protocolVersion']
|
|
170
|
+
: LEGACY_PROTOCOL_VERSION;
|
|
171
|
+
if (agentProtocol !== PROTOCOL_VERSION) {
|
|
172
|
+
throw new Error(`Protocol version mismatch: this client speaks protocol version ` +
|
|
173
|
+
`${PROTOCOL_VERSION}, but the agent speaks ${agentProtocol}. ` +
|
|
174
|
+
(agentProtocol < PROTOCOL_VERSION
|
|
175
|
+
? 'Update the Edge Printing agent.'
|
|
176
|
+
: 'Update the Edge Printing client library.'));
|
|
177
|
+
}
|
|
178
|
+
// Re-checked after the await: `request` suspends, and a newer connect
|
|
179
|
+
// (or a `disconnect`) can land while this one is waiting on the agent.
|
|
180
|
+
// Without this the superseded attempt goes on to publish its agent's
|
|
181
|
+
// versions and mark itself established over whatever replaced it — the
|
|
182
|
+
// same check the loop makes before each attempt and in the catch, which
|
|
183
|
+
// between them cover every path except this one.
|
|
184
|
+
if (this.connectGeneration !== generation) {
|
|
185
|
+
throw new Error('Connection superseded');
|
|
186
|
+
}
|
|
187
|
+
// Assigned together so the getters and `isConnected()` agree: either
|
|
188
|
+
// this attempt owns the connection and publishes all of it, or none.
|
|
189
|
+
this.agentVersionValue = typeof ack['agentVersion'] === 'string'
|
|
190
|
+
? ack['agentVersion']
|
|
191
|
+
: undefined;
|
|
192
|
+
this.protocolVersionValue = agentProtocol;
|
|
109
193
|
this.authenticated = true;
|
|
110
194
|
this.established = true;
|
|
111
195
|
return;
|
|
@@ -142,7 +226,7 @@ export class EdgePrintClient {
|
|
|
142
226
|
*/
|
|
143
227
|
async printers() {
|
|
144
228
|
const resp = await this.request('get_printers', {});
|
|
145
|
-
return resp.printers;
|
|
229
|
+
return (resp.printers ?? []).map(normalisePrinter);
|
|
146
230
|
}
|
|
147
231
|
/**
|
|
148
232
|
* Return the name of the OS default printer.
|
|
@@ -233,6 +317,35 @@ export class EdgePrintClient {
|
|
|
233
317
|
isConnected() {
|
|
234
318
|
return this.ws?.readyState === WebSocket.OPEN && this.authenticated;
|
|
235
319
|
}
|
|
320
|
+
/**
|
|
321
|
+
* Version of the Edge Printing agent this client is connected to, as the
|
|
322
|
+
* agent reported it during {@link connect}.
|
|
323
|
+
*
|
|
324
|
+
* `undefined` until a connection is authenticated, and again once it closes —
|
|
325
|
+
* including for the gap during a reconnect, where the old socket is torn down
|
|
326
|
+
* before the new one authenticates. Also `undefined` on a live connection if
|
|
327
|
+
* the agent reported no version, so it stays optional even when
|
|
328
|
+
* {@link isConnected} is `true`. Read it for diagnostics and support output,
|
|
329
|
+
* not to branch behaviour on: the agent's *protocol* version, which
|
|
330
|
+
* {@link protocolVersion} reports, is what determines what the wire supports.
|
|
331
|
+
*/
|
|
332
|
+
get agentVersion() {
|
|
333
|
+
return this.agentVersionValue;
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Wire protocol version the connected agent speaks.
|
|
337
|
+
*
|
|
338
|
+
* `undefined` until a connection is authenticated, and again once it closes.
|
|
339
|
+
* When it is set it always equals this client's own protocol version —
|
|
340
|
+
* {@link connect} refuses any other — so it is a diagnostic, not a
|
|
341
|
+
* capability check.
|
|
342
|
+
*
|
|
343
|
+
* This client sends its protocol version automatically; it is not a
|
|
344
|
+
* {@link ConnectOptions} field.
|
|
345
|
+
*/
|
|
346
|
+
get protocolVersion() {
|
|
347
|
+
return this.protocolVersionValue;
|
|
348
|
+
}
|
|
236
349
|
/**
|
|
237
350
|
* Register a callback invoked whenever the connection closes — whether from
|
|
238
351
|
* a network drop, an agent restart, or an explicit {@link disconnect} call.
|
|
@@ -299,7 +412,7 @@ export class EdgePrintClient {
|
|
|
299
412
|
const settleOpen = this.pendingOpen;
|
|
300
413
|
this.ws = null;
|
|
301
414
|
this.pendingOpen = null;
|
|
302
|
-
this.
|
|
415
|
+
this.clearSessionState();
|
|
303
416
|
if (ws) {
|
|
304
417
|
ws.onopen = null;
|
|
305
418
|
ws.onmessage = null;
|
|
@@ -337,7 +450,7 @@ export class EdgePrintClient {
|
|
|
337
450
|
settled();
|
|
338
451
|
openWaiting?.(new Error(`Cannot reach Edge Printing agent at ${url}`));
|
|
339
452
|
this.ws = null;
|
|
340
|
-
this.
|
|
453
|
+
this.clearSessionState();
|
|
341
454
|
this.rejectPending(new Error('Connection closed'));
|
|
342
455
|
this.markClosed();
|
|
343
456
|
};
|
|
@@ -412,6 +525,20 @@ export class EdgePrintClient {
|
|
|
412
525
|
}
|
|
413
526
|
});
|
|
414
527
|
}
|
|
528
|
+
/**
|
|
529
|
+
* Clears the state that belongs to one socket rather than to the client.
|
|
530
|
+
*
|
|
531
|
+
* Invariant: when {@link isConnected} is `false`, both version getters read
|
|
532
|
+
* `undefined`. Two paths tear a socket down — {@link discardSocket} and
|
|
533
|
+
* `openSocket`'s `onclose`, which does *not* route through it — and both must
|
|
534
|
+
* call this. Clearing in only one leaves a client whose agent restarted still
|
|
535
|
+
* reporting a version for a connection that is gone.
|
|
536
|
+
*/
|
|
537
|
+
clearSessionState() {
|
|
538
|
+
this.authenticated = false;
|
|
539
|
+
this.agentVersionValue = undefined;
|
|
540
|
+
this.protocolVersionValue = undefined;
|
|
541
|
+
}
|
|
415
542
|
rejectPending(err) {
|
|
416
543
|
for (const { reject } of this.pending.values())
|
|
417
544
|
reject(err);
|