@gohcltech/edge-print-client 2.0.89-develop → 2.0.108-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 CHANGED
@@ -76,6 +76,7 @@ await ep.connect({
76
76
  | `port` | `number` | `8181` | Port the agent listens on |
77
77
  | `token` | `string` | _(required)_ | API token from the Edge Printing settings window |
78
78
  | `clientName` | `string?` | _(none)_ | Name shown in the agent's Clients tab and approval prompt |
79
+ | `requestApproval` | `boolean?` | _(none)_ | Whether an unapproved token asks the user for approval — see below |
79
80
  | `retries` | `number` | `3` | Max additional connection attempts after the first failure |
80
81
  | `retryDelay` | `number` | `1000` | Ms to wait between retry attempts |
81
82
 
@@ -85,6 +86,35 @@ asked to trust an "Unknown client".
85
86
 
86
87
  Throws if the agent is unreachable or the token is rejected after all retries.
87
88
 
89
+ #### Asking for approval
90
+
91
+ An application can connect with a token it generated itself and have the user
92
+ approve it in Edge Printing, instead of having them paste a token in. Set
93
+ `requestApproval` to say when that should prompt the user:
94
+
95
+ - **`true`** when the user has asked to connect, for example from a "Connect to
96
+ Edge Printing" button. The request appears on the agent's Clients tab, and
97
+ `connect()` rejects with `pending_approval` until the user approves it.
98
+ - **`false`** on every automatic connect, such as page load or a reconnect.
99
+ Nobody is prompted. `connect()` rejects with `pending_approval` if a
100
+ request is already waiting, or `not_approved` if there is none: never asked,
101
+ dismissed or revoked.
102
+ - **Omitted:** every unapproved connect prompts and is refused with
103
+ `invalid_token`, as before this option existed.
104
+
105
+ `not_approved` is thrown without retrying, since waiting cannot change it.
106
+ `pending_approval` is retried, and `connect()` succeeds if the user approves
107
+ in time. Within one `connect()`, `true` is only sent until the agent confirms
108
+ the request. Retries send `false`, so a request the user dismisses is not
109
+ put back.
110
+
111
+ An agent older than this option ignores it and answers `invalid_token`.
112
+
113
+ Revoking an application on the Clients tab closes its open connections. The
114
+ client sees the close, and its next connect is treated like any unapproved
115
+ token: refused with `not_approved` under `requestApproval: false`, or put back
116
+ on the Clients tab as a new request under `true` or with the option omitted.
117
+
88
118
  The client sends its wire protocol version automatically as part of the auth
89
119
  handshake; it is not a `connect()` option. If the agent speaks a different
90
120
  version, `connect()` throws a message naming both versions and which side to
@@ -550,7 +580,9 @@ Sent by the agent:
550
580
  | Code | Raised when |
551
581
  |---|---|
552
582
  | `not_authenticated` | A request was sent before `connect()` finished authenticating |
553
- | `invalid_token` | The token is unknown, or still awaiting approval in the agent UI |
583
+ | `invalid_token` | The token is unknown, or still awaiting approval in the agent UI. Only when `requestApproval` was not set |
584
+ | `pending_approval` | The token is waiting for the user to approve it on the agent's Clients tab |
585
+ | `not_approved` | The token is not approved and nobody has been asked: never requested, dismissed or revoked. Connect with `requestApproval: true` to ask |
554
586
  | `invalid_request` | The agent could not parse the request |
555
587
  | `unsupported_value` | `paperSize`, `tray` or `mediaType` names a value the printer does not list. Carries `supported` |
556
588
  | `unsupported_capability` | A capability the printer explicitly reports it lacks |
@@ -685,7 +717,17 @@ If you see a security warning, the certificate is not yet trusted.
685
717
 
686
718
  The token was rejected, or it is new and still awaiting approval in the agent's
687
719
  Clients tab. Approve it there, or generate a fresh one from the Edge Printing
688
- settings window.
720
+ settings window. Setting `requestApproval` tells the two cases apart.
721
+
722
+ ### `connect()` rejects with `code: 'pending_approval'`
723
+
724
+ The request is on the agent's Clients tab, waiting for the user. Tell them to
725
+ approve it there, then connect again with `requestApproval: false`.
726
+
727
+ ### `connect()` rejects with `code: 'not_approved'`
728
+
729
+ The user has not been asked, or dismissed or revoked the request. Offer to ask
730
+ again, and connect with `requestApproval: true` when they accept.
689
731
 
690
732
  ### `connect()` rejects with `code: 'protocol_mismatch'`
691
733
 
@@ -362,6 +362,22 @@ export interface ConnectOptions {
362
362
  * "Unknown client", which is not much of a decision.
363
363
  */
364
364
  clientName?: string;
365
+ /**
366
+ * Whether an unapproved token should ask the user for approval.
367
+ *
368
+ * - `true` — put this application on the agent's Clients tab for the user
369
+ * to approve. Rejects with `pending_approval`. Use only when the user has
370
+ * asked to connect.
371
+ * - `false` — ask nobody. Rejects with `pending_approval` if a request is
372
+ * already waiting, or `not_approved` if there is none (never asked,
373
+ * dismissed or revoked). Use on every automatic connect, so a dismissed
374
+ * request does not come back by itself.
375
+ * - omitted — the agent's original behaviour: every unapproved connect
376
+ * asks, and is refused with `invalid_token`.
377
+ *
378
+ * An agent older than this option ignores it and answers `invalid_token`.
379
+ */
380
+ requestApproval?: boolean;
365
381
  /**
366
382
  * Maximum number of additional connection attempts after the first failure.
367
383
  * @default 3
@@ -373,7 +389,7 @@ export interface ConnectOptions {
373
389
  */
374
390
  retryDelay?: number;
375
391
  }
376
- declare const ERROR_CODES: readonly ["not_authenticated", "invalid_token", "protocol_mismatch", "invalid_request", "unsupported_value", "unsupported_capability", "invalid_geometry", "content_overflow", "unsupported_for_path", "encrypted_pdf", "printer_not_found", "print_failed", "internal_error", "not_connected", "unreachable", "connection_closed", "timeout", "unknown"];
392
+ declare const ERROR_CODES: readonly ["not_authenticated", "invalid_token", "pending_approval", "not_approved", "protocol_mismatch", "invalid_request", "unsupported_value", "unsupported_capability", "invalid_geometry", "content_overflow", "unsupported_for_path", "encrypted_pdf", "printer_not_found", "print_failed", "internal_error", "not_connected", "unreachable", "connection_closed", "timeout", "unknown"];
377
393
  export type EdgePrintErrorCode = typeof ERROR_CODES[number];
378
394
  /** A warning the agent recorded about a job it nonetheless printed. */
379
395
  export interface JobWarning {
@@ -479,7 +495,8 @@ export declare class EdgePrintClient {
479
495
  *
480
496
  * On failure the client retries up to `options.retries` times (default 3),
481
497
  * waiting `options.retryDelay` ms (default 1 000) between attempts. If all
482
- * attempts fail the last error is re-thrown.
498
+ * attempts fail the last error is re-thrown. A `not_approved` refusal is
499
+ * thrown at once: nobody has been asked, so waiting cannot change it.
483
500
  *
484
501
  * Calling this on an already-connected client **replaces** the connection:
485
502
  * the existing socket is torn down before the new one is attempted, and its
@@ -33,7 +33,8 @@ export const inches = (m) => ({ ...m, units: 'in' });
33
33
  * does not recognise, which is what lets a new client work against an old agent.
34
34
  */
35
35
  const AGENT_CODES = [
36
- 'not_authenticated', 'invalid_token', 'protocol_mismatch', 'invalid_request',
36
+ 'not_authenticated', 'invalid_token', 'pending_approval', 'not_approved',
37
+ 'protocol_mismatch', 'invalid_request',
37
38
  'unsupported_value', 'unsupported_capability', 'invalid_geometry',
38
39
  'content_overflow', 'unsupported_for_path', 'encrypted_pdf',
39
40
  'printer_not_found', 'print_failed', 'internal_error',
@@ -212,7 +213,8 @@ export class EdgePrintClient {
212
213
  *
213
214
  * On failure the client retries up to `options.retries` times (default 3),
214
215
  * waiting `options.retryDelay` ms (default 1 000) between attempts. If all
215
- * attempts fail the last error is re-thrown.
216
+ * attempts fail the last error is re-thrown. A `not_approved` refusal is
217
+ * thrown at once: nobody has been asked, so waiting cannot change it.
216
218
  *
217
219
  * Calling this on an already-connected client **replaces** the connection:
218
220
  * the existing socket is torn down before the new one is attempted, and its
@@ -236,7 +238,11 @@ export class EdgePrintClient {
236
238
  * ```
237
239
  */
238
240
  async connect(options) {
239
- const { host = '127.0.0.1', port = 8181, token, clientName, retries = 3, retryDelay = 1000, } = options;
241
+ const { host = '127.0.0.1', port = 8181, token, clientName, requestApproval, retries = 3, retryDelay = 1000, } = options;
242
+ // Asks on the first attempt that reaches the agent, and not again: if the
243
+ // user dismissed the request while this was retrying, asking on the next
244
+ // attempt would put it straight back in front of them.
245
+ let ask = requestApproval;
240
246
  // A negative count would skip the loop altogether and resolve without ever
241
247
  // opening a socket, leaving the caller believing it is connected while
242
248
  // every later call rejects with "Not connected".
@@ -260,6 +266,8 @@ export class EdgePrintClient {
260
266
  token,
261
267
  protocolVersion: PROTOCOL_VERSION,
262
268
  ...(clientName ? { clientName } : {}),
269
+ // Omitted when unset, which an agent reads as the original behaviour.
270
+ ...(typeof ask === 'boolean' ? { requestApproval: ask } : {}),
263
271
  };
264
272
  const ack = await this.request('auth', auth);
265
273
  // An agent that predates the handshake answers with a bare `auth_ok`.
@@ -300,11 +308,22 @@ export class EdgePrintClient {
300
308
  if (this.connectGeneration !== generation)
301
309
  throw err;
302
310
  this.discardSocket(new EdgePrintError('connection_closed', 'Connection closed'));
303
- // Every failure retries, including a refused token. The agent answers a
304
- // token still awaiting approval with the same "Invalid token" it gives
305
- // a bad one, so the client cannot tell them apart — and retrying while
306
- // someone clicks Approve is the documented flow. Repeat attempts only
307
- // bump a counter on the pending entry; the notification fires once.
311
+ // Nobody has been asked, so waiting cannot change the answer.
312
+ if (err instanceof EdgePrintError && err.code === 'not_approved') {
313
+ this.markClosed();
314
+ throw err;
315
+ }
316
+ if (err instanceof EdgePrintError && err.code === 'pending_approval') {
317
+ ask = false;
318
+ }
319
+ // Every other failure retries, including a refused token. A token
320
+ // awaiting approval may be approved before the next attempt — and an
321
+ // older agent answers that with the same `invalid_token` it gives a bad
322
+ // one, so the client cannot tell them apart. Once the agent confirms a
323
+ // request with `pending_approval`, retries send `requestApproval:
324
+ // false`, so they only check on it and a dismissal sticks. Without the
325
+ // option, repeat attempts bump a counter on the pending entry; either
326
+ // way the notification fires once.
308
327
  if (attempt === attempts) {
309
328
  this.markClosed();
310
329
  throw err;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gohcltech/edge-print-client",
3
- "version": "2.0.89-develop",
3
+ "version": "2.0.108-develop",
4
4
  "description": "Browser client for the Edge Printing WebSocket agent",
5
5
  "license": "MIT",
6
6
  "repository": {