capacitor-rfid-plugin-ox 0.4.0 → 0.5.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/README.md CHANGED
@@ -14,6 +14,9 @@ npx cap sync
14
14
  <docgen-index>
15
15
 
16
16
  * [`getCapabilities()`](#getcapabilities)
17
+ * [`getConnectionState()`](#getconnectionstate)
18
+ * [`listReaderPorts()`](#listreaderports)
19
+ * [`selectReaderPort(...)`](#selectreaderport)
17
20
  * [`isConnected()`](#isconnected)
18
21
  * [`startScan()`](#startscan)
19
22
  * [`stopScan()`](#stopscan)
@@ -31,7 +34,10 @@ npx cap sync
31
34
  * [`writeEpcString(...)`](#writeepcstring)
32
35
  * [`startSearch(...)`](#startsearch)
33
36
  * [`stopSearch()`](#stopsearch)
37
+ * [`addListener('onConnectionState', ...)`](#addlisteneronconnectionstate-)
38
+ * [`addListener(string, ...)`](#addlistenerstring-)
34
39
  * [Interfaces](#interfaces)
40
+ * [Type Aliases](#type-aliases)
35
41
 
36
42
  </docgen-index>
37
43
 
@@ -54,6 +60,71 @@ failure: the caller treats a rejection as a handheld Mobile Reader.
54
60
  --------------------
55
61
 
56
62
 
63
+ ### getConnectionState()
64
+
65
+ ```typescript
66
+ getConnectionState() => Promise<ReaderConnectionState>
67
+ ```
68
+
69
+ Connection state for the first render. The stream of later changes comes
70
+ from the `onConnectionState` listener — "not responding" is only found out
71
+ after a timeout, so it cannot be a return value.
72
+
73
+ A reader is an exclusive resource, so its state is only knowable while it
74
+ is held: an implementation that is not connected may start an attempt here
75
+ and answer `connecting`, with the outcome arriving on the listener.
76
+
77
+ Implementations that predate this method reject. The caller then hides the
78
+ indicator instead of guessing.
79
+
80
+ **Returns:** <code>Promise&lt;<a href="#readerconnectionstate">ReaderConnectionState</a>&gt;</code>
81
+
82
+ --------------------
83
+
84
+
85
+ ### listReaderPorts()
86
+
87
+ ```typescript
88
+ listReaderPorts() => Promise<{ ports: ReaderPort[]; }>
89
+ ```
90
+
91
+ The readers plugged in right now, newest listing every call — a port that
92
+ was there a minute ago may be gone.
93
+
94
+ Only readers, not every serial port: what the caller does with this list
95
+ is offer it as a choice, and a Bluetooth port is not a choice.
96
+
97
+ Implementations that predate this method reject. The caller then keeps
98
+ whatever the driver picks on its own.
99
+
100
+ **Returns:** <code>Promise&lt;{ ports: ReaderPort[]; }&gt;</code>
101
+
102
+ --------------------
103
+
104
+
105
+ ### selectReaderPort(...)
106
+
107
+ ```typescript
108
+ selectReaderPort(options: { path: string; }) => Promise<void>
109
+ ```
110
+
111
+ Use this reader from now on. Reconnects when another port is open.
112
+
113
+ The choice itself is not stored here: the driver is restarted with the
114
+ app, and where a choice belongs is the caller's question. The caller
115
+ remembers the path and says it again on the next start.
116
+
117
+ Rejects when the path is not in the current `listReaderPorts()` — a
118
+ remembered reader that was unplugged has to be chosen again, not opened
119
+ blindly.
120
+
121
+ | Param | Type |
122
+ | ------------- | ------------------------------ |
123
+ | **`options`** | <code>{ path: string; }</code> |
124
+
125
+ --------------------
126
+
127
+
57
128
  ### isConnected()
58
129
 
59
130
  ```typescript
@@ -255,6 +326,40 @@ stopSearch() => Promise<void>
255
326
  --------------------
256
327
 
257
328
 
329
+ ### addListener('onConnectionState', ...)
330
+
331
+ ```typescript
332
+ addListener(eventName: 'onConnectionState', listener: (state: ReaderConnectionState) => void) => Promise<PluginListenerHandle>
333
+ ```
334
+
335
+ Every connection state change. Fires only on a change, not on a poll.
336
+
337
+ | Param | Type |
338
+ | --------------- | ------------------------------------------------------------------------------------------- |
339
+ | **`eventName`** | <code>'onConnectionState'</code> |
340
+ | **`listener`** | <code>(state: <a href="#readerconnectionstate">ReaderConnectionState</a>) =&gt; void</code> |
341
+
342
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
343
+
344
+ --------------------
345
+
346
+
347
+ ### addListener(string, ...)
348
+
349
+ ```typescript
350
+ addListener(eventName: string, listenerFunc: (...args: any[]) => any) => Promise<PluginListenerHandle>
351
+ ```
352
+
353
+ | Param | Type |
354
+ | ------------------ | --------------------------------------- |
355
+ | **`eventName`** | <code>string</code> |
356
+ | **`listenerFunc`** | <code>(...args: any[]) =&gt; any</code> |
357
+
358
+ **Returns:** <code>Promise&lt;<a href="#pluginlistenerhandle">PluginListenerHandle</a>&gt;</code>
359
+
360
+ --------------------
361
+
362
+
258
363
  ### Interfaces
259
364
 
260
365
 
@@ -271,4 +376,70 @@ that cannot.
271
376
  | **`isHandheld`** | <code>boolean</code> | Reader is a handheld terminal rather than a desk device. |
272
377
  | **`power`** | <code>{ min: number; max: number; default: number; }</code> | Output power range in dBm. The power slider renders this range. |
273
378
 
379
+
380
+ #### ReaderConnectionState
381
+
382
+ | Prop | Type | Description |
383
+ | ------------ | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
384
+ | **`status`** | <code><a href="#readerconnectionstatus">ReaderConnectionStatus</a></code> | |
385
+ | **`reason`** | <code><a href="#readerconnectionreason">ReaderConnectionReason</a></code> | |
386
+ | **`detail`** | <code>string</code> | Driver text: the port list, the command code, the OS error. This is the one field allowed to talk about the wiring, because nothing branches on it — it is shown as-is for support, not translated. |
387
+
388
+
389
+ #### ReaderPort
390
+
391
+ One reader the driver could talk to.
392
+
393
+ `path` is the identity — `COM3` on Windows, `/dev/cu.usbserial-110`
394
+ elsewhere. Two identical CH340 adapters carry no serial number of their own,
395
+ so the path is all that separates them.
396
+
397
+ The caller treats it as **opaque**: it compares, stores and hands it back,
398
+ and never reads meaning out of it. A driver for a device that is not a
399
+ serial port puts its own identity here.
400
+
401
+ | Prop | Type | Description |
402
+ | --------------- | ------------------- | ------------------------------------------------------------------------ |
403
+ | **`path`** | <code>string</code> | |
404
+ | **`label`** | <code>string</code> | What the OS calls the device. For the operator to read, not to match on. |
405
+ | **`vendorId`** | <code>string</code> | |
406
+ | **`productId`** | <code>string</code> | |
407
+
408
+
409
+ #### PluginListenerHandle
410
+
411
+ | Prop | Type |
412
+ | ------------ | ----------------------------------------- |
413
+ | **`remove`** | <code>() =&gt; Promise&lt;void&gt;</code> |
414
+
415
+
416
+ ### Type Aliases
417
+
418
+
419
+ #### ReaderConnectionStatus
420
+
421
+ The four states an operator can act on.
422
+
423
+ `isConnected()` is too coarse for this: a reader that is plugged in but
424
+ silent answers `false` the same way a missing one does, and the operator
425
+ needs a different move in each case. `isConnected()` stays as it is.
426
+
427
+ <code>'connecting' | 'connected' | 'not-found' | 'not-responding'</code>
428
+
429
+
430
+ #### ReaderConnectionReason
431
+
432
+ Why the reader is unusable. The status alone does not say what to do next:
433
+ a busy reader needs another program closed, a missing one needs the
434
+ connection checked. The UI picks its message from the reason when there is
435
+ one.
436
+
437
+ These name what happened to the *reader*, never how it is wired. A driver
438
+ for a device with no serial port has to be able to answer in this
439
+ vocabulary too, and the operator has to be told the truth either way —
440
+ so nothing here says "COM port" or "USB". The device-specific sentence
441
+ belongs in `detail`.
442
+
443
+ <code>'busy' | 'not-detected' | 'disconnected' | 'no-answer' | 'not-chosen'</code>
444
+
274
445
  </docgen-api>
package/dist/docs.json CHANGED
@@ -17,6 +17,46 @@
17
17
  ],
18
18
  "slug": "getcapabilities"
19
19
  },
20
+ {
21
+ "name": "getConnectionState",
22
+ "signature": "() => Promise<ReaderConnectionState>",
23
+ "parameters": [],
24
+ "returns": "Promise<ReaderConnectionState>",
25
+ "tags": [],
26
+ "docs": "Connection state for the first render. The stream of later changes comes\nfrom the `onConnectionState` listener — \"not responding\" is only found out\nafter a timeout, so it cannot be a return value.\n\nA reader is an exclusive resource, so its state is only knowable while it\nis held: an implementation that is not connected may start an attempt here\nand answer `connecting`, with the outcome arriving on the listener.\n\nImplementations that predate this method reject. The caller then hides the\nindicator instead of guessing.",
27
+ "complexTypes": [
28
+ "ReaderConnectionState"
29
+ ],
30
+ "slug": "getconnectionstate"
31
+ },
32
+ {
33
+ "name": "listReaderPorts",
34
+ "signature": "() => Promise<{ ports: ReaderPort[]; }>",
35
+ "parameters": [],
36
+ "returns": "Promise<{ ports: ReaderPort[]; }>",
37
+ "tags": [],
38
+ "docs": "The readers plugged in right now, newest listing every call — a port that\nwas there a minute ago may be gone.\n\nOnly readers, not every serial port: what the caller does with this list\nis offer it as a choice, and a Bluetooth port is not a choice.\n\nImplementations that predate this method reject. The caller then keeps\nwhatever the driver picks on its own.",
39
+ "complexTypes": [
40
+ "ReaderPort"
41
+ ],
42
+ "slug": "listreaderports"
43
+ },
44
+ {
45
+ "name": "selectReaderPort",
46
+ "signature": "(options: { path: string; }) => Promise<void>",
47
+ "parameters": [
48
+ {
49
+ "name": "options",
50
+ "docs": "",
51
+ "type": "{ path: string; }"
52
+ }
53
+ ],
54
+ "returns": "Promise<void>",
55
+ "tags": [],
56
+ "docs": "Use this reader from now on. Reconnects when another port is open.\n\nThe choice itself is not stored here: the driver is restarted with the\napp, and where a choice belongs is the caller's question. The caller\nremembers the path and says it again on the next start.\n\nRejects when the path is not in the current `listReaderPorts()` — a\nremembered reader that was unplugged has to be chosen again, not opened\nblindly.",
57
+ "complexTypes": [],
58
+ "slug": "selectreaderport"
59
+ },
20
60
  {
21
61
  "name": "isConnected",
22
62
  "signature": "() => Promise<{ connected: boolean; }>",
@@ -222,6 +262,53 @@
222
262
  "docs": "",
223
263
  "complexTypes": [],
224
264
  "slug": "stopsearch"
265
+ },
266
+ {
267
+ "name": "addListener",
268
+ "signature": "(eventName: 'onConnectionState', listener: (state: ReaderConnectionState) => void) => Promise<PluginListenerHandle>",
269
+ "parameters": [
270
+ {
271
+ "name": "eventName",
272
+ "docs": "",
273
+ "type": "'onConnectionState'"
274
+ },
275
+ {
276
+ "name": "listener",
277
+ "docs": "",
278
+ "type": "(state: ReaderConnectionState) => void"
279
+ }
280
+ ],
281
+ "returns": "Promise<PluginListenerHandle>",
282
+ "tags": [],
283
+ "docs": "Every connection state change. Fires only on a change, not on a poll.",
284
+ "complexTypes": [
285
+ "PluginListenerHandle",
286
+ "ReaderConnectionState"
287
+ ],
288
+ "slug": "addlisteneronconnectionstate-"
289
+ },
290
+ {
291
+ "name": "addListener",
292
+ "signature": "(eventName: string, listenerFunc: (...args: any[]) => any) => Promise<PluginListenerHandle>",
293
+ "parameters": [
294
+ {
295
+ "name": "eventName",
296
+ "docs": "",
297
+ "type": "string"
298
+ },
299
+ {
300
+ "name": "listenerFunc",
301
+ "docs": "",
302
+ "type": "(...args: any[]) => any"
303
+ }
304
+ ],
305
+ "returns": "Promise<PluginListenerHandle>",
306
+ "tags": [],
307
+ "docs": "",
308
+ "complexTypes": [
309
+ "PluginListenerHandle"
310
+ ],
311
+ "slug": "addlistenerstring-"
225
312
  }
226
313
  ],
227
314
  "properties": []
@@ -263,9 +350,147 @@
263
350
  "type": "{ min: number; max: number; default: number; }"
264
351
  }
265
352
  ]
353
+ },
354
+ {
355
+ "name": "ReaderConnectionState",
356
+ "slug": "readerconnectionstate",
357
+ "docs": "",
358
+ "tags": [],
359
+ "methods": [],
360
+ "properties": [
361
+ {
362
+ "name": "status",
363
+ "tags": [],
364
+ "docs": "",
365
+ "complexTypes": [
366
+ "ReaderConnectionStatus"
367
+ ],
368
+ "type": "ReaderConnectionStatus"
369
+ },
370
+ {
371
+ "name": "reason",
372
+ "tags": [],
373
+ "docs": "",
374
+ "complexTypes": [
375
+ "ReaderConnectionReason"
376
+ ],
377
+ "type": "ReaderConnectionReason"
378
+ },
379
+ {
380
+ "name": "detail",
381
+ "tags": [],
382
+ "docs": "Driver text: the port list, the command code, the OS error. This is the\none field allowed to talk about the wiring, because nothing branches on\nit — it is shown as-is for support, not translated.",
383
+ "complexTypes": [],
384
+ "type": "string | undefined"
385
+ }
386
+ ]
387
+ },
388
+ {
389
+ "name": "ReaderPort",
390
+ "slug": "readerport",
391
+ "docs": "One reader the driver could talk to.\n\n`path` is the identity — `COM3` on Windows, `/dev/cu.usbserial-110`\nelsewhere. Two identical CH340 adapters carry no serial number of their own,\nso the path is all that separates them.\n\nThe caller treats it as **opaque**: it compares, stores and hands it back,\nand never reads meaning out of it. A driver for a device that is not a\nserial port puts its own identity here.",
392
+ "tags": [],
393
+ "methods": [],
394
+ "properties": [
395
+ {
396
+ "name": "path",
397
+ "tags": [],
398
+ "docs": "",
399
+ "complexTypes": [],
400
+ "type": "string"
401
+ },
402
+ {
403
+ "name": "label",
404
+ "tags": [],
405
+ "docs": "What the OS calls the device. For the operator to read, not to match on.",
406
+ "complexTypes": [],
407
+ "type": "string | undefined"
408
+ },
409
+ {
410
+ "name": "vendorId",
411
+ "tags": [],
412
+ "docs": "",
413
+ "complexTypes": [],
414
+ "type": "string | undefined"
415
+ },
416
+ {
417
+ "name": "productId",
418
+ "tags": [],
419
+ "docs": "",
420
+ "complexTypes": [],
421
+ "type": "string | undefined"
422
+ }
423
+ ]
424
+ },
425
+ {
426
+ "name": "PluginListenerHandle",
427
+ "slug": "pluginlistenerhandle",
428
+ "docs": "",
429
+ "tags": [],
430
+ "methods": [],
431
+ "properties": [
432
+ {
433
+ "name": "remove",
434
+ "tags": [],
435
+ "docs": "",
436
+ "complexTypes": [],
437
+ "type": "() => Promise<void>"
438
+ }
439
+ ]
266
440
  }
267
441
  ],
268
442
  "enums": [],
269
- "typeAliases": [],
443
+ "typeAliases": [
444
+ {
445
+ "name": "ReaderConnectionStatus",
446
+ "slug": "readerconnectionstatus",
447
+ "docs": "The four states an operator can act on.\n\n`isConnected()` is too coarse for this: a reader that is plugged in but\nsilent answers `false` the same way a missing one does, and the operator\nneeds a different move in each case. `isConnected()` stays as it is.",
448
+ "types": [
449
+ {
450
+ "text": "'connecting'",
451
+ "complexTypes": []
452
+ },
453
+ {
454
+ "text": "'connected'",
455
+ "complexTypes": []
456
+ },
457
+ {
458
+ "text": "'not-found'",
459
+ "complexTypes": []
460
+ },
461
+ {
462
+ "text": "'not-responding'",
463
+ "complexTypes": []
464
+ }
465
+ ]
466
+ },
467
+ {
468
+ "name": "ReaderConnectionReason",
469
+ "slug": "readerconnectionreason",
470
+ "docs": "Why the reader is unusable. The status alone does not say what to do next:\na busy reader needs another program closed, a missing one needs the\nconnection checked. The UI picks its message from the reason when there is\none.\n\nThese name what happened to the *reader*, never how it is wired. A driver\nfor a device with no serial port has to be able to answer in this\nvocabulary too, and the operator has to be told the truth either way —\nso nothing here says \"COM port\" or \"USB\". The device-specific sentence\nbelongs in `detail`.",
471
+ "types": [
472
+ {
473
+ "text": "'busy'",
474
+ "complexTypes": []
475
+ },
476
+ {
477
+ "text": "'not-detected'",
478
+ "complexTypes": []
479
+ },
480
+ {
481
+ "text": "'disconnected'",
482
+ "complexTypes": []
483
+ },
484
+ {
485
+ "text": "'no-answer'",
486
+ "complexTypes": []
487
+ },
488
+ {
489
+ "text": "'not-chosen'",
490
+ "complexTypes": []
491
+ }
492
+ ]
493
+ }
494
+ ],
270
495
  "pluginConfigs": []
271
496
  }
@@ -1,4 +1,4 @@
1
- import type { Plugin } from '@capacitor/core';
1
+ import type { Plugin, PluginListenerHandle } from '@capacitor/core';
2
2
  /**
3
3
  * What the connected Reader can do. Mode gating comes from here, not from the
4
4
  * reader type — a desktop reader that can write must not be gated like one
@@ -18,6 +18,77 @@ export interface ReaderCapabilities {
18
18
  default: number;
19
19
  };
20
20
  }
21
+ /**
22
+ * The four states an operator can act on.
23
+ *
24
+ * `isConnected()` is too coarse for this: a reader that is plugged in but
25
+ * silent answers `false` the same way a missing one does, and the operator
26
+ * needs a different move in each case. `isConnected()` stays as it is.
27
+ */
28
+ export declare type ReaderConnectionStatus =
29
+ /** An attempt is running. */
30
+ 'connecting'
31
+ /** The reader is open and answering. */
32
+ | 'connected'
33
+ /** No reader to talk to — nothing found, or found but not openable. */
34
+ | 'not-found'
35
+ /** The port opened but the reader did not answer a command. */
36
+ | 'not-responding';
37
+ /**
38
+ * Why the reader is unusable. The status alone does not say what to do next:
39
+ * a busy reader needs another program closed, a missing one needs the
40
+ * connection checked. The UI picks its message from the reason when there is
41
+ * one.
42
+ *
43
+ * These name what happened to the *reader*, never how it is wired. A driver
44
+ * for a device with no serial port has to be able to answer in this
45
+ * vocabulary too, and the operator has to be told the truth either way —
46
+ * so nothing here says "COM port" or "USB". The device-specific sentence
47
+ * belongs in `detail`.
48
+ */
49
+ export declare type ReaderConnectionReason =
50
+ /** Another program holds the reader. Only one may have it at a time. */
51
+ 'busy'
52
+ /** No reader in sight — nothing connected, or nothing that is a reader. */
53
+ | 'not-detected'
54
+ /** The reader was there and then went away. */
55
+ | 'disconnected'
56
+ /** The reader is reachable but did not answer. */
57
+ | 'no-answer'
58
+ /**
59
+ * Several readers are connected and none was picked. Guessing here would
60
+ * attach tags read by the wrong device, so nothing is opened until
61
+ * `selectReaderPort` says which one.
62
+ */
63
+ | 'not-chosen';
64
+ export interface ReaderConnectionState {
65
+ status: ReaderConnectionStatus;
66
+ reason?: ReaderConnectionReason;
67
+ /**
68
+ * Driver text: the port list, the command code, the OS error. This is the
69
+ * one field allowed to talk about the wiring, because nothing branches on
70
+ * it — it is shown as-is for support, not translated.
71
+ */
72
+ detail?: string;
73
+ }
74
+ /**
75
+ * One reader the driver could talk to.
76
+ *
77
+ * `path` is the identity — `COM3` on Windows, `/dev/cu.usbserial-110`
78
+ * elsewhere. Two identical CH340 adapters carry no serial number of their own,
79
+ * so the path is all that separates them.
80
+ *
81
+ * The caller treats it as **opaque**: it compares, stores and hands it back,
82
+ * and never reads meaning out of it. A driver for a device that is not a
83
+ * serial port puts its own identity here.
84
+ */
85
+ export interface ReaderPort {
86
+ path: string;
87
+ /** What the OS calls the device. For the operator to read, not to match on. */
88
+ label?: string;
89
+ vendorId?: string;
90
+ productId?: string;
91
+ }
21
92
  export interface RFIDPlugin extends Plugin {
22
93
  /**
23
94
  * Capabilities of the connected Reader.
@@ -26,6 +97,46 @@ export interface RFIDPlugin extends Plugin {
26
97
  * failure: the caller treats a rejection as a handheld Mobile Reader.
27
98
  */
28
99
  getCapabilities(): Promise<ReaderCapabilities>;
100
+ /**
101
+ * Connection state for the first render. The stream of later changes comes
102
+ * from the `onConnectionState` listener — "not responding" is only found out
103
+ * after a timeout, so it cannot be a return value.
104
+ *
105
+ * A reader is an exclusive resource, so its state is only knowable while it
106
+ * is held: an implementation that is not connected may start an attempt here
107
+ * and answer `connecting`, with the outcome arriving on the listener.
108
+ *
109
+ * Implementations that predate this method reject. The caller then hides the
110
+ * indicator instead of guessing.
111
+ */
112
+ getConnectionState(): Promise<ReaderConnectionState>;
113
+ /**
114
+ * The readers plugged in right now, newest listing every call — a port that
115
+ * was there a minute ago may be gone.
116
+ *
117
+ * Only readers, not every serial port: what the caller does with this list
118
+ * is offer it as a choice, and a Bluetooth port is not a choice.
119
+ *
120
+ * Implementations that predate this method reject. The caller then keeps
121
+ * whatever the driver picks on its own.
122
+ */
123
+ listReaderPorts(): Promise<{
124
+ ports: ReaderPort[];
125
+ }>;
126
+ /**
127
+ * Use this reader from now on. Reconnects when another port is open.
128
+ *
129
+ * The choice itself is not stored here: the driver is restarted with the
130
+ * app, and where a choice belongs is the caller's question. The caller
131
+ * remembers the path and says it again on the next start.
132
+ *
133
+ * Rejects when the path is not in the current `listReaderPorts()` — a
134
+ * remembered reader that was unplugged has to be chosen again, not opened
135
+ * blindly.
136
+ */
137
+ selectReaderPort(options: {
138
+ path: string;
139
+ }): Promise<void>;
29
140
  isConnected(): Promise<{
30
141
  connected: boolean;
31
142
  }>;
@@ -80,4 +191,7 @@ export interface RFIDPlugin extends Plugin {
80
191
  playSound: boolean;
81
192
  }): Promise<void>;
82
193
  stopSearch(): Promise<void>;
194
+ /** Every connection state change. Fires only on a change, not on a poll. */
195
+ addListener(eventName: 'onConnectionState', listener: (state: ReaderConnectionState) => void): Promise<PluginListenerHandle>;
196
+ addListener(eventName: string, listenerFunc: (...args: any[]) => any): Promise<PluginListenerHandle>;
83
197
  }
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { Plugin } from '@capacitor/core';\n\n/**\n * What the connected Reader can do. Mode gating comes from here, not from the\n * reader type — a desktop reader that can write must not be gated like one\n * that cannot.\n */\nexport interface ReaderCapabilities {\n /** Reader can write an EPC to a tag (Generate & Set mode depends on it). */\n canWrite: boolean;\n /** Reader reports RSSI (Find mode depends on it). */\n hasRssi: boolean;\n /** Reader is a handheld terminal rather than a desk device. */\n isHandheld: boolean;\n /** Output power range in dBm. The power slider renders this range. */\n power: {\n min: number;\n max: number;\n default: number;\n };\n}\n\nexport interface RFIDPlugin extends Plugin {\n /**\n * Capabilities of the connected Reader.\n *\n * Implementations that predate this method reject. That is a signal, not a\n * failure: the caller treats a rejection as a handheld Mobile Reader.\n */\n getCapabilities(): Promise<ReaderCapabilities>;\n\n isConnected(): Promise<{ connected: boolean }>;\n\n startScan(): Promise<void>;\n stopScan(): Promise<void>;\n clearData(): Promise<void>;\n\n getScanData(): Promise<any>;\n getOutputPower(): Promise<{ value: number }>;\n setOutputPower(options: { power: number }): Promise<{ value: number }>;\n\n getRange(): Promise<{ value: number }>;\n setRange(options: { range: number }): Promise<{ value: number }>;\n\n getQueryMode(): Promise<{ value: 0 | 1 | 2 | 3 }>;\n setQueryMode(options: {\n queryMode:\n | 0 // epc\n | 1 // epc+tid\n | 2 // epc+user\n | 3; // fasttid\n }): Promise<{ value: number }>;\n\n getReaderType(): Promise<{ value: number }>; // 80 - short, others - long distance mode\n getFirmwareVersion(): Promise<{ value: string }>;\n\n writeEpc(options: {\n epc: string;\n password?: string;\n }): Promise<{ value: number }>;\n writeEpcString(options: {\n epc: string;\n password?: string;\n }): Promise<{ value: number }>;\n\n startSearch(options: { searchableTags: string[], playSound: boolean }): Promise<void>;\n stopSearch(): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"definitions.js","sourceRoot":"","sources":["../../src/definitions.ts"],"names":[],"mappings":"","sourcesContent":["import type { Plugin, PluginListenerHandle } from '@capacitor/core';\n\n/**\n * What the connected Reader can do. Mode gating comes from here, not from the\n * reader type — a desktop reader that can write must not be gated like one\n * that cannot.\n */\nexport interface ReaderCapabilities {\n /** Reader can write an EPC to a tag (Generate & Set mode depends on it). */\n canWrite: boolean;\n /** Reader reports RSSI (Find mode depends on it). */\n hasRssi: boolean;\n /** Reader is a handheld terminal rather than a desk device. */\n isHandheld: boolean;\n /** Output power range in dBm. The power slider renders this range. */\n power: {\n min: number;\n max: number;\n default: number;\n };\n}\n\n/**\n * The four states an operator can act on.\n *\n * `isConnected()` is too coarse for this: a reader that is plugged in but\n * silent answers `false` the same way a missing one does, and the operator\n * needs a different move in each case. `isConnected()` stays as it is.\n */\nexport type ReaderConnectionStatus =\n /** An attempt is running. */\n | 'connecting'\n /** The reader is open and answering. */\n | 'connected'\n /** No reader to talk to — nothing found, or found but not openable. */\n | 'not-found'\n /** The port opened but the reader did not answer a command. */\n | 'not-responding';\n\n/**\n * Why the reader is unusable. The status alone does not say what to do next:\n * a busy reader needs another program closed, a missing one needs the\n * connection checked. The UI picks its message from the reason when there is\n * one.\n *\n * These name what happened to the *reader*, never how it is wired. A driver\n * for a device with no serial port has to be able to answer in this\n * vocabulary too, and the operator has to be told the truth either way —\n * so nothing here says \"COM port\" or \"USB\". The device-specific sentence\n * belongs in `detail`.\n */\nexport type ReaderConnectionReason =\n /** Another program holds the reader. Only one may have it at a time. */\n | 'busy'\n /** No reader in sight — nothing connected, or nothing that is a reader. */\n | 'not-detected'\n /** The reader was there and then went away. */\n | 'disconnected'\n /** The reader is reachable but did not answer. */\n | 'no-answer'\n /**\n * Several readers are connected and none was picked. Guessing here would\n * attach tags read by the wrong device, so nothing is opened until\n * `selectReaderPort` says which one.\n */\n | 'not-chosen';\n\nexport interface ReaderConnectionState {\n status: ReaderConnectionStatus;\n reason?: ReaderConnectionReason;\n /**\n * Driver text: the port list, the command code, the OS error. This is the\n * one field allowed to talk about the wiring, because nothing branches on\n * it — it is shown as-is for support, not translated.\n */\n detail?: string;\n}\n\n/**\n * One reader the driver could talk to.\n *\n * `path` is the identity — `COM3` on Windows, `/dev/cu.usbserial-110`\n * elsewhere. Two identical CH340 adapters carry no serial number of their own,\n * so the path is all that separates them.\n *\n * The caller treats it as **opaque**: it compares, stores and hands it back,\n * and never reads meaning out of it. A driver for a device that is not a\n * serial port puts its own identity here.\n */\nexport interface ReaderPort {\n path: string;\n /** What the OS calls the device. For the operator to read, not to match on. */\n label?: string;\n vendorId?: string;\n productId?: string;\n}\n\nexport interface RFIDPlugin extends Plugin {\n /**\n * Capabilities of the connected Reader.\n *\n * Implementations that predate this method reject. That is a signal, not a\n * failure: the caller treats a rejection as a handheld Mobile Reader.\n */\n getCapabilities(): Promise<ReaderCapabilities>;\n\n /**\n * Connection state for the first render. The stream of later changes comes\n * from the `onConnectionState` listener — \"not responding\" is only found out\n * after a timeout, so it cannot be a return value.\n *\n * A reader is an exclusive resource, so its state is only knowable while it\n * is held: an implementation that is not connected may start an attempt here\n * and answer `connecting`, with the outcome arriving on the listener.\n *\n * Implementations that predate this method reject. The caller then hides the\n * indicator instead of guessing.\n */\n getConnectionState(): Promise<ReaderConnectionState>;\n\n /**\n * The readers plugged in right now, newest listing every call — a port that\n * was there a minute ago may be gone.\n *\n * Only readers, not every serial port: what the caller does with this list\n * is offer it as a choice, and a Bluetooth port is not a choice.\n *\n * Implementations that predate this method reject. The caller then keeps\n * whatever the driver picks on its own.\n */\n listReaderPorts(): Promise<{ ports: ReaderPort[] }>;\n\n /**\n * Use this reader from now on. Reconnects when another port is open.\n *\n * The choice itself is not stored here: the driver is restarted with the\n * app, and where a choice belongs is the caller's question. The caller\n * remembers the path and says it again on the next start.\n *\n * Rejects when the path is not in the current `listReaderPorts()` — a\n * remembered reader that was unplugged has to be chosen again, not opened\n * blindly.\n */\n selectReaderPort(options: { path: string }): Promise<void>;\n\n isConnected(): Promise<{ connected: boolean }>;\n\n startScan(): Promise<void>;\n stopScan(): Promise<void>;\n clearData(): Promise<void>;\n\n getScanData(): Promise<any>;\n getOutputPower(): Promise<{ value: number }>;\n setOutputPower(options: { power: number }): Promise<{ value: number }>;\n\n getRange(): Promise<{ value: number }>;\n setRange(options: { range: number }): Promise<{ value: number }>;\n\n getQueryMode(): Promise<{ value: 0 | 1 | 2 | 3 }>;\n setQueryMode(options: {\n queryMode:\n | 0 // epc\n | 1 // epc+tid\n | 2 // epc+user\n | 3; // fasttid\n }): Promise<{ value: number }>;\n\n getReaderType(): Promise<{ value: number }>; // 80 - short, others - long distance mode\n getFirmwareVersion(): Promise<{ value: string }>;\n\n writeEpc(options: {\n epc: string;\n password?: string;\n }): Promise<{ value: number }>;\n writeEpcString(options: {\n epc: string;\n password?: string;\n }): Promise<{ value: number }>;\n\n startSearch(options: { searchableTags: string[], playSound: boolean }): Promise<void>;\n stopSearch(): Promise<void>;\n\n /** Every connection state change. Fires only on a change, not on a poll. */\n addListener(\n eventName: 'onConnectionState',\n listener: (state: ReaderConnectionState) => void,\n ): Promise<PluginListenerHandle>;\n\n addListener(\n eventName: string,\n listenerFunc: (...args: any[]) => any,\n ): Promise<PluginListenerHandle>;\n}\n"]}
@@ -0,0 +1,30 @@
1
+ import type { RFIDPlugin } from './definitions';
2
+ /**
3
+ * The bridge built by the `@capacitor-community/electron` preload
4
+ * (`electron-rt.ts`). Its `addListener` returns an id **string** and is
5
+ * synchronous — `removeListener` takes that same id.
6
+ */
7
+ export interface ElectronBridge {
8
+ addListener(eventName: string, callback: (...args: any[]) => void): string;
9
+ removeListener(id: string): void;
10
+ }
11
+ /**
12
+ * On native platforms Capacitor wraps the `addListener` result into a
13
+ * `PluginListenerHandle` itself (`addListenerNative`), but only when
14
+ * `PluginHeaders` are present. Electron has none, so the bridge's raw id
15
+ * string reaches the UI as-is and `handle.remove()` is missing: the listener
16
+ * is never removed, and scanning does not start the second time the modal
17
+ * opens.
18
+ *
19
+ * Do not wrap it with a `Proxy`. Methods of an object exposed through
20
+ * `contextBridge` arrive as `writable: false, configurable: false`, and for
21
+ * such a property a `get` trap must return **that exact value** — even a
22
+ * bound copy is rejected. Otherwise the engine throws the error we saw on
23
+ * the device:
24
+ * TypeError: 'get' on proxy: property 'startScan' is a read-only and
25
+ * non-configurable data property on the proxy target but the proxy did
26
+ * not return its actual value
27
+ * So the bridge is used as the prototype instead: the other methods are
28
+ * found on it as usual, and only `addListener` is defined on top.
29
+ */
30
+ export declare function withListenerHandles(bridge: ElectronBridge): RFIDPlugin;
@@ -0,0 +1,55 @@
1
+ const SCAN_EVENT = 'onScanEvent';
2
+ /**
3
+ * The Electron main process sends the tags it read in one message per 250 ms
4
+ * window instead of one per frame — a single tag is read about 44 times a
5
+ * second. The contract does not change: the array is unrolled here, so a
6
+ * listener still gets one tag per call.
7
+ */
8
+ const SCAN_BATCH_EVENT = 'onScanEventBatch';
9
+ /**
10
+ * On native platforms Capacitor wraps the `addListener` result into a
11
+ * `PluginListenerHandle` itself (`addListenerNative`), but only when
12
+ * `PluginHeaders` are present. Electron has none, so the bridge's raw id
13
+ * string reaches the UI as-is and `handle.remove()` is missing: the listener
14
+ * is never removed, and scanning does not start the second time the modal
15
+ * opens.
16
+ *
17
+ * Do not wrap it with a `Proxy`. Methods of an object exposed through
18
+ * `contextBridge` arrive as `writable: false, configurable: false`, and for
19
+ * such a property a `get` trap must return **that exact value** — even a
20
+ * bound copy is rejected. Otherwise the engine throws the error we saw on
21
+ * the device:
22
+ * TypeError: 'get' on proxy: property 'startScan' is a read-only and
23
+ * non-configurable data property on the proxy target but the proxy did
24
+ * not return its actual value
25
+ * So the bridge is used as the prototype instead: the other methods are
26
+ * found on it as usual, and only `addListener` is defined on top.
27
+ */
28
+ export function withListenerHandles(bridge) {
29
+ const plugin = Object.create(bridge);
30
+ // A plain `plugin.addListener = ...` does not work: the prototype property
31
+ // is `writable: false`, so [[Set]] blocks it.
32
+ Object.defineProperty(plugin, 'addListener', {
33
+ value: async (eventName, callback) => {
34
+ const ids = [bridge.addListener(eventName, callback)];
35
+ if (eventName === SCAN_EVENT) {
36
+ // A desktop app older than the batch sends one message per tag on
37
+ // `onScanEvent`, a newer one sends only batches. Both are listened
38
+ // for, because the bundle in the browser and the installed desktop
39
+ // app are updated separately; only one of them ever fires.
40
+ ids.push(bridge.addListener(SCAN_BATCH_EVENT, (batch) => {
41
+ for (const event of batch)
42
+ callback(event);
43
+ }));
44
+ }
45
+ return {
46
+ remove: async () => {
47
+ for (const id of ids)
48
+ bridge.removeListener(id);
49
+ },
50
+ };
51
+ },
52
+ });
53
+ return plugin;
54
+ }
55
+ //# sourceMappingURL=electron-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"electron-bridge.js","sourceRoot":"","sources":["../../src/electron-bridge.ts"],"names":[],"mappings":"AAcA,MAAM,UAAU,GAAG,aAAa,CAAC;AAEjC;;;;;GAKG;AACH,MAAM,gBAAgB,GAAG,kBAAkB,CAAC;AAE5C;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAsB;IACxD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAe,CAAC;IAEnD,2EAA2E;IAC3E,8CAA8C;IAC9C,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE;QAC3C,KAAK,EAAE,KAAK,EACV,SAAiB,EACjB,QAAkC,EACH,EAAE;YACjC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC,CAAC;YAEtD,IAAI,SAAS,KAAK,UAAU,EAAE;gBAC5B,kEAAkE;gBAClE,mEAAmE;gBACnE,mEAAmE;gBACnE,2DAA2D;gBAC3D,GAAG,CAAC,IAAI,CACN,MAAM,CAAC,WAAW,CAAC,gBAAgB,EAAE,CAAC,KAAgB,EAAE,EAAE;oBACxD,KAAK,MAAM,KAAK,IAAI,KAAK;wBAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;gBAC7C,CAAC,CAAC,CACH,CAAC;aACH;YAED,OAAO;gBACL,MAAM,EAAE,KAAK,IAAI,EAAE;oBACjB,KAAK,MAAM,EAAE,IAAI,GAAG;wBAAE,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;gBAClD,CAAC;aACF,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC","sourcesContent":["import type { PluginListenerHandle } from '@capacitor/core';\n\nimport type { RFIDPlugin } from './definitions';\n\n/**\n * The bridge built by the `@capacitor-community/electron` preload\n * (`electron-rt.ts`). Its `addListener` returns an id **string** and is\n * synchronous — `removeListener` takes that same id.\n */\nexport interface ElectronBridge {\n addListener(eventName: string, callback: (...args: any[]) => void): string;\n removeListener(id: string): void;\n}\n\nconst SCAN_EVENT = 'onScanEvent';\n\n/**\n * The Electron main process sends the tags it read in one message per 250 ms\n * window instead of one per frame — a single tag is read about 44 times a\n * second. The contract does not change: the array is unrolled here, so a\n * listener still gets one tag per call.\n */\nconst SCAN_BATCH_EVENT = 'onScanEventBatch';\n\n/**\n * On native platforms Capacitor wraps the `addListener` result into a\n * `PluginListenerHandle` itself (`addListenerNative`), but only when\n * `PluginHeaders` are present. Electron has none, so the bridge's raw id\n * string reaches the UI as-is and `handle.remove()` is missing: the listener\n * is never removed, and scanning does not start the second time the modal\n * opens.\n *\n * Do not wrap it with a `Proxy`. Methods of an object exposed through\n * `contextBridge` arrive as `writable: false, configurable: false`, and for\n * such a property a `get` trap must return **that exact value** — even a\n * bound copy is rejected. Otherwise the engine throws the error we saw on\n * the device:\n * TypeError: 'get' on proxy: property 'startScan' is a read-only and\n * non-configurable data property on the proxy target but the proxy did\n * not return its actual value\n * So the bridge is used as the prototype instead: the other methods are\n * found on it as usual, and only `addListener` is defined on top.\n */\nexport function withListenerHandles(bridge: ElectronBridge): RFIDPlugin {\n const plugin = Object.create(bridge) as RFIDPlugin;\n\n // A plain `plugin.addListener = ...` does not work: the prototype property\n // is `writable: false`, so [[Set]] blocks it.\n Object.defineProperty(plugin, 'addListener', {\n value: async (\n eventName: string,\n callback: (...args: any[]) => void,\n ): Promise<PluginListenerHandle> => {\n const ids = [bridge.addListener(eventName, callback)];\n\n if (eventName === SCAN_EVENT) {\n // A desktop app older than the batch sends one message per tag on\n // `onScanEvent`, a newer one sends only batches. Both are listened\n // for, because the bundle in the browser and the installed desktop\n // app are updated separately; only one of them ever fires.\n ids.push(\n bridge.addListener(SCAN_BATCH_EVENT, (batch: unknown[]) => {\n for (const event of batch) callback(event);\n }),\n );\n }\n\n return {\n remove: async () => {\n for (const id of ids) bridge.removeListener(id);\n },\n };\n },\n });\n\n return plugin;\n}\n"]}
package/dist/esm/index.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { registerPlugin } from '@capacitor/core';
2
+ import { withListenerHandles } from './electron-bridge';
2
3
  const RFID = registerPlugin('RFID', {
3
4
  web: () => import('./web').then(m => new m.RFIDWeb()),
4
5
  electron: () => {
5
6
  var _a, _b;
6
- const plugin = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;
7
- if (!plugin) {
7
+ const bridge = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;
8
+ if (!bridge) {
8
9
  throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');
9
10
  }
10
- return plugin;
11
+ return withListenerHandles(bridge);
11
12
  },
12
13
  });
13
14
  export * from './definitions';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAQjD,MAAM,IAAI,GAAG,cAAc,CAAa,MAAM,EAAE;IAC9C,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,QAAQ,EAAE,GAAG,EAAE;;QACb,MAAM,MAAM,eAAI,MAAyB,CAAC,uBAAuB,0CAAE,OAAO,0CACtE,IAAI,CAAC;QAET,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;SACH;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { RFIDPlugin } from './definitions';\n\ntype ElectronWindow = Window & {\n CapacitorCustomPlatform?: { plugins?: { RFID?: RFIDPlugin } };\n};\n\nconst RFID = registerPlugin<RFIDPlugin>('RFID', {\n web: () => import('./web').then(m => new m.RFIDWeb()),\n electron: () => {\n const plugin = (window as ElectronWindow).CapacitorCustomPlatform?.plugins\n ?.RFID;\n\n if (!plugin) {\n throw new Error(\n 'RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.',\n );\n }\n\n return plugin;\n },\n});\n\nexport * from './definitions';\nexport { RFID };\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAIjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAMxD,MAAM,IAAI,GAAG,cAAc,CAAa,MAAM,EAAE;IAC9C,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;IACrD,QAAQ,EAAE,GAAG,EAAE;;QACb,MAAM,MAAM,eAAI,MAAyB,CAAC,uBAAuB,0CAAE,OAAO,0CACtE,IAAI,CAAC;QAET,IAAI,CAAC,MAAM,EAAE;YACX,MAAM,IAAI,KAAK,CACb,6FAA6F,CAC9F,CAAC;SACH;QAED,OAAO,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACrC,CAAC;CACF,CAAC,CAAC;AAEH,cAAc,eAAe,CAAC;AAC9B,OAAO,EAAE,IAAI,EAAE,CAAC","sourcesContent":["import { registerPlugin } from '@capacitor/core';\n\nimport type { RFIDPlugin } from './definitions';\nimport type { ElectronBridge } from './electron-bridge';\nimport { withListenerHandles } from './electron-bridge';\n\ntype ElectronWindow = Window & {\n CapacitorCustomPlatform?: { plugins?: { RFID?: ElectronBridge } };\n};\n\nconst RFID = registerPlugin<RFIDPlugin>('RFID', {\n web: () => import('./web').then(m => new m.RFIDWeb()),\n electron: () => {\n const bridge = (window as ElectronWindow).CapacitorCustomPlatform?.plugins\n ?.RFID;\n\n if (!bridge) {\n throw new Error(\n 'RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.',\n );\n }\n\n return withListenerHandles(bridge);\n },\n});\n\nexport * from './definitions';\nexport { RFID };\n"]}
package/dist/esm/web.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { WebPlugin } from '@capacitor/core';
2
2
  import type { PluginListenerHandle } from '@capacitor/core';
3
- import type { ReaderCapabilities, RFIDPlugin } from './definitions';
3
+ import type { ReaderCapabilities, ReaderConnectionState, ReaderPort, RFIDPlugin } from './definitions';
4
4
  export declare class RFIDWeb extends WebPlugin implements RFIDPlugin {
5
5
  /**
6
6
  * The one method the browser can answer truthfully. Everything else rejects:
@@ -15,6 +15,20 @@ export declare class RFIDWeb extends WebPlugin implements RFIDPlugin {
15
15
  */
16
16
  addListener(): Promise<PluginListenerHandle>;
17
17
  getCapabilities(): Promise<ReaderCapabilities>;
18
+ /**
19
+ * Not `not-found`: that state tells the operator to check the cable, and
20
+ * there is no cable to check in a browser. The rejection carries the one
21
+ * useful sentence — open the desktop app or the handheld.
22
+ */
23
+ getConnectionState(): Promise<ReaderConnectionState>;
24
+ /**
25
+ * Not an empty list: "no readers plugged in" would send the operator to look
26
+ * for a cable, and the browser has no serial ports to look at.
27
+ */
28
+ listReaderPorts(): Promise<{
29
+ ports: ReaderPort[];
30
+ }>;
31
+ selectReaderPort(): Promise<void>;
18
32
  startScan(): Promise<void>;
19
33
  stopScan(): Promise<void>;
20
34
  clearData(): Promise<void>;
package/dist/esm/web.js CHANGED
@@ -18,6 +18,24 @@ export class RFIDWeb extends WebPlugin {
18
18
  async getCapabilities() {
19
19
  throw this.unimplemented(NO_READER_ON_WEB);
20
20
  }
21
+ /**
22
+ * Not `not-found`: that state tells the operator to check the cable, and
23
+ * there is no cable to check in a browser. The rejection carries the one
24
+ * useful sentence — open the desktop app or the handheld.
25
+ */
26
+ async getConnectionState() {
27
+ throw this.unimplemented(NO_READER_ON_WEB);
28
+ }
29
+ /**
30
+ * Not an empty list: "no readers plugged in" would send the operator to look
31
+ * for a cable, and the browser has no serial ports to look at.
32
+ */
33
+ async listReaderPorts() {
34
+ throw this.unimplemented(NO_READER_ON_WEB);
35
+ }
36
+ async selectReaderPort() {
37
+ throw this.unimplemented(NO_READER_ON_WEB);
38
+ }
21
39
  async startScan() {
22
40
  throw this.unimplemented(NO_READER_ON_WEB);
23
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAK5C,MAAM,gBAAgB,GACpB,0FAA0F,CAAC;AAE7F,MAAM,OAAO,OAAQ,SAAQ,SAAS;IACpC;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { PluginListenerHandle } from '@capacitor/core';\n\nimport type { ReaderCapabilities, RFIDPlugin } from './definitions';\n\nconst NO_READER_ON_WEB =\n 'No RFID Reader in the browser. Use the desktop app (ox-desktop) or the Android handheld.';\n\nexport class RFIDWeb extends WebPlugin implements RFIDPlugin {\n /**\n * The one method the browser can answer truthfully. Everything else rejects:\n * a silent `resolve` makes the UI report a success that never happened.\n */\n async isConnected(): Promise<{ connected: boolean }> {\n return { connected: false };\n }\n\n /**\n * Events never fire without a Reader, so registering a listener here would be\n * the same silent lie as a resolving `startScan()`.\n */\n async addListener(): Promise<PluginListenerHandle> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getCapabilities(): Promise<ReaderCapabilities> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async startScan(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async stopScan(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async clearData(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getScanData(): Promise<any> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getOutputPower(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async setOutputPower(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getRange(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async setRange(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getQueryMode(): Promise<{ value: 0 | 1 | 2 | 3 }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async setQueryMode(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getReaderType(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getFirmwareVersion(): Promise<{ value: string }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async writeEpc(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async writeEpcString(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async startSearch(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async stopSearch(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n}\n"]}
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../../src/web.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAU5C,MAAM,gBAAgB,GACpB,0FAA0F,CAAC;AAE7F,MAAM,OAAO,OAAQ,SAAQ,SAAS;IACpC;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,SAAS;QACb,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,cAAc;QAClB,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,WAAW;QACf,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,UAAU;QACd,MAAM,IAAI,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;CACF","sourcesContent":["import { WebPlugin } from '@capacitor/core';\nimport type { PluginListenerHandle } from '@capacitor/core';\n\nimport type {\n ReaderCapabilities,\n ReaderConnectionState,\n ReaderPort,\n RFIDPlugin,\n} from './definitions';\n\nconst NO_READER_ON_WEB =\n 'No RFID Reader in the browser. Use the desktop app (ox-desktop) or the Android handheld.';\n\nexport class RFIDWeb extends WebPlugin implements RFIDPlugin {\n /**\n * The one method the browser can answer truthfully. Everything else rejects:\n * a silent `resolve` makes the UI report a success that never happened.\n */\n async isConnected(): Promise<{ connected: boolean }> {\n return { connected: false };\n }\n\n /**\n * Events never fire without a Reader, so registering a listener here would be\n * the same silent lie as a resolving `startScan()`.\n */\n async addListener(): Promise<PluginListenerHandle> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getCapabilities(): Promise<ReaderCapabilities> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n /**\n * Not `not-found`: that state tells the operator to check the cable, and\n * there is no cable to check in a browser. The rejection carries the one\n * useful sentence — open the desktop app or the handheld.\n */\n async getConnectionState(): Promise<ReaderConnectionState> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n /**\n * Not an empty list: \"no readers plugged in\" would send the operator to look\n * for a cable, and the browser has no serial ports to look at.\n */\n async listReaderPorts(): Promise<{ ports: ReaderPort[] }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async selectReaderPort(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async startScan(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async stopScan(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async clearData(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getScanData(): Promise<any> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getOutputPower(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async setOutputPower(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getRange(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async setRange(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getQueryMode(): Promise<{ value: 0 | 1 | 2 | 3 }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async setQueryMode(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getReaderType(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async getFirmwareVersion(): Promise<{ value: string }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async writeEpc(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async writeEpcString(): Promise<{ value: number }> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async startSearch(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n\n async stopSearch(): Promise<void> {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n}\n"]}
@@ -4,15 +4,70 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var core = require('@capacitor/core');
6
6
 
7
+ const SCAN_EVENT = 'onScanEvent';
8
+ /**
9
+ * The Electron main process sends the tags it read in one message per 250 ms
10
+ * window instead of one per frame — a single tag is read about 44 times a
11
+ * second. The contract does not change: the array is unrolled here, so a
12
+ * listener still gets one tag per call.
13
+ */
14
+ const SCAN_BATCH_EVENT = 'onScanEventBatch';
15
+ /**
16
+ * On native platforms Capacitor wraps the `addListener` result into a
17
+ * `PluginListenerHandle` itself (`addListenerNative`), but only when
18
+ * `PluginHeaders` are present. Electron has none, so the bridge's raw id
19
+ * string reaches the UI as-is and `handle.remove()` is missing: the listener
20
+ * is never removed, and scanning does not start the second time the modal
21
+ * opens.
22
+ *
23
+ * Do not wrap it with a `Proxy`. Methods of an object exposed through
24
+ * `contextBridge` arrive as `writable: false, configurable: false`, and for
25
+ * such a property a `get` trap must return **that exact value** — even a
26
+ * bound copy is rejected. Otherwise the engine throws the error we saw on
27
+ * the device:
28
+ * TypeError: 'get' on proxy: property 'startScan' is a read-only and
29
+ * non-configurable data property on the proxy target but the proxy did
30
+ * not return its actual value
31
+ * So the bridge is used as the prototype instead: the other methods are
32
+ * found on it as usual, and only `addListener` is defined on top.
33
+ */
34
+ function withListenerHandles(bridge) {
35
+ const plugin = Object.create(bridge);
36
+ // A plain `plugin.addListener = ...` does not work: the prototype property
37
+ // is `writable: false`, so [[Set]] blocks it.
38
+ Object.defineProperty(plugin, 'addListener', {
39
+ value: async (eventName, callback) => {
40
+ const ids = [bridge.addListener(eventName, callback)];
41
+ if (eventName === SCAN_EVENT) {
42
+ // A desktop app older than the batch sends one message per tag on
43
+ // `onScanEvent`, a newer one sends only batches. Both are listened
44
+ // for, because the bundle in the browser and the installed desktop
45
+ // app are updated separately; only one of them ever fires.
46
+ ids.push(bridge.addListener(SCAN_BATCH_EVENT, (batch) => {
47
+ for (const event of batch)
48
+ callback(event);
49
+ }));
50
+ }
51
+ return {
52
+ remove: async () => {
53
+ for (const id of ids)
54
+ bridge.removeListener(id);
55
+ },
56
+ };
57
+ },
58
+ });
59
+ return plugin;
60
+ }
61
+
7
62
  const RFID = core.registerPlugin('RFID', {
8
63
  web: () => Promise.resolve().then(function () { return web; }).then(m => new m.RFIDWeb()),
9
64
  electron: () => {
10
65
  var _a, _b;
11
- const plugin = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;
12
- if (!plugin) {
66
+ const bridge = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;
67
+ if (!bridge) {
13
68
  throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');
14
69
  }
15
- return plugin;
70
+ return withListenerHandles(bridge);
16
71
  },
17
72
  });
18
73
 
@@ -35,6 +90,24 @@ class RFIDWeb extends core.WebPlugin {
35
90
  async getCapabilities() {
36
91
  throw this.unimplemented(NO_READER_ON_WEB);
37
92
  }
93
+ /**
94
+ * Not `not-found`: that state tells the operator to check the cable, and
95
+ * there is no cable to check in a browser. The rejection carries the one
96
+ * useful sentence — open the desktop app or the handheld.
97
+ */
98
+ async getConnectionState() {
99
+ throw this.unimplemented(NO_READER_ON_WEB);
100
+ }
101
+ /**
102
+ * Not an empty list: "no readers plugged in" would send the operator to look
103
+ * for a cable, and the browser has no serial ports to look at.
104
+ */
105
+ async listReaderPorts() {
106
+ throw this.unimplemented(NO_READER_ON_WEB);
107
+ }
108
+ async selectReaderPort() {
109
+ throw this.unimplemented(NO_READER_ON_WEB);
110
+ }
38
111
  async startScan() {
39
112
  throw this.unimplemented(NO_READER_ON_WEB);
40
113
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.cjs.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst RFID = registerPlugin('RFID', {\n web: () => import('./web').then(m => new m.RFIDWeb()),\n electron: () => {\n var _a, _b;\n const plugin = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;\n if (!plugin) {\n throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');\n }\n return plugin;\n },\n});\nexport * from './definitions';\nexport { RFID };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nconst NO_READER_ON_WEB = 'No RFID Reader in the browser. Use the desktop app (ox-desktop) or the Android handheld.';\nexport class RFIDWeb extends WebPlugin {\n /**\n * The one method the browser can answer truthfully. Everything else rejects:\n * a silent `resolve` makes the UI report a success that never happened.\n */\n async isConnected() {\n return { connected: false };\n }\n /**\n * Events never fire without a Reader, so registering a listener here would be\n * the same silent lie as a resolving `startScan()`.\n */\n async addListener() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getCapabilities() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async clearData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getScanData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getReaderType() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getFirmwareVersion() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpc() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpcString() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.cjs.js","sources":["esm/electron-bridge.js","esm/index.js","esm/web.js"],"sourcesContent":["const SCAN_EVENT = 'onScanEvent';\n/**\n * The Electron main process sends the tags it read in one message per 250 ms\n * window instead of one per frame — a single tag is read about 44 times a\n * second. The contract does not change: the array is unrolled here, so a\n * listener still gets one tag per call.\n */\nconst SCAN_BATCH_EVENT = 'onScanEventBatch';\n/**\n * On native platforms Capacitor wraps the `addListener` result into a\n * `PluginListenerHandle` itself (`addListenerNative`), but only when\n * `PluginHeaders` are present. Electron has none, so the bridge's raw id\n * string reaches the UI as-is and `handle.remove()` is missing: the listener\n * is never removed, and scanning does not start the second time the modal\n * opens.\n *\n * Do not wrap it with a `Proxy`. Methods of an object exposed through\n * `contextBridge` arrive as `writable: false, configurable: false`, and for\n * such a property a `get` trap must return **that exact value** — even a\n * bound copy is rejected. Otherwise the engine throws the error we saw on\n * the device:\n * TypeError: 'get' on proxy: property 'startScan' is a read-only and\n * non-configurable data property on the proxy target but the proxy did\n * not return its actual value\n * So the bridge is used as the prototype instead: the other methods are\n * found on it as usual, and only `addListener` is defined on top.\n */\nexport function withListenerHandles(bridge) {\n const plugin = Object.create(bridge);\n // A plain `plugin.addListener = ...` does not work: the prototype property\n // is `writable: false`, so [[Set]] blocks it.\n Object.defineProperty(plugin, 'addListener', {\n value: async (eventName, callback) => {\n const ids = [bridge.addListener(eventName, callback)];\n if (eventName === SCAN_EVENT) {\n // A desktop app older than the batch sends one message per tag on\n // `onScanEvent`, a newer one sends only batches. Both are listened\n // for, because the bundle in the browser and the installed desktop\n // app are updated separately; only one of them ever fires.\n ids.push(bridge.addListener(SCAN_BATCH_EVENT, (batch) => {\n for (const event of batch)\n callback(event);\n }));\n }\n return {\n remove: async () => {\n for (const id of ids)\n bridge.removeListener(id);\n },\n };\n },\n });\n return plugin;\n}\n//# sourceMappingURL=electron-bridge.js.map","import { registerPlugin } from '@capacitor/core';\nimport { withListenerHandles } from './electron-bridge';\nconst RFID = registerPlugin('RFID', {\n web: () => import('./web').then(m => new m.RFIDWeb()),\n electron: () => {\n var _a, _b;\n const bridge = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;\n if (!bridge) {\n throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');\n }\n return withListenerHandles(bridge);\n },\n});\nexport * from './definitions';\nexport { RFID };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nconst NO_READER_ON_WEB = 'No RFID Reader in the browser. Use the desktop app (ox-desktop) or the Android handheld.';\nexport class RFIDWeb extends WebPlugin {\n /**\n * The one method the browser can answer truthfully. Everything else rejects:\n * a silent `resolve` makes the UI report a success that never happened.\n */\n async isConnected() {\n return { connected: false };\n }\n /**\n * Events never fire without a Reader, so registering a listener here would be\n * the same silent lie as a resolving `startScan()`.\n */\n async addListener() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getCapabilities() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n /**\n * Not `not-found`: that state tells the operator to check the cable, and\n * there is no cable to check in a browser. The rejection carries the one\n * useful sentence — open the desktop app or the handheld.\n */\n async getConnectionState() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n /**\n * Not an empty list: \"no readers plugged in\" would send the operator to look\n * for a cable, and the browser has no serial ports to look at.\n */\n async listReaderPorts() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async selectReaderPort() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async clearData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getScanData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getReaderType() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getFirmwareVersion() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpc() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpcString() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
package/dist/plugin.js CHANGED
@@ -1,15 +1,70 @@
1
1
  var capacitorExample = (function (exports, core) {
2
2
  'use strict';
3
3
 
4
+ const SCAN_EVENT = 'onScanEvent';
5
+ /**
6
+ * The Electron main process sends the tags it read in one message per 250 ms
7
+ * window instead of one per frame — a single tag is read about 44 times a
8
+ * second. The contract does not change: the array is unrolled here, so a
9
+ * listener still gets one tag per call.
10
+ */
11
+ const SCAN_BATCH_EVENT = 'onScanEventBatch';
12
+ /**
13
+ * On native platforms Capacitor wraps the `addListener` result into a
14
+ * `PluginListenerHandle` itself (`addListenerNative`), but only when
15
+ * `PluginHeaders` are present. Electron has none, so the bridge's raw id
16
+ * string reaches the UI as-is and `handle.remove()` is missing: the listener
17
+ * is never removed, and scanning does not start the second time the modal
18
+ * opens.
19
+ *
20
+ * Do not wrap it with a `Proxy`. Methods of an object exposed through
21
+ * `contextBridge` arrive as `writable: false, configurable: false`, and for
22
+ * such a property a `get` trap must return **that exact value** — even a
23
+ * bound copy is rejected. Otherwise the engine throws the error we saw on
24
+ * the device:
25
+ * TypeError: 'get' on proxy: property 'startScan' is a read-only and
26
+ * non-configurable data property on the proxy target but the proxy did
27
+ * not return its actual value
28
+ * So the bridge is used as the prototype instead: the other methods are
29
+ * found on it as usual, and only `addListener` is defined on top.
30
+ */
31
+ function withListenerHandles(bridge) {
32
+ const plugin = Object.create(bridge);
33
+ // A plain `plugin.addListener = ...` does not work: the prototype property
34
+ // is `writable: false`, so [[Set]] blocks it.
35
+ Object.defineProperty(plugin, 'addListener', {
36
+ value: async (eventName, callback) => {
37
+ const ids = [bridge.addListener(eventName, callback)];
38
+ if (eventName === SCAN_EVENT) {
39
+ // A desktop app older than the batch sends one message per tag on
40
+ // `onScanEvent`, a newer one sends only batches. Both are listened
41
+ // for, because the bundle in the browser and the installed desktop
42
+ // app are updated separately; only one of them ever fires.
43
+ ids.push(bridge.addListener(SCAN_BATCH_EVENT, (batch) => {
44
+ for (const event of batch)
45
+ callback(event);
46
+ }));
47
+ }
48
+ return {
49
+ remove: async () => {
50
+ for (const id of ids)
51
+ bridge.removeListener(id);
52
+ },
53
+ };
54
+ },
55
+ });
56
+ return plugin;
57
+ }
58
+
4
59
  const RFID = core.registerPlugin('RFID', {
5
60
  web: () => Promise.resolve().then(function () { return web; }).then(m => new m.RFIDWeb()),
6
61
  electron: () => {
7
62
  var _a, _b;
8
- const plugin = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;
9
- if (!plugin) {
63
+ const bridge = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;
64
+ if (!bridge) {
10
65
  throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');
11
66
  }
12
- return plugin;
67
+ return withListenerHandles(bridge);
13
68
  },
14
69
  });
15
70
 
@@ -32,6 +87,24 @@ var capacitorExample = (function (exports, core) {
32
87
  async getCapabilities() {
33
88
  throw this.unimplemented(NO_READER_ON_WEB);
34
89
  }
90
+ /**
91
+ * Not `not-found`: that state tells the operator to check the cable, and
92
+ * there is no cable to check in a browser. The rejection carries the one
93
+ * useful sentence — open the desktop app or the handheld.
94
+ */
95
+ async getConnectionState() {
96
+ throw this.unimplemented(NO_READER_ON_WEB);
97
+ }
98
+ /**
99
+ * Not an empty list: "no readers plugged in" would send the operator to look
100
+ * for a cable, and the browser has no serial ports to look at.
101
+ */
102
+ async listReaderPorts() {
103
+ throw this.unimplemented(NO_READER_ON_WEB);
104
+ }
105
+ async selectReaderPort() {
106
+ throw this.unimplemented(NO_READER_ON_WEB);
107
+ }
35
108
  async startScan() {
36
109
  throw this.unimplemented(NO_READER_ON_WEB);
37
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sources":["esm/index.js","esm/web.js"],"sourcesContent":["import { registerPlugin } from '@capacitor/core';\nconst RFID = registerPlugin('RFID', {\n web: () => import('./web').then(m => new m.RFIDWeb()),\n electron: () => {\n var _a, _b;\n const plugin = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;\n if (!plugin) {\n throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');\n }\n return plugin;\n },\n});\nexport * from './definitions';\nexport { RFID };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nconst NO_READER_ON_WEB = 'No RFID Reader in the browser. Use the desktop app (ox-desktop) or the Android handheld.';\nexport class RFIDWeb extends WebPlugin {\n /**\n * The one method the browser can answer truthfully. Everything else rejects:\n * a silent `resolve` makes the UI report a success that never happened.\n */\n async isConnected() {\n return { connected: false };\n }\n /**\n * Events never fire without a Reader, so registering a listener here would be\n * the same silent lie as a resolving `startScan()`.\n */\n async addListener() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getCapabilities() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async clearData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getScanData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getReaderType() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getFirmwareVersion() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpc() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpcString() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
1
+ {"version":3,"file":"plugin.js","sources":["esm/electron-bridge.js","esm/index.js","esm/web.js"],"sourcesContent":["const SCAN_EVENT = 'onScanEvent';\n/**\n * The Electron main process sends the tags it read in one message per 250 ms\n * window instead of one per frame — a single tag is read about 44 times a\n * second. The contract does not change: the array is unrolled here, so a\n * listener still gets one tag per call.\n */\nconst SCAN_BATCH_EVENT = 'onScanEventBatch';\n/**\n * On native platforms Capacitor wraps the `addListener` result into a\n * `PluginListenerHandle` itself (`addListenerNative`), but only when\n * `PluginHeaders` are present. Electron has none, so the bridge's raw id\n * string reaches the UI as-is and `handle.remove()` is missing: the listener\n * is never removed, and scanning does not start the second time the modal\n * opens.\n *\n * Do not wrap it with a `Proxy`. Methods of an object exposed through\n * `contextBridge` arrive as `writable: false, configurable: false`, and for\n * such a property a `get` trap must return **that exact value** — even a\n * bound copy is rejected. Otherwise the engine throws the error we saw on\n * the device:\n * TypeError: 'get' on proxy: property 'startScan' is a read-only and\n * non-configurable data property on the proxy target but the proxy did\n * not return its actual value\n * So the bridge is used as the prototype instead: the other methods are\n * found on it as usual, and only `addListener` is defined on top.\n */\nexport function withListenerHandles(bridge) {\n const plugin = Object.create(bridge);\n // A plain `plugin.addListener = ...` does not work: the prototype property\n // is `writable: false`, so [[Set]] blocks it.\n Object.defineProperty(plugin, 'addListener', {\n value: async (eventName, callback) => {\n const ids = [bridge.addListener(eventName, callback)];\n if (eventName === SCAN_EVENT) {\n // A desktop app older than the batch sends one message per tag on\n // `onScanEvent`, a newer one sends only batches. Both are listened\n // for, because the bundle in the browser and the installed desktop\n // app are updated separately; only one of them ever fires.\n ids.push(bridge.addListener(SCAN_BATCH_EVENT, (batch) => {\n for (const event of batch)\n callback(event);\n }));\n }\n return {\n remove: async () => {\n for (const id of ids)\n bridge.removeListener(id);\n },\n };\n },\n });\n return plugin;\n}\n//# sourceMappingURL=electron-bridge.js.map","import { registerPlugin } from '@capacitor/core';\nimport { withListenerHandles } from './electron-bridge';\nconst RFID = registerPlugin('RFID', {\n web: () => import('./web').then(m => new m.RFIDWeb()),\n electron: () => {\n var _a, _b;\n const bridge = (_b = (_a = window.CapacitorCustomPlatform) === null || _a === void 0 ? void 0 : _a.plugins) === null || _b === void 0 ? void 0 : _b.RFID;\n if (!bridge) {\n throw new Error('RFID is not registered in the Electron runtime. Register the driver in electron-plugins.js.');\n }\n return withListenerHandles(bridge);\n },\n});\nexport * from './definitions';\nexport { RFID };\n//# sourceMappingURL=index.js.map","import { WebPlugin } from '@capacitor/core';\nconst NO_READER_ON_WEB = 'No RFID Reader in the browser. Use the desktop app (ox-desktop) or the Android handheld.';\nexport class RFIDWeb extends WebPlugin {\n /**\n * The one method the browser can answer truthfully. Everything else rejects:\n * a silent `resolve` makes the UI report a success that never happened.\n */\n async isConnected() {\n return { connected: false };\n }\n /**\n * Events never fire without a Reader, so registering a listener here would be\n * the same silent lie as a resolving `startScan()`.\n */\n async addListener() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getCapabilities() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n /**\n * Not `not-found`: that state tells the operator to check the cable, and\n * there is no cable to check in a browser. The rejection carries the one\n * useful sentence — open the desktop app or the handheld.\n */\n async getConnectionState() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n /**\n * Not an empty list: \"no readers plugged in\" would send the operator to look\n * for a cable, and the browser has no serial ports to look at.\n */\n async listReaderPorts() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async selectReaderPort() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopScan() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async clearData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getScanData() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setOutputPower() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setRange() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async setQueryMode() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getReaderType() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async getFirmwareVersion() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpc() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async writeEpcString() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async startSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n async stopSearch() {\n throw this.unimplemented(NO_READER_ON_WEB);\n }\n}\n//# sourceMappingURL=web.js.map"],"names":["registerPlugin","WebPlugin"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-rfid-plugin-ox",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "rfid read-write",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -41,7 +41,9 @@
41
41
  "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.js",
42
42
  "clean": "rimraf ./dist",
43
43
  "watch": "tsc --watch",
44
- "prepublishOnly": "npm run build"
44
+ "prepublishOnly": "npm run build",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest"
45
47
  },
46
48
  "devDependencies": {
47
49
  "@capacitor/android": "^6.0.0",
@@ -57,7 +59,8 @@
57
59
  "rimraf": "^3.0.2",
58
60
  "rollup": "^2.32.0",
59
61
  "swiftlint": "^1.0.1",
60
- "typescript": "~4.1.5"
62
+ "typescript": "~4.1.5",
63
+ "vitest": "^0.34.6"
61
64
  },
62
65
  "peerDependencies": {
63
66
  "@capacitor/core": "^6.0.0"