enhanced-printer 1.0.7 → 1.0.9
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 +67 -0
- package/lib/index.d.ts +2 -1
- package/lib/index.js +2 -1
- package/lib/job-tracker.d.ts +42 -0
- package/lib/job-tracker.js +65 -0
- package/lib/print-manager.d.ts +6 -0
- package/lib/print-manager.js +9 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,7 @@ printPDF();
|
|
|
59
59
|
| `getJobInfo()` | Get info of a specific job by custom job name |
|
|
60
60
|
| `getJobs()` | Get all print jobs for a printer |
|
|
61
61
|
| `cancelJob()` | Cancel a print job by custom job name |
|
|
62
|
+
| `watchJob()` | Poll a job until it completes, errors, or times out |
|
|
62
63
|
|
|
63
64
|
## API Reference
|
|
64
65
|
|
|
@@ -210,6 +211,72 @@ console.log('Cancelled:', cancelled);
|
|
|
210
211
|
|
|
211
212
|
---
|
|
212
213
|
|
|
214
|
+
### `watchJob(customJobName: string, printerName?: string, options?: WatchJobOptions): Promise<WatchJobResult>`
|
|
215
|
+
|
|
216
|
+
Polls a print job at regular intervals until it completes, enters an error state, is aborted, or times out.
|
|
217
|
+
|
|
218
|
+
> **Why use this?** Windows removes completed jobs from the print queue immediately. Without `watchJob`, polling `getJobInfo` after completion returns `null`, which is indistinguishable from an error. `watchJob` tracks the last known status and correctly resolves a vanished job as `COMPLETED`.
|
|
219
|
+
|
|
220
|
+
**Options:**
|
|
221
|
+
|
|
222
|
+
```typescript
|
|
223
|
+
interface WatchJobOptions {
|
|
224
|
+
pollInterval?: number; // ms between polls (default: 500)
|
|
225
|
+
timeout?: number; // max wait ms — 0 or omit = no timeout (default: 0)
|
|
226
|
+
onStatusChange?: (status: string[]) => void; // called on every status change
|
|
227
|
+
signal?: AbortSignal; // AbortController signal for manual stop
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Returns:**
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
interface WatchJobResult {
|
|
235
|
+
finalStatus: string[]; // e.g. ["COMPLETED"], ["ERROR"], ["ABORTED"]
|
|
236
|
+
completed: boolean; // true = success, false = error / timed out / aborted
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Example — basic watch with timeout:**
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
const { finalStatus, completed } = await watchJob('Invoice_12345', undefined, {
|
|
244
|
+
timeout: 30000, // stop after 30 seconds
|
|
245
|
+
pollInterval: 500,
|
|
246
|
+
onStatusChange: (status) => console.log('Status:', status)
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
console.log('Completed:', completed); // true
|
|
250
|
+
console.log('Final status:', finalStatus); // ["COMPLETED"]
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
**Example — watch indefinitely (no timeout):**
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
// timeout: 0 (default) = never times out, watches until job finishes or errors
|
|
257
|
+
const { finalStatus, completed } = await watchJob('Invoice_12345');
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**Example — manual stop via AbortController:**
|
|
261
|
+
|
|
262
|
+
```javascript
|
|
263
|
+
const controller = new AbortController();
|
|
264
|
+
|
|
265
|
+
// Start watching (doesn't block — run via Promise if needed)
|
|
266
|
+
const watchPromise = watchJob('Invoice_12345', undefined, {
|
|
267
|
+
signal: controller.signal,
|
|
268
|
+
onStatusChange: (status) => console.log('Status:', status)
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// Stop watching manually (e.g. user cancelled, component unmounted, etc.)
|
|
272
|
+
controller.abort();
|
|
273
|
+
|
|
274
|
+
const { finalStatus } = await watchPromise;
|
|
275
|
+
console.log(finalStatus); // ["ABORTED"]
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
213
280
|
## Usage Examples
|
|
214
281
|
|
|
215
282
|
### Using Printer Defaults
|
package/lib/index.d.ts
CHANGED
|
@@ -10,5 +10,6 @@
|
|
|
10
10
|
* - Automatic use of printer defaults when settings not specified
|
|
11
11
|
* - Query job status and printer information
|
|
12
12
|
*/
|
|
13
|
-
export { print, getPrinters, getDefaultPrinter, getJobInfo, getJobs, cancelJob } from './print-manager';
|
|
13
|
+
export { print, getPrinters, getDefaultPrinter, getJobInfo, getJobs, cancelJob, watchJob } from './print-manager';
|
|
14
14
|
export { PrintOptions, PrintResult, PrinterInfo, JobInfo } from './types';
|
|
15
|
+
export { WatchJobOptions, WatchJobResult } from './job-tracker';
|
package/lib/index.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* - Query job status and printer information
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.cancelJob = exports.getJobs = exports.getJobInfo = exports.getDefaultPrinter = exports.getPrinters = exports.print = void 0;
|
|
15
|
+
exports.watchJob = exports.cancelJob = exports.getJobs = exports.getJobInfo = exports.getDefaultPrinter = exports.getPrinters = exports.print = void 0;
|
|
16
16
|
var print_manager_1 = require("./print-manager");
|
|
17
17
|
Object.defineProperty(exports, "print", { enumerable: true, get: function () { return print_manager_1.print; } });
|
|
18
18
|
Object.defineProperty(exports, "getPrinters", { enumerable: true, get: function () { return print_manager_1.getPrinters; } });
|
|
@@ -20,3 +20,4 @@ Object.defineProperty(exports, "getDefaultPrinter", { enumerable: true, get: fun
|
|
|
20
20
|
Object.defineProperty(exports, "getJobInfo", { enumerable: true, get: function () { return print_manager_1.getJobInfo; } });
|
|
21
21
|
Object.defineProperty(exports, "getJobs", { enumerable: true, get: function () { return print_manager_1.getJobs; } });
|
|
22
22
|
Object.defineProperty(exports, "cancelJob", { enumerable: true, get: function () { return print_manager_1.cancelJob; } });
|
|
23
|
+
Object.defineProperty(exports, "watchJob", { enumerable: true, get: function () { return print_manager_1.watchJob; } });
|
package/lib/job-tracker.d.ts
CHANGED
|
@@ -12,6 +12,48 @@ export declare function getJobInfoBySystemId(printerName: string, jobId: number)
|
|
|
12
12
|
* Resolves the custom job name to the system job ID, then queries the spooler
|
|
13
13
|
*/
|
|
14
14
|
export declare function getJobInfo(customJobName: string, printerName?: string): Promise<JobInfo | null>;
|
|
15
|
+
export interface WatchJobResult {
|
|
16
|
+
/** Final resolved status of the job */
|
|
17
|
+
finalStatus: string[];
|
|
18
|
+
/** True if the job completed successfully, false if it errored or timed out */
|
|
19
|
+
completed: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface WatchJobOptions {
|
|
22
|
+
/** Milliseconds between each status poll (default: 500) */
|
|
23
|
+
pollInterval?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Maximum time to wait in milliseconds before giving up.
|
|
26
|
+
* Set to 0 or omit to watch indefinitely (no timeout).
|
|
27
|
+
* Default: 0 (no timeout)
|
|
28
|
+
*/
|
|
29
|
+
timeout?: number;
|
|
30
|
+
/** Called every time the job status changes */
|
|
31
|
+
onStatusChange?: (status: string[]) => void;
|
|
32
|
+
/**
|
|
33
|
+
* An AbortSignal to stop watching manually.
|
|
34
|
+
* Call controller.abort() on the corresponding AbortController to stop.
|
|
35
|
+
* @example
|
|
36
|
+
* const controller = new AbortController();
|
|
37
|
+
* watchJob('MyJob', undefined, { signal: controller.signal });
|
|
38
|
+
* // later...
|
|
39
|
+
* controller.abort(); // stops watching
|
|
40
|
+
*/
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Polls a print job until it finishes, is aborted, times out, or enters an error state.
|
|
45
|
+
*
|
|
46
|
+
* Handles the Windows spooler race condition where completed jobs are
|
|
47
|
+
* immediately removed from the queue (making them indistinguishable from
|
|
48
|
+
* missing jobs). If the job disappears and the last known status was not
|
|
49
|
+
* an error, it is treated as successfully COMPLETED.
|
|
50
|
+
*
|
|
51
|
+
* @example No timeout, manual stop via AbortController:
|
|
52
|
+
* const controller = new AbortController();
|
|
53
|
+
* const result = await watchJob('MyJob', undefined, { signal: controller.signal });
|
|
54
|
+
* // elsewhere: controller.abort();
|
|
55
|
+
*/
|
|
56
|
+
export declare function watchJob(customJobName: string, printerName?: string, options?: WatchJobOptions): Promise<WatchJobResult>;
|
|
15
57
|
/**
|
|
16
58
|
* Gets all print jobs for a printer with enhanced details
|
|
17
59
|
*/
|
package/lib/job-tracker.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.getJobIdByName = getJobIdByName;
|
|
4
4
|
exports.getJobInfoBySystemId = getJobInfoBySystemId;
|
|
5
5
|
exports.getJobInfo = getJobInfo;
|
|
6
|
+
exports.watchJob = watchJob;
|
|
6
7
|
exports.getAllJobs = getAllJobs;
|
|
7
8
|
const child_process_1 = require("child_process");
|
|
8
9
|
const job_name_mapper_1 = require("./job-name-mapper");
|
|
@@ -110,6 +111,70 @@ async function getJobInfo(customJobName, printerName) {
|
|
|
110
111
|
}
|
|
111
112
|
return getJobInfoBySystemId(mapping.printerName, mapping.jobId);
|
|
112
113
|
}
|
|
114
|
+
/** Status flags that indicate a job has stopped due to a problem */
|
|
115
|
+
const ERROR_STATUSES = ['ERROR', 'OFFLINE', 'PAPER_OUT', 'USER_INTERVENTION', 'DELETED', 'DELETING'];
|
|
116
|
+
/** Status flags that indicate a job has finished successfully */
|
|
117
|
+
const DONE_STATUSES = ['PRINTED', 'COMPLETE'];
|
|
118
|
+
/**
|
|
119
|
+
* Polls a print job until it finishes, is aborted, times out, or enters an error state.
|
|
120
|
+
*
|
|
121
|
+
* Handles the Windows spooler race condition where completed jobs are
|
|
122
|
+
* immediately removed from the queue (making them indistinguishable from
|
|
123
|
+
* missing jobs). If the job disappears and the last known status was not
|
|
124
|
+
* an error, it is treated as successfully COMPLETED.
|
|
125
|
+
*
|
|
126
|
+
* @example No timeout, manual stop via AbortController:
|
|
127
|
+
* const controller = new AbortController();
|
|
128
|
+
* const result = await watchJob('MyJob', undefined, { signal: controller.signal });
|
|
129
|
+
* // elsewhere: controller.abort();
|
|
130
|
+
*/
|
|
131
|
+
async function watchJob(customJobName, printerName, options = {}) {
|
|
132
|
+
const { pollInterval = 500, timeout = 0, onStatusChange, signal } = options;
|
|
133
|
+
const hasTimeout = timeout > 0;
|
|
134
|
+
const deadline = hasTimeout ? Date.now() + timeout : Infinity;
|
|
135
|
+
let lastKnownStatus = ['INQUEUE'];
|
|
136
|
+
while (Date.now() < deadline) {
|
|
137
|
+
// Check if manually aborted
|
|
138
|
+
if (signal?.aborted) {
|
|
139
|
+
return { finalStatus: ['ABORTED'], completed: false };
|
|
140
|
+
}
|
|
141
|
+
const jobInfo = await getJobInfo(customJobName, printerName);
|
|
142
|
+
if (jobInfo === null) {
|
|
143
|
+
// Job has disappeared from the queue.
|
|
144
|
+
// If the last known status was a hard error, surface that.
|
|
145
|
+
// Otherwise, Windows removed it because it finished — treat as COMPLETED.
|
|
146
|
+
const hadError = lastKnownStatus.some(s => ERROR_STATUSES.includes(s));
|
|
147
|
+
return {
|
|
148
|
+
finalStatus: hadError ? lastKnownStatus : ['COMPLETED'],
|
|
149
|
+
completed: !hadError
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
const currentStatus = jobInfo.status;
|
|
153
|
+
// Notify caller if status changed
|
|
154
|
+
if (JSON.stringify(currentStatus) !== JSON.stringify(lastKnownStatus)) {
|
|
155
|
+
lastKnownStatus = currentStatus;
|
|
156
|
+
onStatusChange?.(currentStatus);
|
|
157
|
+
}
|
|
158
|
+
// Job explicitly marked as done
|
|
159
|
+
if (currentStatus.some(s => DONE_STATUSES.includes(s))) {
|
|
160
|
+
return { finalStatus: ['COMPLETED'], completed: true };
|
|
161
|
+
}
|
|
162
|
+
// Job is in a hard-error state — stop polling
|
|
163
|
+
if (currentStatus.some(s => ERROR_STATUSES.includes(s))) {
|
|
164
|
+
return { finalStatus: currentStatus, completed: false };
|
|
165
|
+
}
|
|
166
|
+
// Sleep for pollInterval, but wake early if aborted
|
|
167
|
+
await new Promise(resolve => {
|
|
168
|
+
const timer = setTimeout(resolve, pollInterval);
|
|
169
|
+
signal?.addEventListener('abort', () => {
|
|
170
|
+
clearTimeout(timer);
|
|
171
|
+
resolve();
|
|
172
|
+
}, { once: true });
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Timed out
|
|
176
|
+
return { finalStatus: lastKnownStatus, completed: false };
|
|
177
|
+
}
|
|
113
178
|
/**
|
|
114
179
|
* Gets all print jobs for a printer with enhanced details
|
|
115
180
|
*/
|
package/lib/print-manager.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PrintOptions, PrintResult, PrinterInfo, JobInfo } from './types';
|
|
2
|
+
import { WatchJobOptions, WatchJobResult } from './job-tracker';
|
|
2
3
|
/**
|
|
3
4
|
* Prints a PDF file with the specified options
|
|
4
5
|
*/
|
|
@@ -23,3 +24,8 @@ export declare function getJobs(printerName: string): Promise<JobInfo[]>;
|
|
|
23
24
|
* Cancels a print job using custom job name
|
|
24
25
|
*/
|
|
25
26
|
export declare function cancelJob(customJobName: string, printerName?: string): Promise<boolean>;
|
|
27
|
+
/**
|
|
28
|
+
* Watches a print job until it completes, errors, or times out.
|
|
29
|
+
* Handles the Windows spooler race condition where completed jobs disappear from the queue.
|
|
30
|
+
*/
|
|
31
|
+
export declare function watchJob(customJobName: string, printerName?: string, options?: WatchJobOptions): Promise<WatchJobResult>;
|
package/lib/print-manager.js
CHANGED
|
@@ -6,6 +6,7 @@ exports.getDefaultPrinter = getDefaultPrinter;
|
|
|
6
6
|
exports.getJobInfo = getJobInfo;
|
|
7
7
|
exports.getJobs = getJobs;
|
|
8
8
|
exports.cancelJob = cancelJob;
|
|
9
|
+
exports.watchJob = watchJob;
|
|
9
10
|
const pdf_to_printer_1 = require("pdf-to-printer");
|
|
10
11
|
const uuid_1 = require("uuid");
|
|
11
12
|
const child_process_1 = require("child_process");
|
|
@@ -150,6 +151,13 @@ async function cancelJob(customJobName, printerName) {
|
|
|
150
151
|
return false;
|
|
151
152
|
}
|
|
152
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Watches a print job until it completes, errors, or times out.
|
|
156
|
+
* Handles the Windows spooler race condition where completed jobs disappear from the queue.
|
|
157
|
+
*/
|
|
158
|
+
async function watchJob(customJobName, printerName, options = {}) {
|
|
159
|
+
return (0, job_tracker_1.watchJob)(customJobName, printerName, options);
|
|
160
|
+
}
|
|
153
161
|
/**
|
|
154
162
|
* Executes a PowerShell script and returns output
|
|
155
163
|
*/
|
|
@@ -170,7 +178,7 @@ function executePowerShell(script) {
|
|
|
170
178
|
errorOutput += data.toString();
|
|
171
179
|
});
|
|
172
180
|
ps.on('close', (code) => {
|
|
173
|
-
if (code === 0
|
|
181
|
+
if (code === 0) {
|
|
174
182
|
resolve(output.trim());
|
|
175
183
|
}
|
|
176
184
|
else {
|
package/package.json
CHANGED