@crowsgear/escl-protocol-scanner 0.1.0
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/LICENSE +21 -0
- package/README.md +663 -0
- package/dist/client.d.ts +69 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +320 -0
- package/dist/client.js.map +1 -0
- package/dist/discovery.d.ts +71 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +269 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +250 -0
- package/dist/index.js.map +1 -0
- package/dist/types.d.ts +170 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +63 -0
- package/python/base.py +57 -0
- package/python/escl_backend.py +541 -0
- package/python/escl_main.py +119 -0
- package/scripts/check-python-deps.js +185 -0
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eSCL Protocol Client for Node.js
|
|
3
|
+
* HTTP client for communicating with eSCL/AirPrint network scanners
|
|
4
|
+
*/
|
|
5
|
+
import { ESCLScanner, ESCLCapabilities } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* eSCL HTTP Client
|
|
8
|
+
* Handles all HTTP communication with eSCL-compatible network scanners
|
|
9
|
+
*/
|
|
10
|
+
export declare class ESCLClient {
|
|
11
|
+
private timeout;
|
|
12
|
+
constructor(timeout?: number);
|
|
13
|
+
/**
|
|
14
|
+
* Get scanner capabilities
|
|
15
|
+
* @param scanner Target scanner
|
|
16
|
+
* @param debug Enable debug logging (default: false)
|
|
17
|
+
* @returns Scanner capabilities
|
|
18
|
+
*/
|
|
19
|
+
getCapabilities(scanner: ESCLScanner, debug?: boolean): Promise<ESCLCapabilities | null>;
|
|
20
|
+
/**
|
|
21
|
+
* Start scan job
|
|
22
|
+
* @param scanner Target scanner
|
|
23
|
+
* @param dpi Resolution in DPI
|
|
24
|
+
* @param colorMode Color mode (BlackAndWhite1, Grayscale8, RGB24)
|
|
25
|
+
* @param source Scan source (Platen, Feeder)
|
|
26
|
+
* @returns Scan job UUID
|
|
27
|
+
*/
|
|
28
|
+
createScanJob(scanner: ESCLScanner, dpi: number, colorMode: string, source: string): Promise<string | null>;
|
|
29
|
+
/**
|
|
30
|
+
* Poll scan job status
|
|
31
|
+
* @param scanner Target scanner
|
|
32
|
+
* @param jobId Scan job ID
|
|
33
|
+
* @returns Job status and image URLs if complete
|
|
34
|
+
*/
|
|
35
|
+
getScanJobStatus(scanner: ESCLScanner, jobId: string): Promise<{
|
|
36
|
+
status: 'Processing' | 'Completed' | 'Aborted' | 'Unknown';
|
|
37
|
+
images: string[];
|
|
38
|
+
}>;
|
|
39
|
+
/**
|
|
40
|
+
* Download scanned image
|
|
41
|
+
* @param scanner Target scanner
|
|
42
|
+
* @param imageUrl Relative image URL
|
|
43
|
+
* @returns PNG image data as Buffer
|
|
44
|
+
*/
|
|
45
|
+
downloadImage(scanner: ESCLScanner, imageUrl: string): Promise<Buffer | null>;
|
|
46
|
+
/**
|
|
47
|
+
* Build eSCL scan settings XML
|
|
48
|
+
*/
|
|
49
|
+
private buildScanSettings;
|
|
50
|
+
/**
|
|
51
|
+
* Parse scanner capabilities from XML response
|
|
52
|
+
* @param xml XML response from scanner
|
|
53
|
+
* @param debug Enable debug logging (default: false)
|
|
54
|
+
*/
|
|
55
|
+
private parseCapabilities;
|
|
56
|
+
/**
|
|
57
|
+
* HTTP GET request
|
|
58
|
+
*/
|
|
59
|
+
private httpGet;
|
|
60
|
+
/**
|
|
61
|
+
* HTTP GET request (binary)
|
|
62
|
+
*/
|
|
63
|
+
private httpGetBinary;
|
|
64
|
+
/**
|
|
65
|
+
* HTTP POST request
|
|
66
|
+
*/
|
|
67
|
+
private httpPost;
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAgB,MAAM,SAAS,CAAC;AAEtE;;;GAGG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAiB;gBAEpB,OAAO,CAAC,EAAE,MAAM;IAM5B;;;;;OAKG;IACG,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,GAAE,OAAe,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAarG;;;;;;;OAOG;IACG,aAAa,CACjB,OAAO,EAAE,WAAW,EACpB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAqBzB;;;;;OAKG;IACG,gBAAgB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QACnE,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC;QAC3D,MAAM,EAAE,MAAM,EAAE,CAAC;KAClB,CAAC;IAyBF;;;;;OAKG;IACG,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAUnF;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA8BzB;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAqEzB;;OAEG;IACH,OAAO,CAAC,OAAO;IAyBf;;OAEG;IACH,OAAO,CAAC,aAAa;IAyBrB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAmCjB"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* eSCL Protocol Client for Node.js
|
|
4
|
+
* HTTP client for communicating with eSCL/AirPrint network scanners
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.ESCLClient = void 0;
|
|
41
|
+
const http = __importStar(require("http"));
|
|
42
|
+
/**
|
|
43
|
+
* eSCL HTTP Client
|
|
44
|
+
* Handles all HTTP communication with eSCL-compatible network scanners
|
|
45
|
+
*/
|
|
46
|
+
class ESCLClient {
|
|
47
|
+
constructor(timeout) {
|
|
48
|
+
this.timeout = 10000;
|
|
49
|
+
if (timeout) {
|
|
50
|
+
this.timeout = timeout;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get scanner capabilities
|
|
55
|
+
* @param scanner Target scanner
|
|
56
|
+
* @param debug Enable debug logging (default: false)
|
|
57
|
+
* @returns Scanner capabilities
|
|
58
|
+
*/
|
|
59
|
+
async getCapabilities(scanner, debug = false) {
|
|
60
|
+
try {
|
|
61
|
+
const response = await this.httpGet(`http://${scanner.host}:${scanner.port}/eSCL/ScannerCapabilities`);
|
|
62
|
+
return this.parseCapabilities(response, debug);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
console.error(`Failed to get capabilities for ${scanner.name}:`, error);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Start scan job
|
|
71
|
+
* @param scanner Target scanner
|
|
72
|
+
* @param dpi Resolution in DPI
|
|
73
|
+
* @param colorMode Color mode (BlackAndWhite1, Grayscale8, RGB24)
|
|
74
|
+
* @param source Scan source (Platen, Feeder)
|
|
75
|
+
* @returns Scan job UUID
|
|
76
|
+
*/
|
|
77
|
+
async createScanJob(scanner, dpi, colorMode, source) {
|
|
78
|
+
try {
|
|
79
|
+
const scanSettings = this.buildScanSettings(dpi, colorMode, source);
|
|
80
|
+
const jobResponse = await this.httpPost(`http://${scanner.host}:${scanner.port}/eSCL/ScanJobs`, scanSettings);
|
|
81
|
+
// Extract job UUID from Location header or response
|
|
82
|
+
const jobMatch = jobResponse.match(/\/eSCL\/ScanJobs\/([a-f0-9-]+)/);
|
|
83
|
+
if (jobMatch) {
|
|
84
|
+
return jobMatch[1];
|
|
85
|
+
}
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error(`Failed to create scan job on ${scanner.name}:`, error);
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Poll scan job status
|
|
95
|
+
* @param scanner Target scanner
|
|
96
|
+
* @param jobId Scan job ID
|
|
97
|
+
* @returns Job status and image URLs if complete
|
|
98
|
+
*/
|
|
99
|
+
async getScanJobStatus(scanner, jobId) {
|
|
100
|
+
try {
|
|
101
|
+
const response = await this.httpGet(`http://${scanner.host}:${scanner.port}/eSCL/ScanJobs/${jobId}`);
|
|
102
|
+
const status = response.match(/<JobState>(\w+)<\/JobState>/)?.[1] || 'Unknown';
|
|
103
|
+
const images = [];
|
|
104
|
+
// Extract image URLs
|
|
105
|
+
const imageMatches = response.matchAll(/<DocumentURI>([^<]+)<\/DocumentURI>/g);
|
|
106
|
+
for (const match of imageMatches) {
|
|
107
|
+
images.push(match[1]);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
status: status,
|
|
111
|
+
images
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
console.error(`Failed to get scan job status:`, error);
|
|
116
|
+
return { status: 'Unknown', images: [] };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Download scanned image
|
|
121
|
+
* @param scanner Target scanner
|
|
122
|
+
* @param imageUrl Relative image URL
|
|
123
|
+
* @returns PNG image data as Buffer
|
|
124
|
+
*/
|
|
125
|
+
async downloadImage(scanner, imageUrl) {
|
|
126
|
+
try {
|
|
127
|
+
const fullUrl = `http://${scanner.host}:${scanner.port}${imageUrl}`;
|
|
128
|
+
return await this.httpGetBinary(fullUrl);
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error(`Failed to download image:`, error);
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Build eSCL scan settings XML
|
|
137
|
+
*/
|
|
138
|
+
buildScanSettings(dpi, colorMode, source) {
|
|
139
|
+
// Match the Python implementation which uses proper namespaces
|
|
140
|
+
const ESCL_NS = 'http://schemas.hp.com/imaging/escl/2011/05/03';
|
|
141
|
+
const PWG_NS = 'http://www.pwg.org/schemas/2010/12/sm';
|
|
142
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
143
|
+
<scan:ScanSettings xmlns:scan="${ESCL_NS}" xmlns:pwg="${PWG_NS}">
|
|
144
|
+
<pwg:Version>2.0</pwg:Version>
|
|
145
|
+
<scan:Intent>Document</scan:Intent>
|
|
146
|
+
<pwg:ScanRegions>
|
|
147
|
+
<pwg:ScanRegion>
|
|
148
|
+
<pwg:ContentRegionUnits>escl:ThreeHundredthsOfInches</pwg:ContentRegionUnits>
|
|
149
|
+
<pwg:XOffset>0</pwg:XOffset>
|
|
150
|
+
<pwg:YOffset>0</pwg:YOffset>
|
|
151
|
+
<pwg:Width>3508</pwg:Width>
|
|
152
|
+
<pwg:Height>4961</pwg:Height>
|
|
153
|
+
</pwg:ScanRegion>
|
|
154
|
+
</pwg:ScanRegions>
|
|
155
|
+
<scan:Justification>
|
|
156
|
+
<pwg:XImagePosition>Center</pwg:XImagePosition>
|
|
157
|
+
<pwg:YImagePosition>Center</pwg:YImagePosition>
|
|
158
|
+
</scan:Justification>
|
|
159
|
+
<pwg:InputSource>${source}</pwg:InputSource>
|
|
160
|
+
<scan:ColorMode>${colorMode}</scan:ColorMode>
|
|
161
|
+
<scan:XResolution>${dpi}</scan:XResolution>
|
|
162
|
+
<scan:YResolution>${dpi}</scan:YResolution>
|
|
163
|
+
<pwg:DocumentFormat>image/jpeg</pwg:DocumentFormat>
|
|
164
|
+
</scan:ScanSettings>`;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Parse scanner capabilities from XML response
|
|
168
|
+
* @param xml XML response from scanner
|
|
169
|
+
* @param debug Enable debug logging (default: false)
|
|
170
|
+
*/
|
|
171
|
+
parseCapabilities(xml, debug = false) {
|
|
172
|
+
const resolutions = [];
|
|
173
|
+
const colorModes = [];
|
|
174
|
+
const sources = [];
|
|
175
|
+
// Debug: Print raw XML for inspection (only if debug enabled)
|
|
176
|
+
if (debug) {
|
|
177
|
+
console.log('[eSCL] Capabilities XML response (full):');
|
|
178
|
+
console.log(xml);
|
|
179
|
+
}
|
|
180
|
+
// eSCL uses XML namespaces, need to handle scan: and pwg: prefixes
|
|
181
|
+
// Extract resolutions - look for XResolution inside DiscreteResolution
|
|
182
|
+
const discreteResMatches = xml.matchAll(/<[a-z]*:?DiscreteResolution>[\s\S]*?<[a-z]*:?XResolution>(\d+)<\/[a-z]*:?XResolution>[\s\S]*?<\/[a-z]*:?DiscreteResolution>/g);
|
|
183
|
+
for (const match of discreteResMatches) {
|
|
184
|
+
resolutions.push(parseInt(match[1], 10));
|
|
185
|
+
}
|
|
186
|
+
// Also try simpler pattern for XResolution
|
|
187
|
+
if (resolutions.length === 0) {
|
|
188
|
+
const xResMatches = xml.matchAll(/<[a-z]*:?XResolution>(\d+)<\/[a-z]*:?XResolution>/g);
|
|
189
|
+
for (const match of xResMatches) {
|
|
190
|
+
const res = parseInt(match[1], 10);
|
|
191
|
+
if (!resolutions.includes(res)) {
|
|
192
|
+
resolutions.push(res);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Extract color modes - handle namespaced tags
|
|
197
|
+
const colorMatches = xml.matchAll(/<[a-z]*:?ColorMode>(\w+)<\/[a-z]*:?ColorMode>/g);
|
|
198
|
+
for (const match of colorMatches) {
|
|
199
|
+
const mode = match[1];
|
|
200
|
+
if (['BlackAndWhite1', 'Grayscale8', 'RGB24'].includes(mode)) {
|
|
201
|
+
colorModes.push(mode);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
// Extract sources - look for InputSource tags or check for Platen/Adf elements
|
|
205
|
+
const sourceMatches = xml.matchAll(/<[a-z]*:?InputSource>(\w+)<\/[a-z]*:?InputSource>/g);
|
|
206
|
+
for (const match of sourceMatches) {
|
|
207
|
+
const source = match[1];
|
|
208
|
+
if (['Platen', 'Adf', 'Feeder'].includes(source)) {
|
|
209
|
+
sources.push(source);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
// If no InputSource tags found, check for Platen/Adf elements directly
|
|
213
|
+
if (sources.length === 0) {
|
|
214
|
+
if (/<[a-z]*:?Platen[\s>]/.test(xml)) {
|
|
215
|
+
sources.push('Platen');
|
|
216
|
+
}
|
|
217
|
+
if (/<[a-z]*:?Adf[\s>]/.test(xml)) {
|
|
218
|
+
sources.push('Adf');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
// Remove duplicates from resolutions
|
|
222
|
+
const uniqueResolutions = Array.from(new Set(resolutions)).sort((a, b) => a - b);
|
|
223
|
+
const uniqueColorModes = Array.from(new Set(colorModes));
|
|
224
|
+
const uniqueSources = Array.from(new Set(sources));
|
|
225
|
+
if (debug) {
|
|
226
|
+
console.log('[eSCL] Parsed capabilities:', { resolutions: uniqueResolutions, colorModes: uniqueColorModes, sources: uniqueSources });
|
|
227
|
+
}
|
|
228
|
+
return { resolutions: uniqueResolutions, colorModes: uniqueColorModes, sources: uniqueSources };
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* HTTP GET request
|
|
232
|
+
*/
|
|
233
|
+
httpGet(url) {
|
|
234
|
+
return new Promise((resolve, reject) => {
|
|
235
|
+
const req = http.get(url, { timeout: this.timeout }, (res) => {
|
|
236
|
+
let data = '';
|
|
237
|
+
res.on('data', (chunk) => {
|
|
238
|
+
data += chunk;
|
|
239
|
+
});
|
|
240
|
+
res.on('end', () => {
|
|
241
|
+
if (res.statusCode === 200) {
|
|
242
|
+
resolve(data);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
246
|
+
}
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
req.on('timeout', () => {
|
|
250
|
+
req.destroy();
|
|
251
|
+
reject(new Error('Request timeout'));
|
|
252
|
+
});
|
|
253
|
+
req.on('error', reject);
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* HTTP GET request (binary)
|
|
258
|
+
*/
|
|
259
|
+
httpGetBinary(url) {
|
|
260
|
+
return new Promise((resolve, reject) => {
|
|
261
|
+
const req = http.get(url, { timeout: this.timeout }, (res) => {
|
|
262
|
+
const chunks = [];
|
|
263
|
+
res.on('data', (chunk) => {
|
|
264
|
+
chunks.push(chunk);
|
|
265
|
+
});
|
|
266
|
+
res.on('end', () => {
|
|
267
|
+
if (res.statusCode === 200) {
|
|
268
|
+
resolve(Buffer.concat(chunks));
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
272
|
+
}
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
req.on('timeout', () => {
|
|
276
|
+
req.destroy();
|
|
277
|
+
reject(new Error('Request timeout'));
|
|
278
|
+
});
|
|
279
|
+
req.on('error', reject);
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* HTTP POST request
|
|
284
|
+
*/
|
|
285
|
+
httpPost(url, body) {
|
|
286
|
+
return new Promise((resolve, reject) => {
|
|
287
|
+
const options = {
|
|
288
|
+
method: 'POST',
|
|
289
|
+
headers: {
|
|
290
|
+
'Content-Type': 'application/xml',
|
|
291
|
+
'Content-Length': Buffer.byteLength(body)
|
|
292
|
+
},
|
|
293
|
+
timeout: this.timeout
|
|
294
|
+
};
|
|
295
|
+
const req = http.request(url, options, (res) => {
|
|
296
|
+
let data = '';
|
|
297
|
+
res.on('data', (chunk) => {
|
|
298
|
+
data += chunk;
|
|
299
|
+
});
|
|
300
|
+
res.on('end', () => {
|
|
301
|
+
if (res.statusCode === 201 || res.statusCode === 200) {
|
|
302
|
+
resolve(data || res.headers.location || '');
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
reject(new Error(`HTTP ${res.statusCode}`));
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
req.on('timeout', () => {
|
|
310
|
+
req.destroy();
|
|
311
|
+
reject(new Error('Request timeout'));
|
|
312
|
+
});
|
|
313
|
+
req.on('error', reject);
|
|
314
|
+
req.write(body);
|
|
315
|
+
req.end();
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
exports.ESCLClient = ESCLClient;
|
|
320
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* eSCL Scanner Discovery via Python Subprocess
|
|
3
|
+
* Uses Python's zeroconf library for mDNS discovery
|
|
4
|
+
*/
|
|
5
|
+
import { ESCLScanner, DiscoveryResponse } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* eSCL Scanner Discovery Service Options
|
|
8
|
+
*/
|
|
9
|
+
export interface ESCLDiscoveryOptions {
|
|
10
|
+
/** Custom Python executable path (e.g., /path/to/venv/bin/python3) */
|
|
11
|
+
pythonPath?: string;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* eSCL Scanner Discovery Service
|
|
15
|
+
* Spawns Python subprocess to handle mDNS discovery using zeroconf
|
|
16
|
+
*/
|
|
17
|
+
export declare class ESCLDiscovery {
|
|
18
|
+
private pythonProcess;
|
|
19
|
+
private discovered;
|
|
20
|
+
private listeners;
|
|
21
|
+
private timeout;
|
|
22
|
+
private processReady;
|
|
23
|
+
private pythonPath;
|
|
24
|
+
constructor(timeout?: number, options?: ESCLDiscoveryOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Validate Python path exists and is executable
|
|
27
|
+
* @throws Error if Python path is invalid
|
|
28
|
+
*/
|
|
29
|
+
private validatePythonPath;
|
|
30
|
+
/**
|
|
31
|
+
* Start discovering scanners
|
|
32
|
+
* @param timeout Optional timeout in milliseconds (default: 5000ms)
|
|
33
|
+
* @returns Promise resolving with discovery response containing success status and scanner data
|
|
34
|
+
*/
|
|
35
|
+
startDiscovery(timeout?: number): Promise<DiscoveryResponse>;
|
|
36
|
+
/**
|
|
37
|
+
* Stop active discovery and cleanup subprocess
|
|
38
|
+
*/
|
|
39
|
+
stopDiscovery(): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get currently discovered scanners
|
|
42
|
+
*/
|
|
43
|
+
getScanners(): ESCLScanner[];
|
|
44
|
+
/**
|
|
45
|
+
* Subscribe to scanner discovery updates
|
|
46
|
+
*/
|
|
47
|
+
onScannerDiscovered(callback: (scanners: ESCLScanner[]) => void): void;
|
|
48
|
+
/**
|
|
49
|
+
* Unsubscribe from scanner discovery updates
|
|
50
|
+
*/
|
|
51
|
+
offScannerDiscovered(callback: (scanners: ESCLScanner[]) => void): void;
|
|
52
|
+
/**
|
|
53
|
+
* Start Python subprocess for eSCL operations
|
|
54
|
+
*/
|
|
55
|
+
private startPythonService;
|
|
56
|
+
/**
|
|
57
|
+
* Cleanup Python subprocess
|
|
58
|
+
*/
|
|
59
|
+
private cleanup;
|
|
60
|
+
/**
|
|
61
|
+
* Notify all listeners of scanner changes
|
|
62
|
+
*/
|
|
63
|
+
private notifyListeners;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Convenience function for quick scanner discovery
|
|
67
|
+
* @param timeout Discovery timeout in milliseconds (default: 5000)
|
|
68
|
+
* @returns Discovery response with success status and scanner data
|
|
69
|
+
*/
|
|
70
|
+
export declare function discoverScanners(timeout?: number): Promise<DiscoveryResponse>;
|
|
71
|
+
//# sourceMappingURL=discovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../src/discovery.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH,OAAO,EAAE,WAAW,EAA6B,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEpF;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,UAAU,CAAuC;IACzD,OAAO,CAAC,SAAS,CAAqD;IACtE,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAkB;IACtC,OAAO,CAAC,UAAU,CAAqB;gBAE3B,OAAO,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,oBAAoB;IAS5D;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAuB1B;;;;OAIG;IACG,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IAoDlE;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,WAAW,IAAI,WAAW,EAAE;IAI5B;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAItE;;OAEG;IACH,oBAAoB,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,WAAW,EAAE,KAAK,IAAI,GAAG,IAAI;IAIvE;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA6D1B;;OAEG;IACH,OAAO,CAAC,OAAO;IA2Bf;;OAEG;IACH,OAAO,CAAC,eAAe;CAMxB;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,MAAa,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAGzF"}
|