@capturebridge/sdk 0.17.1 → 0.18.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/CHANGELOG.md CHANGED
@@ -5,11 +5,38 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.18.0] - 2024-10-18
9
+
10
+ ### Added
11
+
12
+ - When running in a web browser, the value for `BASE_URL` will first be checked
13
+ in a `capturebridge:url` meta tag, followed by `window.CAPTURE_BRIDGE_URL`,
14
+ and finally `window.location.origin`. When running the SDK in Node.js a
15
+ `CAPTURE_BRIDGE_URL` environment variable will be checked.
16
+ - Added the `Verifone.cancel()` method to gracefully cancel long-running
17
+ operations such as the `Verifone.displayObjects()` and
18
+ `Verifone.readMagstripe()` requests.
19
+ - Added the `Verifone.reset()` method to reset a device's state.
20
+ - Several Verifone SDK examples now contain a "Cancel Request" button.
21
+ - Added a Reset demo to the Verifone SDK examples allowing the SDK to reset the
22
+ device state, forms processor app, and reboot the device.
23
+ - Added a Live ICAO demo containing face detection auto capture to the ICAO
24
+ examples.
25
+
26
+ ### Changed
27
+
28
+ - `Verifone.displayObjects()` and `Verifone.readMagstripe()` now accept an
29
+ additional `requestOpt` argument containing an optional
30
+ [AbortSignal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) and
31
+ `readyTimeout` parameter. `Verifone.cancel()` should be typically be used
32
+ instead of the `AbortSignal` as it will block until the device is freed and
33
+ available for another request.
34
+
8
35
  ## [0.16.0] - 2024-10-03
9
36
 
10
37
  ### Changed
11
38
 
12
- - `Plugin.shutdown` now accepts a `timeout` argument.
39
+ - `Plugin.shutdown()` now accepts a `timeout` argument.
13
40
 
14
41
  ### Removed
15
42
 
@@ -20,7 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
20
47
  ### Added
21
48
 
22
49
  - Add `Plugin.hasMethod()` method for checking if a plugin has a specific RPC
23
- method.
50
+ method.
24
51
  - Add suppport for transcoding captured images to gif.
25
52
 
26
53
  ## [0.14.0] - 2024-09-17
package/Common.js CHANGED
@@ -5,13 +5,63 @@
5
5
  */
6
6
  export const DEFAULT_URL = 'https://local.capturebridge.net:9001'
7
7
 
8
+ function CaptureBridgeSetDefaultURL () {
9
+ // Prefer <meta name="capturebridge:url" content="proto://fqdn" /> if set
10
+ if (typeof document === 'object') {
11
+ const url = document.querySelector('meta[name="capturebridge:url"]')
12
+ if (url && url.content) {
13
+ return url.content
14
+ }
15
+ }
16
+
17
+ // Prefer CAPTURE_BRIDGE_URL if set on the global window object (Browser)
18
+ if (typeof window === 'object' && window?.CAPTURE_BRIDGE_URL) {
19
+ return window.CAPTURE_BRIDGE_URL
20
+ }
21
+
22
+ // Prefer CAPTURE_BRIDGE_URL if set on in the environment (Node.js)
23
+ if (typeof process === 'object' && process?.env?.CAPTURE_BRIDGE_URL) {
24
+ return process?.env?.CAPTURE_BRIDGE_URL
25
+ }
26
+
27
+ // Failing an explicit CAPTURE_BRIDGE_URL, use the URL in which the SDK
28
+ // is being served from (Browser)
29
+ if (typeof window === 'object' && window?.location?.origin) {
30
+ return window.location.origin
31
+ }
32
+
33
+ // If all else fails, use the default vanity URL
34
+ return DEFAULT_URL
35
+ }
36
+
8
37
  /**
9
- * @summary Base URL for API requests.
38
+ * @summary Base URL for all HTTP API requests.
39
+ *
40
+ * Due to the various environments in which the SDK can be loaded and the
41
+ * different ways that a Capture Bridge instance may be configured. The
42
+ * `BASE_URL` variable used for all default HTTP API request can be set in
43
+ * several ways:
44
+ *
45
+ * When running the SDK within a web browser the following locations are
46
+ * checked, in order:
47
+ * 1. A {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta|meta} tag containing a url: `<meta name="capturebridge:url" content="proto://fqdn:port" />`
48
+ * 2. In the global {@link https://developer.mozilla.org/en-US/docs/Web/API/Window|Window} object: `window.CAPTURE_BRIDGE_URL`
49
+ * 3. In the {@link https://developer.mozilla.org/en-US/docs/Web/API/Location/origin|origin} in which the SDK is running: `window.location.origin`
50
+ * 4. The `DEFAULT_URL` constant from `Common.js`
51
+ *
52
+ * When running the SDK within Node.js the following locations are
53
+ * checked, in order:
54
+ * 1. In a `CAPTURE_BRIDGE_URL` {@link https://nodejs.org/api/process.html#processenv|environment variable}
55
+ * 2. The `DEFAULT_URL` constant from `Common.js`
56
+ *
57
+ * **Note** that each constructor also accepts a `baseUrl` parameter that will override
58
+ * any value for `BASE_URL` previously set.
59
+ *
10
60
  * @constant
11
61
  * @type {string}
12
62
  * @default {@link https://local.capturebridge.net:9001}
13
63
  */
14
- export const BASE_URL = window?.location?.origin ?? DEFAULT_URL
64
+ export const BASE_URL = CaptureBridgeSetDefaultURL()
15
65
 
16
66
  /**
17
67
  * Common {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API|Fetch}
package/Device.js CHANGED
@@ -359,10 +359,16 @@ class Device {
359
359
  * @param {Object} [sceneOpt.backlight] If `true`, the backlight will be
360
360
  * enabled before adding the Objects.
361
361
  *
362
+ * @param {AbortSignal} [signal] - An {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal|AbortSignal}
363
+ * obtained from an {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController|AbortController}
364
+ * allowing the request to be aborted. If the request is aborted or the HTTP
365
+ * connection is closed for any reason before a user has interacted with the
366
+ * display, the device state will be reset and the screen will be cleared.
367
+ *
362
368
  * @async
363
369
  * @method
364
370
  */
365
- async displayObjects (objects, sceneOpt = {}) {
371
+ async displayObjects (objects, sceneOpt = {}, signal) {
366
372
  this.can('draw')
367
373
  const url = `${this.baseUrl}/plugin/${this.plugin.id}/device/${this.id}/display`
368
374
 
@@ -386,6 +392,10 @@ class Device {
386
392
  body: JSON.stringify(sceneOpt)
387
393
  }
388
394
 
395
+ if (signal instanceof AbortSignal) {
396
+ options.signal = signal
397
+ }
398
+
389
399
  const response = await fetch(url, options)
390
400
  await checkResponse(response)
391
401
  return await response.json()
@@ -408,6 +418,77 @@ class Device {
408
418
  const response = await fetch(url)
409
419
  return await response.json()
410
420
  }
421
+
422
+ /**
423
+ * Cancel a pending device operation
424
+ *
425
+ * @description Will attempt to cancel any active long-running operation
426
+ *
427
+ * @param {Number?} [timeout] If provided, will attempt to
428
+ * wait at least this long (in seconds) for the operation to be cancelled
429
+ * before returning a response. By default this endpoint will request a
430
+ * cancellation and return immediately, however if you'd like to block, waiting
431
+ * for the device to be ready before attempting another operation you may supply
432
+ * this timeout.
433
+ *
434
+ * @async
435
+ * @method
436
+ * @returns {object} Cancellation status
437
+ * @example
438
+ * const {status} = await device.cancel()
439
+ */
440
+ async cancel (timeout) {
441
+ this.can('cancel')
442
+ let url = `${this.baseUrl}/plugin/${this.plugin.id}/device/${this.id}/cancel`
443
+ if (typeof timeout === 'number') {
444
+ this.can('ready')
445
+ url += `?timeout=${timeout}`
446
+ }
447
+ const options = {
448
+ method: 'POST',
449
+ mode: 'cors',
450
+ headers: {
451
+ 'Content-Type': 'application/json'
452
+ }
453
+ }
454
+ const response = await fetch(url, options)
455
+ await checkResponse(response)
456
+ return await response.json()
457
+ }
458
+
459
+ /**
460
+ * Restart a device or reset its state.
461
+ *
462
+ * @description Used to restart a device or reset its state
463
+ *
464
+ * @param {Number?} [depth=0] Some devices have several abstractions layers
465
+ * that can be reset separately. For example, the entire device might be
466
+ * rebooted or an outstanding operation could be cancelled.
467
+ *
468
+ * @async
469
+ * @method
470
+ * @returns {object} Reset status
471
+ * @example
472
+ * const {status} = await device.reset()
473
+ */
474
+ async reset (depth = 0) {
475
+ this.can('reset')
476
+ let url = `${this.baseUrl}/plugin/${this.plugin.id}/device/${this.id}/reset`
477
+ if (typeof depth === 'number') {
478
+ this.can('reset')
479
+ url += `?depth=${depth}`
480
+ }
481
+ const options = {
482
+ method: 'POST',
483
+ mode: 'cors',
484
+ headers: {
485
+ 'Content-Type': 'application/json'
486
+ }
487
+ }
488
+ const response = await fetch(url, options)
489
+ await checkResponse(response)
490
+ return await response.json()
491
+ }
411
492
  }
412
493
 
413
494
  export default Device
package/Verifone.js CHANGED
@@ -126,6 +126,7 @@ export class Verifone extends SignatureTablet {
126
126
  /**
127
127
  * Not currently supported for this plugin.
128
128
  * @throws Not implemented error
129
+ * @see {@link Verifone#cancel}
129
130
  * @example
130
131
  * // Not currently supported for this plugin
131
132
  * @override
@@ -134,6 +135,58 @@ export class Verifone extends SignatureTablet {
134
135
  throw new Error('stopFeed() is not implemented for the Verifone Plugin')
135
136
  }
136
137
 
138
+ /**
139
+ * Cancel a pending device operation
140
+ *
141
+ * @description Will attempt to cancel any active long-running operation
142
+ *
143
+ * @param {Number?} [timeout] If provided, will attempt to
144
+ * wait at least this long (in seconds) for the operation to be cancelled
145
+ * before returning a response. By default this endpoint will request a
146
+ * cancellation and return immediately, however if you'd like to block, waiting
147
+ * for the device to be ready before attempting another operation you may supply
148
+ * this timeout.
149
+ *
150
+ * @param {string|object} [deviceOpt] - Cancel the operation for either a
151
+ * specific Device ID or a Device Object. The default is the first available
152
+ * device.
153
+ *
154
+ * @async
155
+ * @method
156
+ * @returns {object} Cancellation status
157
+ * @example
158
+ * const {status} = await device.cancel()
159
+ */
160
+ async cancel (timeout = null, deviceOpt) {
161
+ const device = await this.setupDevice(deviceOpt)
162
+ return device.cancel(timeout)
163
+ }
164
+
165
+ /**
166
+ * Restart the device or reset state.
167
+ *
168
+ * @description Reset the Verifone's internal state, reboot the device, or
169
+ * Forms Processor application.
170
+ *
171
+ * @param {Number?} [depth=3] 0 = Reboot the device, 1 = Restart Forms
172
+ * Processor, 2 = Reset device state, 3 = Reset device state and drain serial
173
+ * port.
174
+ *
175
+ * @param {string|object} [deviceOpt] - Display the objects on either a
176
+ * specific Device ID or a Device Object. The default is the first available
177
+ * device.
178
+ *
179
+ * @async
180
+ * @method
181
+ * @returns {object} Reset status
182
+ * @example
183
+ * const {status} = await device.reset()
184
+ */
185
+ async reset (depth = 3, deviceOpt) {
186
+ const device = await this.setupDevice(deviceOpt)
187
+ return device.reset(depth)
188
+ }
189
+
137
190
  /**
138
191
  * Render an array of form controls.
139
192
  *
@@ -150,6 +203,22 @@ export class Verifone extends SignatureTablet {
150
203
  * specific Device ID or a Device Object. The default is the first available
151
204
  * device.
152
205
  *
206
+ * @param {object} [requestOpt] - Additional options for the request
207
+ *
208
+ * @param {AbortSignal} [requestOpt.signal=null] - An {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal|AbortSignal}
209
+ * obtained from an {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController|AbortController}
210
+ * allowing the request to be aborted. If the request is aborted or the HTTP
211
+ * connection is closed for any reason before a user has interacted with the
212
+ * display, the device state will be reset and the screen will be cleared.
213
+ * The {@link Verifone#cancel} method is the preferred method for cancelling
214
+ * a displayObjects request and should usually be used instead of an AbortSignal.
215
+ *
216
+ * @param {Number?} [requestOpt.readyTimeout=10] Attempt to wait at least this
217
+ * long (in seconds) for the device to be ready before sending the
218
+ * displayObjects request. This reduces the chance that the request will fail
219
+ * due to the serial port being locked after a previously cancelled or aborted
220
+ * request.
221
+ *
153
222
  * @async
154
223
  * @method
155
224
  * @returns {string?} ID of the clicked object if any
@@ -161,9 +230,18 @@ export class Verifone extends SignatureTablet {
161
230
  *
162
231
  * console.log(result)
163
232
  */
164
- async displayObjects (objects, formOpt = {}, deviceOpt) {
233
+ async displayObjects (objects, formOpt = {}, deviceOpt, requestOpt = {}) {
165
234
  const device = await this.setupDevice(deviceOpt)
166
- return device.displayObjects(objects, formOpt)
235
+ const defaultRequestOpt = { signal: null, readyTimeout: 10 }
236
+ const mergedOpts = Object.assign({}, defaultRequestOpt, requestOpt)
237
+
238
+ if (typeof mergedOpts.readyTimeout === 'number') {
239
+ // Abort any active in-flight requests and wait at most readyTimeout
240
+ // seconds for the device to be ready
241
+ await this.cancel(mergedOpts.readyTimeout)
242
+ }
243
+
244
+ return device.displayObjects(objects, formOpt, mergedOpts.signal)
167
245
  }
168
246
 
169
247
  /**
@@ -173,7 +251,7 @@ export class Verifone extends SignatureTablet {
173
251
  * object with addtional configuration details.
174
252
  * @param {ControlProperty[]} [controlProps] - Array of {@link ControlProperty}
175
253
  * objects.
176
- * @param {string|object} [deviceOpt] - Clear the display of a either a
254
+ * @param {string|object} [deviceOpt] - Display the objects on either a
177
255
  * specific Device ID or a Device Object. The default is the first available
178
256
  * device.
179
257
  *
@@ -217,10 +295,26 @@ export class Verifone extends SignatureTablet {
217
295
  * @param {string|object} [deviceOpt] - Read magstrip using a specific Device
218
296
  * ID or a Device Object. The default is the first available device.
219
297
  *
298
+ * @param {object} [requestOpt] - Additional options for the request
299
+ *
300
+ * @param {AbortSignal} [requestOpt.signal=null] - An {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal|AbortSignal}
301
+ * obtained from an {@link https://developer.mozilla.org/en-US/docs/Web/API/AbortController|AbortController}
302
+ * allowing the request to be aborted. If the request is aborted or the HTTP
303
+ * connection is closed for any reason before a user has interacted with the
304
+ * display, the device state will be reset and the screen will be cleared.
305
+ * The {@link Verifone#cancel} method is the preferred method for cancelling
306
+ * a readMagstripe request and should usually be used instead of an AbortSignal.
307
+ *
308
+ * @param {Number?} [requestOpt.readyTimeout=10] Attempt to wait at least this
309
+ * long (in seconds) for the device to be ready before sending the
310
+ * readMagstripe request. This reduces the chance that the request will fail
311
+ * due to the serial port being locked after a previously cancelled or aborted
312
+ * request.
220
313
  */
221
- async readMagstripe (readOpts = { timeout: 30 }, deviceOpt) {
314
+ async readMagstripe (readOpts = { timeout: 30 }, deviceOpt, requestOpt = {}) {
222
315
  const device = await this.setupDevice(deviceOpt)
223
- device.can('read_magstripe')
316
+ const defaultRequestOpt = { signal: null, readyTimeout: 10 }
317
+ const mergedOpts = Object.assign({}, defaultRequestOpt, requestOpt)
224
318
 
225
319
  const options = {
226
320
  method: 'GET',
@@ -229,6 +323,19 @@ export class Verifone extends SignatureTablet {
229
323
  'Content-Type': 'application/json'
230
324
  }
231
325
  }
326
+
327
+ device.can('read_magstripe')
328
+
329
+ if (typeof mergedOpts.readyTimeout === 'number') {
330
+ // Abort any active in-flight requests and wait at most readyTimeout
331
+ // seconds for the device to be ready
332
+ await this.cancel(mergedOpts.readyTimeout)
333
+ }
334
+
335
+ if (requestOpt.signal instanceof AbortSignal) {
336
+ options.signal = requestOpt.signal
337
+ }
338
+
232
339
  const url = `${this.baseUrl}/plugin/${this.id}/device/${device.id}/magstripe?timeout=${readOpts.timeout}`
233
340
  const response = await fetch(url, options)
234
341
  await checkResponse(response)
@@ -1,10 +1,15 @@
1
1
  /**
2
- * @module SignatureTabletWidgets
2
+ * @module VerifoneControls
3
3
  */
4
4
 
5
5
  import { blobToBase64 } from './Common.js'
6
6
  import { DisplayWidget } from './SignatureTabletWidgets.js'
7
7
 
8
+ /**
9
+ * AccessKey
10
+ * @classdesc Special keys which can be mapped to a button
11
+ * @see {@link Verifone}
12
+ */
8
13
  export class AccessKey {
9
14
  static STAR = '&#138;'
10
15
  static HASH = '&#139;'
@@ -17,7 +22,7 @@ export class AccessKey {
17
22
  * Button
18
23
  * @classdesc A button for displaying clickable text on a tablet
19
24
  * @extends DisplayWidget
20
- * @see {@link SignatureTablet}
25
+ * @see {@link Verifone}
21
26
  */
22
27
  export class Button extends DisplayWidget {
23
28
  id
@@ -82,7 +87,7 @@ export class Button extends DisplayWidget {
82
87
  * Text
83
88
  * @classdesc Text to be displayed on a tablet
84
89
  * @extends DisplayWidget
85
- * @see {@link SignatureTablet}
90
+ * @see {@link Verifone}
86
91
  */
87
92
  export class Text extends DisplayWidget {
88
93
  id
@@ -145,7 +150,7 @@ export class Text extends DisplayWidget {
145
150
  * Image
146
151
  * @classdesc Image to be displayed on a tablet
147
152
  * @extends DisplayWidget
148
- * @see {@link SignatureTablet}
153
+ * @see {@link Verifone}
149
154
  */
150
155
  export class Image extends DisplayWidget {
151
156
  id
@@ -219,7 +224,7 @@ export class Image extends DisplayWidget {
219
224
  * SignatureBox
220
225
  * @classdesc Bounding box to capture a signature
221
226
  * @extends DisplayWidget
222
- * @see {@link SignatureTablet}
227
+ * @see {@link Verifone}
223
228
  */
224
229
  export class SignatureBox extends DisplayWidget {
225
230
  id
@@ -265,7 +270,7 @@ export class SignatureBox extends DisplayWidget {
265
270
  * Rect
266
271
  * @classdesc A rectangle
267
272
  * @extends DisplayWidget
268
- * @see {@link SignatureTablet}
273
+ * @see {@link Verifone}
269
274
  */
270
275
  export class Rect extends DisplayWidget {
271
276
  id
@@ -318,7 +323,7 @@ export class Rect extends DisplayWidget {
318
323
  * CheckBox
319
324
  * @classdesc A check box object
320
325
  * @extends DisplayWidget
321
- * @see {@link SignatureTablet}
326
+ * @see {@link Verifone}
322
327
  */
323
328
  export class CheckBox extends DisplayWidget {
324
329
  id
@@ -363,7 +368,7 @@ export class CheckBox extends DisplayWidget {
363
368
  * RadioButton
364
369
  * @classdesc A radio button object
365
370
  * @extends DisplayWidget
366
- * @see {@link SignatureTablet}
371
+ * @see {@link Verifone}
367
372
  */
368
373
  export class RadioButton extends DisplayWidget {
369
374
  id
@@ -415,7 +420,7 @@ export class RadioButton extends DisplayWidget {
415
420
  * TextInput
416
421
  * @classdesc A text input object
417
422
  * @extends DisplayWidget
418
- * @see {@link SignatureTablet}
423
+ * @see {@link Verifone}
419
424
  */
420
425
  export class TextInput extends DisplayWidget {
421
426
  id
package/Version.js CHANGED
@@ -4,5 +4,5 @@
4
4
  * @constant
5
5
  * @type {string}
6
6
  */
7
- const VERSION = '0.17.1'
7
+ const VERSION = '0.18.0'
8
8
  export default VERSION
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capturebridge/sdk",
3
- "version": "0.17.1",
3
+ "version": "0.18.0",
4
4
  "description": "Capture Bridge JavaScript SDK for web applications",
5
5
  "type": "module",
6
6
  "types": "types.d.ts",