@ecobridge.xyz/devicemanager 3.0.2 → 3.1.1

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.
Files changed (42) hide show
  1. package/.gitea/workflows/default_tags.yaml +0 -21
  2. package/.smartconfig.json +69 -0
  3. package/changelog.md +23 -1
  4. package/dist_ts/00_commitinfo_data.js +1 -1
  5. package/dist_ts/devicemanager.classes.devicemanager.js +33 -7
  6. package/dist_ts/discovery/discovery.classes.mdns.js +46 -11
  7. package/dist_ts/discovery/discovery.classes.networkscanner.d.ts +1 -1
  8. package/dist_ts/discovery/discovery.classes.networkscanner.js +1 -1
  9. package/dist_ts/discovery/discovery.classes.ssdp.d.ts +52 -21
  10. package/dist_ts/discovery/discovery.classes.ssdp.js +407 -117
  11. package/dist_ts/features/feature.print.d.ts +4 -2
  12. package/dist_ts/features/feature.print.js +48 -38
  13. package/dist_ts/index.d.ts +1 -1
  14. package/dist_ts/interfaces/feature.interfaces.d.ts +1 -1
  15. package/dist_ts/interfaces/index.d.ts +1 -1
  16. package/dist_ts/plugins.d.ts +5 -15
  17. package/dist_ts/plugins.js +6 -19
  18. package/dist_ts/protocols/index.d.ts +1 -1
  19. package/dist_ts/protocols/index.js +2 -2
  20. package/dist_ts/protocols/protocol.escl.js +2 -2
  21. package/dist_ts/protocols/protocol.ipp.d.ts +144 -33
  22. package/dist_ts/protocols/protocol.ipp.js +942 -239
  23. package/license.md +21 -0
  24. package/package.json +16 -25
  25. package/pnpm-workspace.yaml +4 -0
  26. package/readme.md +61 -76
  27. package/test/{test.ts → test.node.ts} +20 -45
  28. package/test/test.ssdp.node.ts +978 -0
  29. package/ts/00_commitinfo_data.ts +1 -1
  30. package/ts/devicemanager.classes.devicemanager.ts +44 -6
  31. package/ts/discovery/discovery.classes.mdns.ts +40 -11
  32. package/ts/discovery/discovery.classes.networkscanner.ts +1 -1
  33. package/ts/discovery/discovery.classes.ssdp.ts +493 -130
  34. package/ts/features/feature.print.ts +53 -45
  35. package/ts/index.ts +1 -0
  36. package/ts/interfaces/feature.interfaces.ts +1 -1
  37. package/ts/interfaces/index.ts +1 -1
  38. package/ts/plugins.ts +5 -25
  39. package/ts/protocols/index.ts +6 -1
  40. package/ts/protocols/protocol.escl.ts +1 -1
  41. package/ts/protocols/protocol.ipp.ts +1183 -260
  42. package/npmextra.json +0 -24
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@ecobridge.xyz/devicemanager',
6
- version: '3.0.2',
6
+ version: '3.1.1',
7
7
  description: 'a device manager for talking to devices on network and over usb'
8
8
  }
@@ -546,17 +546,55 @@ export class DeviceManager extends plugins.events.EventEmitter {
546
546
  // ============================================================================
547
547
 
548
548
  public async startDiscovery(): Promise<void> {
549
- await Promise.all([
550
- this.mdnsDiscovery.start(),
551
- this.ssdpDiscovery.start(),
549
+ const startResults = await Promise.allSettled([
550
+ Promise.resolve().then(() => this.mdnsDiscovery.start()),
551
+ Promise.resolve().then(() => this.ssdpDiscovery.start()),
552
552
  ]);
553
+ const startupFailures = startResults
554
+ .filter((result): result is PromiseRejectedResult => result.status === 'rejected')
555
+ .map((result) => result.reason as unknown);
556
+ if (startupFailures.length === 0) {
557
+ return;
558
+ }
559
+
560
+ const cleanupResults = await Promise.allSettled([
561
+ Promise.resolve().then(() => this.mdnsDiscovery.stop()),
562
+ Promise.resolve().then(() => this.ssdpDiscovery.stop()),
563
+ ]);
564
+ const cleanupFailures = cleanupResults
565
+ .filter((result): result is PromiseRejectedResult => result.status === 'rejected')
566
+ .map((result) => result.reason as unknown);
567
+
568
+ if (startupFailures.length === 1 && cleanupFailures.length === 0) {
569
+ throw startupFailures[0];
570
+ }
571
+
572
+ const message = cleanupFailures.length > 0
573
+ ? 'Device discovery failed to start and rollback cleanup was incomplete.'
574
+ : 'Multiple discovery transports failed to start.';
575
+ throw new AggregateError(
576
+ [...startupFailures, ...cleanupFailures],
577
+ message,
578
+ { cause: startupFailures[0] }
579
+ );
553
580
  }
554
581
 
555
582
  public async stopDiscovery(): Promise<void> {
556
- await Promise.all([
557
- this.mdnsDiscovery.stop(),
558
- this.ssdpDiscovery.stop(),
583
+ const stopResults = await Promise.allSettled([
584
+ Promise.resolve().then(() => this.mdnsDiscovery.stop()),
585
+ Promise.resolve().then(() => this.ssdpDiscovery.stop()),
559
586
  ]);
587
+ const stopFailures = stopResults
588
+ .filter((result): result is PromiseRejectedResult => result.status === 'rejected')
589
+ .map((result) => result.reason as unknown);
590
+
591
+ if (stopFailures.length > 0) {
592
+ throw new AggregateError(
593
+ stopFailures,
594
+ 'One or more discovery transports failed to stop cleanly.',
595
+ { cause: stopFailures[0] }
596
+ );
597
+ }
560
598
  }
561
599
 
562
600
  public get isDiscovering(): boolean {
@@ -117,20 +117,49 @@ export class MdnsDiscovery extends plugins.events.EventEmitter {
117
117
  return;
118
118
  }
119
119
 
120
- // Stop all browsers
121
- for (const browser of this.browsers) {
122
- browser.stop();
123
- }
120
+ const browsers = this.browsers;
121
+ const bonjour = this.bonjour;
122
+ const cleanupErrors: unknown[] = [];
124
123
  this.browsers = [];
125
-
126
- // Destroy bonjour instance
127
- if (this.bonjour) {
128
- this.bonjour.destroy();
129
- this.bonjour = null;
124
+ this.bonjour = null;
125
+
126
+ try {
127
+ for (const browser of browsers) {
128
+ try {
129
+ browser.stop();
130
+ } catch (error) {
131
+ cleanupErrors.push(error);
132
+ }
133
+ }
134
+ } finally {
135
+ try {
136
+ if (bonjour) {
137
+ await new Promise<void>((resolve, reject) => {
138
+ try {
139
+ bonjour.destroy((error?: Error) => error ? reject(error) : resolve());
140
+ } catch (error) {
141
+ reject(error);
142
+ }
143
+ });
144
+ }
145
+ } catch (error) {
146
+ cleanupErrors.push(error);
147
+ } finally {
148
+ this.isRunning = false;
149
+ try {
150
+ this.emit('stopped');
151
+ } catch (error) {
152
+ cleanupErrors.push(error);
153
+ }
154
+ }
130
155
  }
131
156
 
132
- this.isRunning = false;
133
- this.emit('stopped');
157
+ if (cleanupErrors.length === 1) {
158
+ throw cleanupErrors[0];
159
+ }
160
+ if (cleanupErrors.length > 1) {
161
+ throw new AggregateError(cleanupErrors, 'Failed to stop mDNS discovery cleanly.');
162
+ }
134
163
  }
135
164
 
136
165
  /**
@@ -332,7 +332,7 @@ export class NetworkScanner extends plugins.events.EventEmitter {
332
332
 
333
333
  /**
334
334
  * Probe for IPP printer using a simple HTTP check
335
- * We avoid using the full ipp library for probing since it can hang and produce noisy output
335
+ * Use a minimal request instead of an external IPP client for discovery probing.
336
336
  */
337
337
  private async probeIpp(
338
338
  ip: string,