@ecobridge.xyz/devicemanager 3.1.0 → 3.2.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/.gitea/workflows/default_tags.yaml +0 -21
- package/.smartconfig.json +69 -0
- package/changelog.md +38 -1
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/device/device.classes.device.d.ts +2 -2
- package/dist_ts/device/device.classes.device.js +3 -3
- package/dist_ts/devicemanager.classes.devicemanager.d.ts +17 -4
- package/dist_ts/devicemanager.classes.devicemanager.js +99 -11
- package/dist_ts/discovery/discovery.classes.mdns.js +46 -11
- package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +5 -1
- package/dist_ts/discovery/discovery.classes.networkscanner.js +44 -3
- package/dist_ts/discovery/discovery.classes.ssdp.d.ts +52 -21
- package/dist_ts/discovery/discovery.classes.ssdp.js +407 -117
- package/dist_ts/events/events.observable.d.ts +15 -0
- package/dist_ts/events/events.observable.js +22 -0
- package/dist_ts/events/index.d.ts +1 -0
- package/dist_ts/events/index.js +2 -0
- package/dist_ts/factories/index.js +7 -5
- package/dist_ts/features/feature.abstract.d.ts +2 -2
- package/dist_ts/features/feature.abstract.js +3 -3
- package/dist_ts/features/feature.print.d.ts +3 -0
- package/dist_ts/features/feature.print.js +17 -2
- package/dist_ts/features/feature.scan.d.ts +5 -2
- package/dist_ts/features/feature.scan.js +98 -27
- package/dist_ts/index.d.ts +4 -2
- package/dist_ts/index.js +4 -2
- package/dist_ts/interfaces/feature.interfaces.d.ts +22 -2
- package/dist_ts/interfaces/index.d.ts +22 -4
- package/dist_ts/interfaces/index.js +1 -1
- package/dist_ts/plugins.d.ts +6 -15
- package/dist_ts/plugins.js +7 -19
- package/dist_ts/protocols/index.d.ts +2 -1
- package/dist_ts/protocols/index.js +4 -2
- package/dist_ts/protocols/protocol.brother.d.ts +67 -0
- package/dist_ts/protocols/protocol.brother.js +560 -0
- package/dist_ts/protocols/protocol.escl.d.ts +13 -4
- package/dist_ts/protocols/protocol.escl.js +345 -178
- package/dist_ts/protocols/protocol.ipp.d.ts +2 -0
- package/dist_ts/protocols/protocol.ipp.js +22 -2
- package/dist_ts/providers/index.d.ts +1 -0
- package/dist_ts/providers/index.js +2 -0
- package/dist_ts/providers/provider.smb.d.ts +60 -0
- package/dist_ts/providers/provider.smb.js +224 -0
- package/license.md +21 -0
- package/package.json +15 -20
- package/pnpm-workspace.yaml +6 -0
- package/readme.md +157 -78
- package/test/test.brother.node.ts +306 -0
- package/test/test.escl.node.ts +233 -0
- package/test/test.ipp.node.ts +75 -0
- package/test/{test.ts → test.node.ts} +37 -45
- package/test/test.smb.node.ts +123 -0
- package/test/test.ssdp.node.ts +978 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/device/device.classes.device.ts +2 -2
- package/ts/devicemanager.classes.devicemanager.ts +125 -12
- package/ts/discovery/discovery.classes.mdns.ts +40 -11
- package/ts/discovery/discovery.classes.networkscanner.ts +51 -2
- package/ts/discovery/discovery.classes.ssdp.ts +493 -130
- package/ts/events/events.observable.ts +31 -0
- package/ts/events/index.ts +4 -0
- package/ts/factories/index.ts +7 -5
- package/ts/features/feature.abstract.ts +2 -2
- package/ts/features/feature.print.ts +16 -1
- package/ts/features/feature.scan.ts +93 -27
- package/ts/index.ts +21 -0
- package/ts/interfaces/feature.interfaces.ts +23 -2
- package/ts/interfaces/index.ts +23 -4
- package/ts/plugins.ts +6 -25
- package/ts/protocols/index.ts +11 -1
- package/ts/protocols/protocol.brother.ts +648 -0
- package/ts/protocols/protocol.escl.ts +348 -191
- package/ts/protocols/protocol.ipp.ts +24 -1
- package/ts/providers/index.ts +7 -0
- package/ts/providers/provider.smb.ts +326 -0
- package/dist_ts/protocols/protocol.ipp.old.d.ts +0 -45
- package/dist_ts/protocols/protocol.ipp.old.js +0 -284
- package/npmextra.json +0 -24
|
@@ -164,6 +164,8 @@ export interface IIppPrinterCapabilities {
|
|
|
164
164
|
// Document formats
|
|
165
165
|
documentFormatSupported: string[];
|
|
166
166
|
documentFormatDefault?: string;
|
|
167
|
+
/** Whether the printer advertises Apple's Universal Raster Format. */
|
|
168
|
+
airPrintSupported: boolean;
|
|
167
169
|
|
|
168
170
|
// Media
|
|
169
171
|
mediaSizeSupported: string[];
|
|
@@ -573,6 +575,10 @@ class IppDecoder {
|
|
|
573
575
|
return date;
|
|
574
576
|
}
|
|
575
577
|
|
|
578
|
+
case TAG_VALUE.TEXT_WITH_LANGUAGE:
|
|
579
|
+
case TAG_VALUE.NAME_WITH_LANGUAGE:
|
|
580
|
+
return this.readLanguageTaggedString(length);
|
|
581
|
+
|
|
576
582
|
case TAG_VALUE.NO_VALUE:
|
|
577
583
|
case TAG_VALUE.UNKNOWN:
|
|
578
584
|
case TAG_VALUE.UNSUPPORTED:
|
|
@@ -584,6 +590,20 @@ class IppDecoder {
|
|
|
584
590
|
return this.readString(length);
|
|
585
591
|
}
|
|
586
592
|
}
|
|
593
|
+
|
|
594
|
+
private readLanguageTaggedString(length: number): string {
|
|
595
|
+
const value = this.buffer.subarray(this.offset, this.offset + length);
|
|
596
|
+
this.offset += length;
|
|
597
|
+
if (value.length < 4) return value.toString('utf-8');
|
|
598
|
+
|
|
599
|
+
const languageLength = value.readUInt16BE(0);
|
|
600
|
+
const textLengthOffset = 2 + languageLength;
|
|
601
|
+
if (textLengthOffset + 2 > value.length) return value.toString('utf-8');
|
|
602
|
+
const textLength = value.readUInt16BE(textLengthOffset);
|
|
603
|
+
const textOffset = textLengthOffset + 2;
|
|
604
|
+
if (textOffset + textLength > value.length) return value.toString('utf-8');
|
|
605
|
+
return value.toString('utf-8', textOffset, textOffset + textLength);
|
|
606
|
+
}
|
|
587
607
|
}
|
|
588
608
|
|
|
589
609
|
// ============================================================================
|
|
@@ -745,6 +765,8 @@ export class IppClient {
|
|
|
745
765
|
const printerStateString = printerState === PRINTER_STATE.IDLE ? 'idle' :
|
|
746
766
|
printerState === PRINTER_STATE.PROCESSING ? 'processing' : 'stopped';
|
|
747
767
|
|
|
768
|
+
const documentFormatSupported = getStringArray('document-format-supported');
|
|
769
|
+
|
|
748
770
|
return {
|
|
749
771
|
printerName: getString('printer-name', 'Unknown Printer'),
|
|
750
772
|
printerInfo: getString('printer-info') || undefined,
|
|
@@ -757,8 +779,9 @@ export class IppClient {
|
|
|
757
779
|
printerIsAcceptingJobs: getBool('printer-is-accepting-jobs', true),
|
|
758
780
|
queuedJobCount: getNumber('queued-job-count', 0),
|
|
759
781
|
|
|
760
|
-
documentFormatSupported
|
|
782
|
+
documentFormatSupported,
|
|
761
783
|
documentFormatDefault: getString('document-format-default') || undefined,
|
|
784
|
+
airPrintSupported: documentFormatSupported.includes('image/urf'),
|
|
762
785
|
|
|
763
786
|
mediaSizeSupported: getStringArray('media-supported'),
|
|
764
787
|
mediaDefault: getString('media-default') || undefined,
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SambaServer,
|
|
3
|
+
type ISambaAuthOptions,
|
|
4
|
+
type ISambaClientOptions,
|
|
5
|
+
type ISambaServerOptions,
|
|
6
|
+
type ISambaServerStartResult,
|
|
7
|
+
type ISambaServerStatus,
|
|
8
|
+
} from '@push.rocks/smartsamba';
|
|
9
|
+
import * as fs from 'node:fs/promises';
|
|
10
|
+
import * as path from 'node:path';
|
|
11
|
+
import * as plugins from '../plugins.js';
|
|
12
|
+
import { ObservableEventEmitter } from '../events/index.js';
|
|
13
|
+
|
|
14
|
+
export interface ISmbShareProviderOptions extends ISambaServerOptions {
|
|
15
|
+
/** Interval used to inspect share contents for changes. */
|
|
16
|
+
pollIntervalMs?: number;
|
|
17
|
+
/** Time a file must remain unchanged before it is emitted as ready. */
|
|
18
|
+
settleTimeMs?: number;
|
|
19
|
+
/** Emit pre-existing regular files after startup. Defaults to false. */
|
|
20
|
+
emitExistingDocuments?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ISmbDocumentEvent {
|
|
24
|
+
shareName: string;
|
|
25
|
+
relativePath: string;
|
|
26
|
+
absolutePath: string;
|
|
27
|
+
size: number;
|
|
28
|
+
createdAt: Date;
|
|
29
|
+
modifiedAt: Date;
|
|
30
|
+
detectedAt: Date;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ISmbServerAdapter {
|
|
34
|
+
start(): Promise<ISambaServerStartResult>;
|
|
35
|
+
stop(): Promise<void>;
|
|
36
|
+
status(): Promise<ISambaServerStatus>;
|
|
37
|
+
getConnectionOptions(auth?: Partial<ISambaAuthOptions>): ISambaClientOptions;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface ISmbShareProviderDependencies {
|
|
41
|
+
serverFactory?: (options: ISambaServerOptions) => ISmbServerAdapter;
|
|
42
|
+
now?: () => number;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
type TPendingFileEvent = 'arrived' | 'changed';
|
|
46
|
+
|
|
47
|
+
interface ITrackedFile {
|
|
48
|
+
shareName: string;
|
|
49
|
+
relativePath: string;
|
|
50
|
+
absolutePath: string;
|
|
51
|
+
size: number;
|
|
52
|
+
createdMs: number;
|
|
53
|
+
modifiedMs: number;
|
|
54
|
+
lastChangedMs: number;
|
|
55
|
+
emitted: boolean;
|
|
56
|
+
pending?: TPendingFileEvent;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const DEFAULT_POLL_INTERVAL_MS = 250;
|
|
60
|
+
const DEFAULT_SETTLE_TIME_MS = 1000;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Hosts local folders through SMB and emits settled document arrivals.
|
|
64
|
+
*/
|
|
65
|
+
export class SmbShareProvider extends ObservableEventEmitter {
|
|
66
|
+
private readonly server: ISmbServerAdapter;
|
|
67
|
+
private readonly shares: ISambaServerOptions['shares'];
|
|
68
|
+
private readonly pollIntervalMs: number;
|
|
69
|
+
private readonly settleTimeMs: number;
|
|
70
|
+
private readonly emitExistingDocuments: boolean;
|
|
71
|
+
private readonly now: () => number;
|
|
72
|
+
private readonly trackedFiles = new Map<string, ITrackedFile>();
|
|
73
|
+
private pollTimer?: ReturnType<typeof setInterval>;
|
|
74
|
+
private scanPromise?: Promise<void>;
|
|
75
|
+
private startResult?: ISambaServerStartResult;
|
|
76
|
+
|
|
77
|
+
public readonly documents$ = this.events$.pipe(
|
|
78
|
+
plugins.rxjs.filter((event) => event.name === 'document:arrived'),
|
|
79
|
+
plugins.rxjs.map((event) => event.args[0] as ISmbDocumentEvent)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
constructor(
|
|
83
|
+
options: ISmbShareProviderOptions,
|
|
84
|
+
dependencies: ISmbShareProviderDependencies = {}
|
|
85
|
+
) {
|
|
86
|
+
super();
|
|
87
|
+
const {
|
|
88
|
+
pollIntervalMs = DEFAULT_POLL_INTERVAL_MS,
|
|
89
|
+
settleTimeMs = DEFAULT_SETTLE_TIME_MS,
|
|
90
|
+
emitExistingDocuments = false,
|
|
91
|
+
...serverOptions
|
|
92
|
+
} = options;
|
|
93
|
+
if (
|
|
94
|
+
!Number.isFinite(pollIntervalMs) ||
|
|
95
|
+
!Number.isFinite(settleTimeMs) ||
|
|
96
|
+
pollIntervalMs <= 0 ||
|
|
97
|
+
settleTimeMs < 0
|
|
98
|
+
) {
|
|
99
|
+
throw new Error('SMB poll interval must be positive and settle time must not be negative');
|
|
100
|
+
}
|
|
101
|
+
this.shares = serverOptions.shares;
|
|
102
|
+
this.pollIntervalMs = pollIntervalMs;
|
|
103
|
+
this.settleTimeMs = settleTimeMs;
|
|
104
|
+
this.emitExistingDocuments = emitExistingDocuments;
|
|
105
|
+
this.now = dependencies.now ?? Date.now;
|
|
106
|
+
this.server = dependencies.serverFactory?.(serverOptions) ?? new SambaServer(serverOptions);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
public get running(): boolean {
|
|
110
|
+
return this.startResult !== undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public async start(): Promise<ISambaServerStartResult> {
|
|
114
|
+
if (this.startResult) return this.startResult;
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await this.prepareShareDirectories();
|
|
118
|
+
await this.initializeTrackedFiles();
|
|
119
|
+
this.startResult = await this.server.start();
|
|
120
|
+
this.pollTimer = setInterval(() => this.queueScan(), this.pollIntervalMs);
|
|
121
|
+
this.pollTimer.unref?.();
|
|
122
|
+
this.emit('started', this.startResult);
|
|
123
|
+
return this.startResult;
|
|
124
|
+
} catch (error) {
|
|
125
|
+
this.emit('provider:error', error);
|
|
126
|
+
throw error;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async stop(): Promise<void> {
|
|
131
|
+
if (this.pollTimer) {
|
|
132
|
+
clearInterval(this.pollTimer);
|
|
133
|
+
this.pollTimer = undefined;
|
|
134
|
+
}
|
|
135
|
+
await this.scanPromise;
|
|
136
|
+
if (!this.startResult) return;
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
await this.server.stop();
|
|
140
|
+
} finally {
|
|
141
|
+
this.startResult = undefined;
|
|
142
|
+
this.trackedFiles.clear();
|
|
143
|
+
this.emit('stopped');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async status(): Promise<ISambaServerStatus> {
|
|
148
|
+
if (!this.startResult) {
|
|
149
|
+
return { running: false, shares: this.shares.map((share) => share.name) };
|
|
150
|
+
}
|
|
151
|
+
return this.server.status();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
public getConnectionOptions(auth?: Partial<ISambaAuthOptions>): ISambaClientOptions {
|
|
155
|
+
return this.server.getConnectionOptions(auth);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Runs an immediate share scan. Useful after an external notification. */
|
|
159
|
+
public async scanNow(): Promise<void> {
|
|
160
|
+
await this.queueScan();
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private async prepareShareDirectories(): Promise<void> {
|
|
164
|
+
for (const share of this.shares) {
|
|
165
|
+
if (share.createIfMissing !== false) {
|
|
166
|
+
await fs.mkdir(path.resolve(share.path), { recursive: true });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private async initializeTrackedFiles(): Promise<void> {
|
|
172
|
+
this.trackedFiles.clear();
|
|
173
|
+
const now = this.now();
|
|
174
|
+
for (const share of this.shares) {
|
|
175
|
+
const files = await listRegularFiles(path.resolve(share.path));
|
|
176
|
+
for (const file of files) {
|
|
177
|
+
const tracked = this.toTrackedFile(share.name, file, now);
|
|
178
|
+
tracked.emitted = !this.emitExistingDocuments;
|
|
179
|
+
tracked.pending = this.emitExistingDocuments ? 'arrived' : undefined;
|
|
180
|
+
this.trackedFiles.set(trackedFileKey(share.name, file.absolutePath), tracked);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private queueScan(): Promise<void> {
|
|
186
|
+
if (this.scanPromise) return this.scanPromise;
|
|
187
|
+
const scanPromise = this.scanShares()
|
|
188
|
+
.catch((error) => {
|
|
189
|
+
this.emit('provider:error', error);
|
|
190
|
+
})
|
|
191
|
+
.finally(() => {
|
|
192
|
+
this.scanPromise = undefined;
|
|
193
|
+
});
|
|
194
|
+
this.scanPromise = scanPromise;
|
|
195
|
+
return scanPromise;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private async scanShares(): Promise<void> {
|
|
199
|
+
const now = this.now();
|
|
200
|
+
const seenFiles = new Set<string>();
|
|
201
|
+
|
|
202
|
+
for (const share of this.shares) {
|
|
203
|
+
const rootPath = path.resolve(share.path);
|
|
204
|
+
const files = await listRegularFiles(rootPath);
|
|
205
|
+
for (const file of files) {
|
|
206
|
+
const key = trackedFileKey(share.name, file.absolutePath);
|
|
207
|
+
seenFiles.add(key);
|
|
208
|
+
const existing = this.trackedFiles.get(key);
|
|
209
|
+
if (!existing) {
|
|
210
|
+
this.trackedFiles.set(
|
|
211
|
+
key,
|
|
212
|
+
this.toTrackedFile(share.name, file, now, 'arrived')
|
|
213
|
+
);
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (existing.size !== file.size || existing.modifiedMs !== file.modifiedMs) {
|
|
218
|
+
existing.size = file.size;
|
|
219
|
+
existing.createdMs = file.createdMs;
|
|
220
|
+
existing.modifiedMs = file.modifiedMs;
|
|
221
|
+
existing.lastChangedMs = now;
|
|
222
|
+
existing.pending = existing.emitted ? 'changed' : 'arrived';
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (existing.pending && now - existing.lastChangedMs >= this.settleTimeMs) {
|
|
227
|
+
const eventName = existing.pending === 'arrived'
|
|
228
|
+
? 'document:arrived'
|
|
229
|
+
: 'document:changed';
|
|
230
|
+
existing.pending = undefined;
|
|
231
|
+
existing.emitted = true;
|
|
232
|
+
this.emit(eventName, this.toDocumentEvent(existing, now));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
for (const [key, tracked] of this.trackedFiles) {
|
|
238
|
+
if (seenFiles.has(key)) continue;
|
|
239
|
+
this.trackedFiles.delete(key);
|
|
240
|
+
if (tracked.emitted) {
|
|
241
|
+
this.emit('document:removed', this.toDocumentEvent(tracked, now));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private toTrackedFile(
|
|
247
|
+
shareName: string,
|
|
248
|
+
file: IRegularFile,
|
|
249
|
+
now: number,
|
|
250
|
+
pending?: TPendingFileEvent
|
|
251
|
+
): ITrackedFile {
|
|
252
|
+
return {
|
|
253
|
+
shareName,
|
|
254
|
+
relativePath: file.relativePath,
|
|
255
|
+
absolutePath: file.absolutePath,
|
|
256
|
+
size: file.size,
|
|
257
|
+
createdMs: file.createdMs,
|
|
258
|
+
modifiedMs: file.modifiedMs,
|
|
259
|
+
lastChangedMs: now,
|
|
260
|
+
emitted: false,
|
|
261
|
+
pending,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
private toDocumentEvent(file: ITrackedFile, now: number): ISmbDocumentEvent {
|
|
266
|
+
return {
|
|
267
|
+
shareName: file.shareName,
|
|
268
|
+
relativePath: file.relativePath,
|
|
269
|
+
absolutePath: file.absolutePath,
|
|
270
|
+
size: file.size,
|
|
271
|
+
createdAt: new Date(file.createdMs),
|
|
272
|
+
modifiedAt: new Date(file.modifiedMs),
|
|
273
|
+
detectedAt: new Date(now),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
interface IRegularFile {
|
|
279
|
+
relativePath: string;
|
|
280
|
+
absolutePath: string;
|
|
281
|
+
size: number;
|
|
282
|
+
createdMs: number;
|
|
283
|
+
modifiedMs: number;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function trackedFileKey(shareName: string, absolutePath: string): string {
|
|
287
|
+
return `${shareName}\0${absolutePath}`;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
async function listRegularFiles(rootPath: string): Promise<IRegularFile[]> {
|
|
291
|
+
const files: IRegularFile[] = [];
|
|
292
|
+
await walkDirectory(rootPath, rootPath, files);
|
|
293
|
+
return files;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function walkDirectory(
|
|
297
|
+
rootPath: string,
|
|
298
|
+
directoryPath: string,
|
|
299
|
+
files: IRegularFile[]
|
|
300
|
+
): Promise<void> {
|
|
301
|
+
let entries;
|
|
302
|
+
try {
|
|
303
|
+
entries = await fs.readdir(directoryPath, { withFileTypes: true });
|
|
304
|
+
} catch (error) {
|
|
305
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return;
|
|
306
|
+
throw error;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
for (const entry of entries) {
|
|
310
|
+
const absolutePath = path.join(directoryPath, entry.name);
|
|
311
|
+
if (entry.isDirectory()) {
|
|
312
|
+
await walkDirectory(rootPath, absolutePath, files);
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
if (!entry.isFile()) continue;
|
|
316
|
+
|
|
317
|
+
const stats = await fs.stat(absolutePath);
|
|
318
|
+
files.push({
|
|
319
|
+
relativePath: path.relative(rootPath, absolutePath).split(path.sep).join('/'),
|
|
320
|
+
absolutePath,
|
|
321
|
+
size: stats.size,
|
|
322
|
+
createdMs: stats.birthtimeMs,
|
|
323
|
+
modifiedMs: stats.mtimeMs,
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type { IPrinterCapabilities, IPrintOptions, IPrintJob } from '../interfaces/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* IPP protocol wrapper using the ipp npm package
|
|
4
|
-
*/
|
|
5
|
-
export declare class IppProtocol {
|
|
6
|
-
private printerUrl;
|
|
7
|
-
private printer;
|
|
8
|
-
constructor(address: string, port: number, path?: string);
|
|
9
|
-
/**
|
|
10
|
-
* Get printer attributes/capabilities
|
|
11
|
-
*/
|
|
12
|
-
getAttributes(): Promise<IPrinterCapabilities>;
|
|
13
|
-
/**
|
|
14
|
-
* Print a document
|
|
15
|
-
*/
|
|
16
|
-
print(data: Buffer, options?: IPrintOptions): Promise<IPrintJob>;
|
|
17
|
-
/**
|
|
18
|
-
* Get all jobs
|
|
19
|
-
*/
|
|
20
|
-
getJobs(): Promise<IPrintJob[]>;
|
|
21
|
-
/**
|
|
22
|
-
* Get specific job info
|
|
23
|
-
*/
|
|
24
|
-
getJobInfo(jobId: number): Promise<IPrintJob>;
|
|
25
|
-
/**
|
|
26
|
-
* Cancel a job
|
|
27
|
-
*/
|
|
28
|
-
cancelJob(jobId: number): Promise<void>;
|
|
29
|
-
/**
|
|
30
|
-
* Check if printer is available
|
|
31
|
-
*/
|
|
32
|
-
checkAvailability(): Promise<boolean>;
|
|
33
|
-
/**
|
|
34
|
-
* Build IPP print message from options
|
|
35
|
-
*/
|
|
36
|
-
private buildPrintMessage;
|
|
37
|
-
/**
|
|
38
|
-
* Parse printer capabilities from attributes
|
|
39
|
-
*/
|
|
40
|
-
private parseCapabilities;
|
|
41
|
-
/**
|
|
42
|
-
* Parse job info from attributes
|
|
43
|
-
*/
|
|
44
|
-
private parseJobInfo;
|
|
45
|
-
}
|