@aparajita/capacitor-biometric-auth 5.2.0 โ†’ 6.0.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
@@ -6,7 +6,7 @@ This plugin for [Capacitor 5](https://capacitorjs.com) provides access to native
6
6
 
7
7
  ๐Ÿ‘‰ **NOTE:** This plugin only works with Capacitor 5. If you are upgrading from the Capacitor 2 version, note that the plugin name has changed to `BiometricAuth`.
8
8
 
9
- ๐Ÿ›‘ **BREAKING CHANGE:** If you are upgrading from a version prior to 3.0.0, please note that `androidMaxAttempts` is no longer supported. See the documentation for [`authenticate()`](#authenticate) for more information.
9
+ ๐Ÿ›‘ **BREAKING CHANGE:** If you are upgrading from a version prior to 6.0.0, please note that [`authenticate()`](#authenticate) now throws an instance of `BiometryError`, and `BiometryError.code` is now typed as [`BiometryErrorType`](#biometryerrortype).
10
10
 
11
11
  ## Demos
12
12
 
@@ -24,29 +24,103 @@ pnpm add @aparajita/capacitor-biometric-auth
24
24
 
25
25
  Not using [pnpm](https://pnpm.js.org/)? You owe it to yourself to give it a try. Itโ€™s faster, better with monorepos, and uses _way, way_ less disk space than the alternatives.
26
26
 
27
+ ### iOS
28
+
29
+ ๐Ÿ‘‰ **IMPORTANT!!** In order to use Face ID, you must add the `NSFaceIDUsageDescription` key to your `Info.plist` file. This is a string that describes why your app needs access to Face ID. If you donโ€™t add this key, the system wonโ€™t allow your app to use Face ID.
30
+
31
+ 1. In Xcode, open your appโ€™s `Info.plist` file.
32
+ 2. Hover your mouse over one of the existing keys, and click the `+` button that appears.
33
+ 3. In the popup that appears, type `Privacy - Face ID Usage Description` and press Enter.
34
+ 4. In the Value column, enter a string that describes why your app needs access to Face ID.
35
+ 5. Save your changes.
36
+
27
37
  ## Usage
28
38
 
29
39
  The API is extensively documented in the [TypeScript definitions file](src/definitions.ts). There is also (somewhat incomplete auto-generated) documentation [below](#api). For a complete example of how to use this plugin in practice, see the [demo app](https://github.com/aparajita/capacitor-biometric-auth-demo).
30
40
 
31
- > **NOTE:** Your Android app must use a base theme named "AppTheme".
41
+ ๐Ÿ‘‰ **NOTE:** Your Android app must use a base theme named "AppTheme".
32
42
 
33
43
  ### Checking availability
34
44
 
35
- Before giving the user the option to use biometry (such as displaying a biometry icon), call [`checkBiometry`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry is available on the device. Note that `isAvailable` may be `false` but `biometryType` may indicate the presence of biometry on the device. This occurs if the current user is not enrolled in biometry, or if biometry has been disabled for the current app. In such cases the `reason` and `code` will tell you why.
45
+ Before giving the user the option to use biometry (such as displaying a biometry icon), call [`checkBiometry()`](#checkbiometry) and inspect the [`CheckBiometryResult`](#checkbiometryresult) to see what (if any) biometry is available on the device. Note the following:
36
46
 
37
- Because the availability of biometry can change while your app is in the background, itโ€™s important to check availability when your app resumes. By calling [`addResumeListener`](#addresumelistener) you can register a callback that is passed a [`CheckBiometryResult`](#checkbiometryresult) when your app resumes.
47
+ - `isAvailable` may be `false` but `biometryType` may indicate the presence of biometry on the device. This occurs if the current user is not enrolled in biometry, or if biometry has been disabled for the current app. In such cases the `reason` and `code` will tell you why.
48
+
49
+ - `biometryTypes` may contain more than one type of biometry. This occurs on Android devices that support multiple types of biometry. In such cases the `biometryType` will indicate the primary (most secure) type of biometry, and the `biometryTypes` array will contain all of the biometry types supported by the device. Note that Android only guarantees that one of the types is actually available.
50
+
51
+ Because the availability of biometry can change while your app is in the background, itโ€™s important to check availability when your app resumes. By calling [`addResumeListener()`](#addresumelistener) you can register a callback that is passed a [`CheckBiometryResult`](#checkbiometryresult) when your app resumes.
52
+
53
+ #### Example
54
+
55
+ ```typescript
56
+ import { CheckBiometryResult } from './definitions'
57
+
58
+ let appListener: PluginListenerHandle
59
+
60
+ function updateBiometryInfo(info: CheckBiometryResult): void {
61
+ if (info.isAvailable) {
62
+ // Biometry is available, info.biometryType will tell you the primary type.
63
+ } else {
64
+ // Biometry is not available, info.reason and info.code will tell you why.
65
+ }
66
+ }
67
+
68
+ async function onComponentMounted(): Promise<void> {
69
+ updateBiometryInfo(await BiometricAuth.checkBiometry())
70
+
71
+ try {
72
+ appListener = await BiometricAuth.addResumeListener(updateBiometryInfo)
73
+ } catch (error) {
74
+ if (error instanceof Error) {
75
+ console.error(error.message)
76
+ }
77
+ }
78
+ }
79
+
80
+ async function onComponentUnmounted(): Promise<void> {
81
+ await appListener?.remove()
82
+ }
83
+ ```
38
84
 
39
85
  ### Authenticating
40
86
 
41
- Once you have determined that biometry is available, to initiate biometric authentication call [`authenticate`](#authenticate). `authenticate` takes an [`AuthenticateOptions`](#authenticateoptions) object which you will want to use in order to control the behavior and appearance of the biometric prompt.
87
+ To initiate biometric authentication call [`authenticate()`](#authenticate). `authenticate` takes an [`AuthenticateOptions`](#authenticateoptions) object which you will want to use in order to control the behavior and appearance of the biometric prompt.
42
88
 
43
- If authentication succeeds, the Promise resolves. If authentication fails, the Promise is rejected with a `BiometryError`, which has two properties:
89
+ If authentication succeeds, the Promise resolves. If authentication fails, the Promise is rejected with an instance of [`BiometryError`](#biometryerror), which has two properties:
44
90
 
45
91
  | Property | Type | Description |
46
92
  | :------- | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------ |
47
93
  | message | string | A description of the error suitable for debugging |
48
94
  | code | [BiometryErrorType](https://github.com/aparajita/capacitor-biometric-auth/blob/main/src/definitions.ts) | What caused the error |
49
95
 
96
+ #### Example
97
+
98
+ ```typescript
99
+ import { BiometryError, BiometryErrorType } from './definitions'
100
+
101
+ async function authenticate(): Promise<void> {
102
+ try {
103
+ await BiometricAuth.authenticate({
104
+ reason: 'Please authenticate',
105
+ cancelTitle: 'Cancel',
106
+ allowDeviceCredential: true,
107
+ iosFallbackTitle: 'Use passcode',
108
+ androidTitle: 'Biometric login',
109
+ androidSubtitle: 'Log in using biometric authentication',
110
+ androidConfirmationRequired: false,
111
+ })
112
+ } catch (error) {
113
+ // error is always an instance of BiometryError.
114
+ if (error instanceof BiometryError) {
115
+ if (error.code !== BiometryErrorType.userCancel) {
116
+ // Display the error.
117
+ await showAlert(error.message)
118
+ }
119
+ }
120
+ }
121
+ }
122
+ ```
123
+
50
124
  ## Biometry support
51
125
 
52
126
  ### web
@@ -59,7 +133,7 @@ On iOS, Touch ID and Face ID are supported.
59
133
 
60
134
  ### Android
61
135
 
62
- On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will only present the primary type, which is determined by the system.
136
+ On Android, fingerprint, face, and iris authentication are supported. Note that if a device supports more than one type of biometry, the plugin will only present the primary (most secure) type, which is determined by the system.
63
137
 
64
138
  ## API
65
139
 
@@ -77,6 +151,8 @@ On Android, fingerprint, face, and iris authentication are supported. Note that
77
151
  <docgen-api>
78
152
  <!--Update the source file JSDoc comments and rerun docgen to update the docs below-->
79
153
 
154
+ This is the public interface of the plugin.
155
+
80
156
  ### checkBiometry()
81
157
 
82
158
  ```typescript
@@ -123,7 +199,7 @@ Prompt the user for authentication. If authorization fails for any reason, the p
123
199
  addResumeListener(listener: ResumeListener) => Promise<PluginListenerHandle>
124
200
  ```
125
201
 
126
- Register a function that will be called when the app resumes. The function will be passed the result of `checkBiometry()`.
202
+ Register a function that will be called when the app resumes. The function will be passed the result of `checkBiometry()`.<br><br>๐Ÿ‘‰ **NOTE:** checkBiometry() must be called at least once before calling this method.
127
203
 
128
204
  | Param | Type |
129
205
  | :------- | :------------------------------------------- |
@@ -137,13 +213,13 @@ Register a function that will be called when the app resumes. The function will
137
213
 
138
214
  #### CheckBiometryResult
139
215
 
140
- | Prop | Type | Description |
141
- | :------------ | :------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
142
- | isAvailable | boolean | True if the device has biometric authentication capability and the current user has enrolled in some form of biometry. |
143
- | biometryType | <a href="#biometrytype">BiometryType</a> | The primary type of biometry available on the device. If the device supports both fingerprint and face authentication, this will be <a href="#biometrytype">`BiometryType.touchId`</a>. |
144
- | biometryTypes | BiometryType[] | All of the biometry types supported by the device (currently only Android devices support multiple biometry types). If no biometry is available, this will be an empty array. If multiple types are supported, Android only guarantees that one of them is actually available. |
145
- | reason | string | If biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. |
146
- | code | <a href="#biometryerrortype">BiometryErrorType</a> | If biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms. This allows you to check for specific errors in a platform- independent way, for example:<br><br>if (result.code === <a href="#biometryerrortype">BiometryErrorType.biometryNotEnrolled</a>) { ... } |
216
+ | Prop | Type | Description |
217
+ | :------------ | :------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
218
+ | isAvailable | boolean | True if the device has biometric authentication capability and the current user has enrolled in some form of biometry. |
219
+ | biometryType | <a href="#biometrytype">BiometryType</a> | The primary type of biometry available on the device. If the device supports both fingerprint and face authentication, this will be <a href="#biometrytype">`BiometryType.touchId`</a>. |
220
+ | biometryTypes | BiometryType[] | All of the biometry types supported by the device (currently only Android devices support multiple biometry types). If no biometry is available, this will be an empty array. If multiple types are supported, Android only guarantees that one of them is actually available. |
221
+ | reason | string | If biometry is not available and the system gives a reason why, it will be returned here. Otherwise it's an empty string. |
222
+ | code | <a href="#biometryerrortype">BiometryErrorType</a> | If biometry is not available, the error code will be returned here. Otherwise it's an empty string. The error code will be one of the <a href="#biometryerrortype">`BiometryErrorType`</a> enum values, and is consistent across platforms. |
147
223
 
148
224
  #### AuthenticateOptions
149
225
 
@@ -155,7 +231,7 @@ Register a function that will be called when the app resumes. The function will
155
231
  | iosFallbackTitle | string | The system presents a fallback button when biometric authentication fails โ€” for example, because the system doesnโ€™t recognize the presented finger, or after several failed attempts to recognize the userโ€™s face.<br><br>If `allowDeviceCredential` is false, tapping this button dismisses the authentication dialog and returns the error code userFallback. If undefined, the localized system default title is used. Passing an empty string hides the fallback button completely.<br><br>If `allowDeviceCredential` is true and this is undefined, the localized system default title is used. |
156
232
  | androidTitle | string | Title for the Android dialog. If not supplied, the system default is used. |
157
233
  | androidSubtitle | string | Subtitle for the Android dialog. If not supplied, the system default is used. |
158
- | androidConfirmationRequired | boolean | For information on this setting, see:<br><br>https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean)<br><br>If not set, defaults to true. |
234
+ | androidConfirmationRequired | boolean | If not set, defaults to true.<br><br>For information on this setting, see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean). |
159
235
 
160
236
  #### PluginListenerHandle
161
237
 
@@ -96,6 +96,13 @@ public class BiometricAuthNative extends Plugin {
96
96
  */
97
97
  @PluginMethod
98
98
  public void checkBiometry(PluginCall call) {
99
+ call.resolve(checkDeviceBiometry());
100
+ }
101
+
102
+ /**
103
+ * Check the device's availability and type of biometric authentication.
104
+ */
105
+ private JSObject checkDeviceBiometry() {
99
106
  BiometricManager manager = BiometricManager.from(getContext());
100
107
  int biometryResult;
101
108
 
@@ -106,14 +113,14 @@ public class BiometricAuthNative extends Plugin {
106
113
  biometryResult = manager.canAuthenticate();
107
114
  }
108
115
 
109
- JSObject ret = new JSObject();
110
- ret.put(
116
+ JSObject result = new JSObject();
117
+ result.put(
111
118
  "isAvailable",
112
119
  biometryResult == BiometricManager.BIOMETRIC_SUCCESS
113
120
  );
114
121
 
115
122
  biometryTypes = getDeviceBiometryTypes();
116
- ret.put("biometryType", biometryTypes.get(0).getType());
123
+ result.put("biometryType", biometryTypes.get(0).getType());
117
124
 
118
125
  JSArray returnTypes = new JSArray();
119
126
 
@@ -121,7 +128,7 @@ public class BiometricAuthNative extends Plugin {
121
128
  returnTypes.put(type.getType());
122
129
  }
123
130
 
124
- ret.put("biometryTypes", returnTypes);
131
+ result.put("biometryTypes", returnTypes);
125
132
 
126
133
  String reason = "";
127
134
 
@@ -156,13 +163,13 @@ public class BiometricAuthNative extends Plugin {
156
163
  errorCode = "biometryNotAvailable";
157
164
  }
158
165
 
159
- ret.put("reason", reason);
160
- ret.put("code", errorCode);
161
- call.resolve(ret);
166
+ result.put("reason", reason);
167
+ result.put("code", errorCode);
168
+ return result;
162
169
  }
163
170
 
164
171
  private ArrayList<BiometryType> getDeviceBiometryTypes() {
165
- ArrayList<BiometryType> types = new ArrayList<BiometryType>();
172
+ ArrayList<BiometryType> types = new ArrayList<>();
166
173
  PackageManager manager = getContext().getPackageManager();
167
174
 
168
175
  if (manager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
@@ -188,7 +195,18 @@ public class BiometricAuthNative extends Plugin {
188
195
  * Prompt the user for biometric authentication.
189
196
  */
190
197
  @PluginMethod
191
- public void authenticate(final PluginCall call) {
198
+ public void internalAuthenticate(final PluginCall call) {
199
+ // Make sure biometry is available
200
+ JSObject checkResult = checkDeviceBiometry();
201
+
202
+ if (Boolean.FALSE.equals(checkResult.getBoolean("isAvailable", false))) {
203
+ call.reject(
204
+ checkResult.getString("reason", ""),
205
+ checkResult.getString("code", "")
206
+ );
207
+ return;
208
+ }
209
+
192
210
  // The result of an intent is supposed to have the package name as a prefix
193
211
  RESULT_EXTRA_PREFIX = getContext().getPackageName() + ".";
194
212
 
@@ -279,23 +297,19 @@ public class BiometricAuthNative extends Plugin {
279
297
  );
280
298
 
281
299
  switch (resultType) {
282
- case SUCCESS:
283
- call.resolve();
284
- break;
285
- case FAILURE:
286
- // Biometry was successfully presented but was not recognized
287
- call.reject(errorMessage, BIOMETRIC_FAILURE);
288
- break;
289
- case ERROR:
290
- // The user cancelled, the system cancelled, or some error occurred.
291
- // If the user cancelled, errorMessage is the text of the "negative" button,
292
- // which is not especially descriptive.
300
+ case SUCCESS -> call.resolve();
301
+ // Biometry was successfully presented but was not recognized
302
+ case FAILURE -> call.reject(errorMessage, BIOMETRIC_FAILURE);
303
+ // The user cancelled, the system cancelled, or some error occurred.
304
+ // If the user cancelled, errorMessage is the text of the "negative" button,
305
+ // which is not especially descriptive.
306
+ case ERROR -> {
293
307
  if (errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
294
308
  errorMessage = "Cancel button was pressed";
295
309
  }
296
310
 
297
311
  call.reject(errorMessage, biometryErrorCodeMap.get(errorCode));
298
- break;
312
+ }
299
313
  }
300
314
  }
301
315
 
@@ -4,6 +4,7 @@ import type { AuthenticateOptions, BiometricAuthPlugin, CheckBiometryResult, Res
4
4
  export declare abstract class BiometricAuthBase extends WebPlugin implements BiometricAuthPlugin {
5
5
  abstract setBiometryType(type: BiometryType | string | undefined): Promise<void>;
6
6
  abstract checkBiometry(): Promise<CheckBiometryResult>;
7
- abstract authenticate(options?: AuthenticateOptions): Promise<void>;
7
+ authenticate(options?: AuthenticateOptions): Promise<void>;
8
+ protected abstract internalAuthenticate(options?: AuthenticateOptions): Promise<void>;
8
9
  addResumeListener(listener: ResumeListener): Promise<PluginListenerHandle> & PluginListenerHandle;
9
10
  }
package/dist/esm/base.js CHANGED
@@ -1,7 +1,25 @@
1
1
  import { App } from '@capacitor/app';
2
- import { WebPlugin } from '@capacitor/core';
2
+ import { CapacitorException, WebPlugin } from '@capacitor/core';
3
+ import { BiometryError } from './definitions';
3
4
  // eslint-disable-next-line import/prefer-default-export
4
5
  export class BiometricAuthBase extends WebPlugin {
6
+ async authenticate(options) {
7
+ try {
8
+ await this.internalAuthenticate(options);
9
+ }
10
+ catch (error) {
11
+ // error will be an instance of CapacitorException on native platforms,
12
+ // an instance of BiometryError on the web.
13
+ if (error instanceof CapacitorException) {
14
+ throw new BiometryError(error.message,
15
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- error.data values are typed as any
16
+ error.data.code);
17
+ }
18
+ else {
19
+ throw error;
20
+ }
21
+ }
22
+ }
5
23
  addResumeListener(listener) {
6
24
  return App.addListener('appStateChange', ({ isActive }) => {
7
25
  if (isActive) {
@@ -1,4 +1,4 @@
1
- import type { PluginListenerHandle, PluginResultError, WebPlugin } from '@capacitor/core';
1
+ import type { PluginListenerHandle, WebPlugin } from '@capacitor/core';
2
2
  export declare enum BiometryType {
3
3
  /**
4
4
  * No biometry is available
@@ -101,17 +101,15 @@ export interface AuthenticateOptions {
101
101
  */
102
102
  androidSubtitle?: string;
103
103
  /**
104
- * For information on this setting, see:
105
- *
106
- * https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean)
107
- *
108
104
  * If not set, defaults to true.
105
+ *
106
+ * For information on this setting, see https://developer.android.com/reference/android/hardware/biometrics/BiometricPrompt.Builder#setConfirmationRequired(boolean).
109
107
  */
110
108
  androidConfirmationRequired?: boolean;
111
109
  }
112
110
  /**
113
- * If the `authenticate()` method throws an exception, the error object
114
- * contains a .code property which will contain one of these strings,
111
+ * If the `authenticate()` method throws an exception, the `BiometryError`
112
+ * instance contains a .code property which will contain one of these strings,
115
113
  * indicating what the error was.
116
114
  *
117
115
  * See https://developer.apple.com/documentation/localauthentication/laerror
@@ -132,12 +130,12 @@ export declare enum BiometryErrorType {
132
130
  biometryNotEnrolled = "biometryNotEnrolled",
133
131
  noDeviceCredential = "noDeviceCredential"
134
132
  }
135
- export interface ResultError extends PluginResultError {
136
- code: string;
137
- }
138
- export declare class BiometryError implements ResultError {
133
+ /**
134
+ * `authenticate()` throws instances of this class.
135
+ */
136
+ export declare class BiometryError {
139
137
  message: string;
140
- code: string;
138
+ code: BiometryErrorType;
141
139
  constructor(message: string, code: BiometryErrorType);
142
140
  }
143
141
  export interface CheckBiometryResult {
@@ -169,12 +167,7 @@ export interface CheckBiometryResult {
169
167
  * If biometry is not available, the error code will be returned here.
170
168
  * Otherwise it's an empty string. The error code will be one of the
171
169
  * `BiometryErrorType` enum values, and is consistent across
172
- * platforms. This allows you to check for specific errors in a platform-
173
- * independent way, for example:
174
- *
175
- * if (result.code === BiometryErrorType.biometryNotEnrolled) {
176
- * ...
177
- * }
170
+ * platforms.
178
171
  */
179
172
  code: BiometryErrorType;
180
173
  }
@@ -182,6 +175,9 @@ export interface CheckBiometryResult {
182
175
  * The signature of the callback passed to `addResumeListener()`.
183
176
  */
184
177
  export type ResumeListener = (info: CheckBiometryResult) => void;
178
+ /**
179
+ * This is the public interface of the plugin.
180
+ */
185
181
  export interface BiometricAuthPlugin extends WebPlugin {
186
182
  /**
187
183
  * Check to see what biometry type (if any) is available.
@@ -218,6 +214,9 @@ export interface BiometricAuthPlugin extends WebPlugin {
218
214
  /**
219
215
  * Register a function that will be called when the app resumes.
220
216
  * The function will be passed the result of `checkBiometry()`.
217
+ *
218
+ * ๐Ÿ‘‰ **NOTE:** checkBiometry() must be called at least once
219
+ * before calling this method.
221
220
  */
222
221
  addResumeListener: (listener: ResumeListener) => Promise<PluginListenerHandle>;
223
222
  }
@@ -1,3 +1,4 @@
1
+ // noinspection JSUnusedGlobalSymbols
1
2
  export var BiometryType;
2
3
  (function (BiometryType) {
3
4
  /**
@@ -26,8 +27,8 @@ export var BiometryType;
26
27
  BiometryType[BiometryType["irisAuthentication"] = 5] = "irisAuthentication";
27
28
  })(BiometryType || (BiometryType = {}));
28
29
  /**
29
- * If the `authenticate()` method throws an exception, the error object
30
- * contains a .code property which will contain one of these strings,
30
+ * If the `authenticate()` method throws an exception, the `BiometryError`
31
+ * instance contains a .code property which will contain one of these strings,
31
32
  * indicating what the error was.
32
33
  *
33
34
  * See https://developer.apple.com/documentation/localauthentication/laerror
@@ -49,6 +50,9 @@ export var BiometryErrorType;
49
50
  BiometryErrorType["biometryNotEnrolled"] = "biometryNotEnrolled";
50
51
  BiometryErrorType["noDeviceCredential"] = "noDeviceCredential";
51
52
  })(BiometryErrorType || (BiometryErrorType = {}));
53
+ /**
54
+ * `authenticate()` throws instances of this class.
55
+ */
52
56
  export class BiometryError {
53
57
  constructor(message, code) {
54
58
  this.message = message;
@@ -4,6 +4,6 @@ import { BiometryType } from './definitions';
4
4
  export declare class BiometricAuthNative extends BiometricAuthBase {
5
5
  constructor(capProxy: BiometricAuthPlugin);
6
6
  checkBiometry(): Promise<CheckBiometryResult>;
7
- authenticate(options?: AuthenticateOptions): Promise<void>;
7
+ internalAuthenticate(options?: AuthenticateOptions): Promise<void>;
8
8
  setBiometryType(type: BiometryType | string | undefined): Promise<void>;
9
9
  }
@@ -4,11 +4,24 @@ import { BiometryErrorType, BiometryType } from './definitions';
4
4
  export class BiometricAuthNative extends BiometricAuthBase {
5
5
  constructor(capProxy) {
6
6
  super();
7
- this.checkBiometry = capProxy.checkBiometry;
8
- this.authenticate = capProxy.authenticate;
7
+ /*
8
+ In order to call native methods and maintain the ability to
9
+ call pure Javascript methods as well, we have to bind the native methods
10
+ to the proxy.
11
+
12
+ capProxy is a proxy of an instance of this class, so it is safe
13
+ to cast it to this class.
14
+ */
15
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
16
+ const proxy = capProxy;
17
+ /* eslint-disable @typescript-eslint/unbound-method */
18
+ this.checkBiometry = proxy.checkBiometry;
19
+ this.internalAuthenticate = proxy.internalAuthenticate;
20
+ /* eslint-enable */
9
21
  }
22
+ // @native
10
23
  async checkBiometry() {
11
- // Never used, satisfy the compiler
24
+ // Never used, but we have to satisfy the compiler.
12
25
  return Promise.resolve({
13
26
  isAvailable: true,
14
27
  biometryType: BiometryType.none,
@@ -17,12 +30,16 @@ export class BiometricAuthNative extends BiometricAuthBase {
17
30
  code: BiometryErrorType.none,
18
31
  });
19
32
  }
20
- // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
21
- async authenticate(options) { }
33
+ // @native
34
+ // On native platforms, this will present the native authentication UI.
35
+ async internalAuthenticate(
36
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
37
+ options) { }
38
+ // Web only, used for simulating biometric authentication.
22
39
  // eslint-disable-next-line @typescript-eslint/require-await
23
40
  async setBiometryType(
24
41
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
25
42
  type) {
26
- throw this.unimplemented('setBiometryType is web only');
43
+ throw this.unimplemented('setBiometryType() is web only');
27
44
  }
28
45
  }
package/dist/esm/web.d.ts CHANGED
@@ -4,6 +4,6 @@ import { BiometryType } from './definitions';
4
4
  export declare class BiometricAuthWeb extends BiometricAuthBase {
5
5
  private biometryType;
6
6
  checkBiometry(): Promise<CheckBiometryResult>;
7
- authenticate(options?: AuthenticateOptions): Promise<void>;
7
+ internalAuthenticate(options?: AuthenticateOptions): Promise<void>;
8
8
  setBiometryType(type: BiometryType | string | undefined): Promise<void>;
9
9
  }
package/dist/esm/web.js CHANGED
@@ -7,6 +7,7 @@ export class BiometricAuthWeb extends BiometricAuthBase {
7
7
  super(...arguments);
8
8
  this.biometryType = BiometryType.none;
9
9
  }
10
+ // On the web, return the fake biometry set by setBiometryType().
10
11
  async checkBiometry() {
11
12
  return Promise.resolve({
12
13
  isAvailable: this.biometryType !== BiometryType.none,
@@ -16,20 +17,23 @@ export class BiometricAuthWeb extends BiometricAuthBase {
16
17
  code: BiometryErrorType.none,
17
18
  });
18
19
  }
19
- async authenticate(options) {
20
- return this.checkBiometry().then(({ isAvailable, biometryType }) => {
21
- var _a;
22
- if (isAvailable) {
23
- if (
24
- // eslint-disable-next-line no-alert
25
- confirm((_a = options === null || options === void 0 ? void 0 : options.reason) !== null && _a !== void 0 ? _a : `Authenticate with ${getBiometryName(biometryType)}?`)) {
26
- return;
27
- }
28
- throw new BiometryError('User cancelled', BiometryErrorType.userCancel);
20
+ // On the web, fake authentication with a confirm dialog.
21
+ async internalAuthenticate(options) {
22
+ const { isAvailable, biometryType } = await this.checkBiometry();
23
+ if (isAvailable) {
24
+ if (
25
+ // eslint-disable-next-line no-alert
26
+ confirm(
27
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string
28
+ (options === null || options === void 0 ? void 0 : options.reason) ||
29
+ `Authenticate with ${getBiometryName(biometryType)}?`)) {
30
+ return;
29
31
  }
30
- throw new BiometryError('Biometry not available', BiometryErrorType.biometryNotAvailable);
31
- });
32
+ throw new BiometryError('User cancelled', BiometryErrorType.userCancel);
33
+ }
34
+ throw new BiometryError('Biometry not available', BiometryErrorType.biometryNotAvailable);
32
35
  }
36
+ // Web only, used for simulating biometric authentication.
33
37
  async setBiometryType(type) {
34
38
  if (typeof type === 'undefined') {
35
39
  return Promise.resolve();
@@ -3,6 +3,7 @@
3
3
  var core = require('@capacitor/core');
4
4
  var app = require('@capacitor/app');
5
5
 
6
+ // noinspection JSUnusedGlobalSymbols
6
7
  exports.BiometryType = void 0;
7
8
  (function (BiometryType) {
8
9
  /**
@@ -31,8 +32,8 @@ exports.BiometryType = void 0;
31
32
  BiometryType[BiometryType["irisAuthentication"] = 5] = "irisAuthentication";
32
33
  })(exports.BiometryType || (exports.BiometryType = {}));
33
34
  /**
34
- * If the `authenticate()` method throws an exception, the error object
35
- * contains a .code property which will contain one of these strings,
35
+ * If the `authenticate()` method throws an exception, the `BiometryError`
36
+ * instance contains a .code property which will contain one of these strings,
36
37
  * indicating what the error was.
37
38
  *
38
39
  * See https://developer.apple.com/documentation/localauthentication/laerror
@@ -54,6 +55,9 @@ exports.BiometryErrorType = void 0;
54
55
  BiometryErrorType["biometryNotEnrolled"] = "biometryNotEnrolled";
55
56
  BiometryErrorType["noDeviceCredential"] = "noDeviceCredential";
56
57
  })(exports.BiometryErrorType || (exports.BiometryErrorType = {}));
58
+ /**
59
+ * `authenticate()` throws instances of this class.
60
+ */
57
61
  class BiometryError {
58
62
  constructor(message, code) {
59
63
  this.message = message;
@@ -85,6 +89,23 @@ const proxy = core.registerPlugin('BiometricAuthNative', {
85
89
 
86
90
  // eslint-disable-next-line import/prefer-default-export
87
91
  class BiometricAuthBase extends core.WebPlugin {
92
+ async authenticate(options) {
93
+ try {
94
+ await this.internalAuthenticate(options);
95
+ }
96
+ catch (error) {
97
+ // error will be an instance of CapacitorException on native platforms,
98
+ // an instance of BiometryError on the web.
99
+ if (error instanceof core.CapacitorException) {
100
+ throw new BiometryError(error.message,
101
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- error.data values are typed as any
102
+ error.data.code);
103
+ }
104
+ else {
105
+ throw error;
106
+ }
107
+ }
108
+ }
88
109
  addResumeListener(listener) {
89
110
  return app.App.addListener('appStateChange', ({ isActive }) => {
90
111
  if (isActive) {
@@ -104,6 +125,7 @@ class BiometricAuthWeb extends BiometricAuthBase {
104
125
  super(...arguments);
105
126
  this.biometryType = exports.BiometryType.none;
106
127
  }
128
+ // On the web, return the fake biometry set by setBiometryType().
107
129
  async checkBiometry() {
108
130
  return Promise.resolve({
109
131
  isAvailable: this.biometryType !== exports.BiometryType.none,
@@ -113,20 +135,23 @@ class BiometricAuthWeb extends BiometricAuthBase {
113
135
  code: exports.BiometryErrorType.none,
114
136
  });
115
137
  }
116
- async authenticate(options) {
117
- return this.checkBiometry().then(({ isAvailable, biometryType }) => {
118
- var _a;
119
- if (isAvailable) {
120
- if (
121
- // eslint-disable-next-line no-alert
122
- confirm((_a = options === null || options === void 0 ? void 0 : options.reason) !== null && _a !== void 0 ? _a : `Authenticate with ${getBiometryName(biometryType)}?`)) {
123
- return;
124
- }
125
- throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel);
138
+ // On the web, fake authentication with a confirm dialog.
139
+ async internalAuthenticate(options) {
140
+ const { isAvailable, biometryType } = await this.checkBiometry();
141
+ if (isAvailable) {
142
+ if (
143
+ // eslint-disable-next-line no-alert
144
+ confirm(
145
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string
146
+ (options === null || options === void 0 ? void 0 : options.reason) ||
147
+ `Authenticate with ${getBiometryName(biometryType)}?`)) {
148
+ return;
126
149
  }
127
- throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable);
128
- });
150
+ throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel);
151
+ }
152
+ throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable);
129
153
  }
154
+ // Web only, used for simulating biometric authentication.
130
155
  async setBiometryType(type) {
131
156
  if (typeof type === 'undefined') {
132
157
  return Promise.resolve();
@@ -154,11 +179,24 @@ var web = /*#__PURE__*/Object.freeze({
154
179
  class BiometricAuthNative extends BiometricAuthBase {
155
180
  constructor(capProxy) {
156
181
  super();
157
- this.checkBiometry = capProxy.checkBiometry;
158
- this.authenticate = capProxy.authenticate;
182
+ /*
183
+ In order to call native methods and maintain the ability to
184
+ call pure Javascript methods as well, we have to bind the native methods
185
+ to the proxy.
186
+
187
+ capProxy is a proxy of an instance of this class, so it is safe
188
+ to cast it to this class.
189
+ */
190
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
191
+ const proxy = capProxy;
192
+ /* eslint-disable @typescript-eslint/unbound-method */
193
+ this.checkBiometry = proxy.checkBiometry;
194
+ this.internalAuthenticate = proxy.internalAuthenticate;
195
+ /* eslint-enable */
159
196
  }
197
+ // @native
160
198
  async checkBiometry() {
161
- // Never used, satisfy the compiler
199
+ // Never used, but we have to satisfy the compiler.
162
200
  return Promise.resolve({
163
201
  isAvailable: true,
164
202
  biometryType: exports.BiometryType.none,
@@ -167,13 +205,17 @@ class BiometricAuthNative extends BiometricAuthBase {
167
205
  code: exports.BiometryErrorType.none,
168
206
  });
169
207
  }
170
- // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
171
- async authenticate(options) { }
208
+ // @native
209
+ // On native platforms, this will present the native authentication UI.
210
+ async internalAuthenticate(
211
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
212
+ options) { }
213
+ // Web only, used for simulating biometric authentication.
172
214
  // eslint-disable-next-line @typescript-eslint/require-await
173
215
  async setBiometryType(
174
216
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
175
217
  type) {
176
- throw this.unimplemented('setBiometryType is web only');
218
+ throw this.unimplemented('setBiometryType() is web only');
177
219
  }
178
220
  }
179
221
 
package/dist/plugin.js CHANGED
@@ -1,6 +1,7 @@
1
1
  var capacitorBiometricAuth = (function (exports, core, app) {
2
2
  'use strict';
3
3
 
4
+ // noinspection JSUnusedGlobalSymbols
4
5
  exports.BiometryType = void 0;
5
6
  (function (BiometryType) {
6
7
  /**
@@ -29,8 +30,8 @@ var capacitorBiometricAuth = (function (exports, core, app) {
29
30
  BiometryType[BiometryType["irisAuthentication"] = 5] = "irisAuthentication";
30
31
  })(exports.BiometryType || (exports.BiometryType = {}));
31
32
  /**
32
- * If the `authenticate()` method throws an exception, the error object
33
- * contains a .code property which will contain one of these strings,
33
+ * If the `authenticate()` method throws an exception, the `BiometryError`
34
+ * instance contains a .code property which will contain one of these strings,
34
35
  * indicating what the error was.
35
36
  *
36
37
  * See https://developer.apple.com/documentation/localauthentication/laerror
@@ -52,6 +53,9 @@ var capacitorBiometricAuth = (function (exports, core, app) {
52
53
  BiometryErrorType["biometryNotEnrolled"] = "biometryNotEnrolled";
53
54
  BiometryErrorType["noDeviceCredential"] = "noDeviceCredential";
54
55
  })(exports.BiometryErrorType || (exports.BiometryErrorType = {}));
56
+ /**
57
+ * `authenticate()` throws instances of this class.
58
+ */
55
59
  class BiometryError {
56
60
  constructor(message, code) {
57
61
  this.message = message;
@@ -83,6 +87,23 @@ var capacitorBiometricAuth = (function (exports, core, app) {
83
87
 
84
88
  // eslint-disable-next-line import/prefer-default-export
85
89
  class BiometricAuthBase extends core.WebPlugin {
90
+ async authenticate(options) {
91
+ try {
92
+ await this.internalAuthenticate(options);
93
+ }
94
+ catch (error) {
95
+ // error will be an instance of CapacitorException on native platforms,
96
+ // an instance of BiometryError on the web.
97
+ if (error instanceof core.CapacitorException) {
98
+ throw new BiometryError(error.message,
99
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- error.data values are typed as any
100
+ error.data.code);
101
+ }
102
+ else {
103
+ throw error;
104
+ }
105
+ }
106
+ }
86
107
  addResumeListener(listener) {
87
108
  return app.App.addListener('appStateChange', ({ isActive }) => {
88
109
  if (isActive) {
@@ -102,6 +123,7 @@ var capacitorBiometricAuth = (function (exports, core, app) {
102
123
  super(...arguments);
103
124
  this.biometryType = exports.BiometryType.none;
104
125
  }
126
+ // On the web, return the fake biometry set by setBiometryType().
105
127
  async checkBiometry() {
106
128
  return Promise.resolve({
107
129
  isAvailable: this.biometryType !== exports.BiometryType.none,
@@ -111,20 +133,23 @@ var capacitorBiometricAuth = (function (exports, core, app) {
111
133
  code: exports.BiometryErrorType.none,
112
134
  });
113
135
  }
114
- async authenticate(options) {
115
- return this.checkBiometry().then(({ isAvailable, biometryType }) => {
116
- var _a;
117
- if (isAvailable) {
118
- if (
119
- // eslint-disable-next-line no-alert
120
- confirm((_a = options === null || options === void 0 ? void 0 : options.reason) !== null && _a !== void 0 ? _a : `Authenticate with ${getBiometryName(biometryType)}?`)) {
121
- return;
122
- }
123
- throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel);
136
+ // On the web, fake authentication with a confirm dialog.
137
+ async internalAuthenticate(options) {
138
+ const { isAvailable, biometryType } = await this.checkBiometry();
139
+ if (isAvailable) {
140
+ if (
141
+ // eslint-disable-next-line no-alert
142
+ confirm(
143
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to use the default value if options?.reason is an empty string
144
+ (options === null || options === void 0 ? void 0 : options.reason) ||
145
+ `Authenticate with ${getBiometryName(biometryType)}?`)) {
146
+ return;
124
147
  }
125
- throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable);
126
- });
148
+ throw new BiometryError('User cancelled', exports.BiometryErrorType.userCancel);
149
+ }
150
+ throw new BiometryError('Biometry not available', exports.BiometryErrorType.biometryNotAvailable);
127
151
  }
152
+ // Web only, used for simulating biometric authentication.
128
153
  async setBiometryType(type) {
129
154
  if (typeof type === 'undefined') {
130
155
  return Promise.resolve();
@@ -152,11 +177,24 @@ var capacitorBiometricAuth = (function (exports, core, app) {
152
177
  class BiometricAuthNative extends BiometricAuthBase {
153
178
  constructor(capProxy) {
154
179
  super();
155
- this.checkBiometry = capProxy.checkBiometry;
156
- this.authenticate = capProxy.authenticate;
180
+ /*
181
+ In order to call native methods and maintain the ability to
182
+ call pure Javascript methods as well, we have to bind the native methods
183
+ to the proxy.
184
+
185
+ capProxy is a proxy of an instance of this class, so it is safe
186
+ to cast it to this class.
187
+ */
188
+ // eslint-disable-next-line @typescript-eslint/consistent-type-assertions
189
+ const proxy = capProxy;
190
+ /* eslint-disable @typescript-eslint/unbound-method */
191
+ this.checkBiometry = proxy.checkBiometry;
192
+ this.internalAuthenticate = proxy.internalAuthenticate;
193
+ /* eslint-enable */
157
194
  }
195
+ // @native
158
196
  async checkBiometry() {
159
- // Never used, satisfy the compiler
197
+ // Never used, but we have to satisfy the compiler.
160
198
  return Promise.resolve({
161
199
  isAvailable: true,
162
200
  biometryType: exports.BiometryType.none,
@@ -165,13 +203,17 @@ var capacitorBiometricAuth = (function (exports, core, app) {
165
203
  code: exports.BiometryErrorType.none,
166
204
  });
167
205
  }
168
- // eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-function
169
- async authenticate(options) { }
206
+ // @native
207
+ // On native platforms, this will present the native authentication UI.
208
+ async internalAuthenticate(
209
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
210
+ options) { }
211
+ // Web only, used for simulating biometric authentication.
170
212
  // eslint-disable-next-line @typescript-eslint/require-await
171
213
  async setBiometryType(
172
214
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
173
215
  type) {
174
- throw this.unimplemented('setBiometryType is web only');
216
+ throw this.unimplemented('setBiometryType() is web only');
175
217
  }
176
218
  }
177
219
 
@@ -3,5 +3,5 @@
3
3
 
4
4
  CAP_PLUGIN(BiometricAuthNative, "BiometricAuthNative",
5
5
  CAP_PLUGIN_METHOD(checkBiometry, CAPPluginReturnPromise);
6
- CAP_PLUGIN_METHOD(authenticate, CAPPluginReturnPromise);
6
+ CAP_PLUGIN_METHOD(internalAuthenticate, CAPPluginReturnPromise);
7
7
  )
@@ -22,12 +22,32 @@ public class BiometricAuthNative: CAPPlugin {
22
22
  LAError.biometryNotEnrolled.rawValue: "biometryNotEnrolled"
23
23
  ]
24
24
 
25
- var canEvaluatePolicy = true
25
+ struct CheckDeviceBiometryResult {
26
+ let isAvailable: Bool
27
+ let biometryType: LABiometryType.RawValue
28
+ let biometryTypes: JSArray
29
+ let reason: String
30
+ let code: String
31
+ }
26
32
 
27
33
  /**
28
- * Check the device's availability and type of biometric authentication.
34
+ * Plugin call checkBiometry()
29
35
  */
30
36
  @objc func checkBiometry(_ call: CAPPluginCall) {
37
+ let checkResult = checkDeviceBiometry()
38
+ call.resolve([
39
+ "isAvailable": checkResult.isAvailable,
40
+ "biometryType": checkResult.biometryType,
41
+ "biometryTypes": checkResult.biometryTypes,
42
+ "reason": checkResult.reason,
43
+ "code": checkResult.code
44
+ ])
45
+ }
46
+
47
+ /**
48
+ * Check the device's availability and type of biometric authentication.
49
+ */
50
+ func checkDeviceBiometry() -> CheckDeviceBiometryResult {
31
51
  let context = LAContext()
32
52
  var error: NSError?
33
53
  var available = context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
@@ -42,7 +62,6 @@ public class BiometricAuthNative: CAPPlugin {
42
62
 
43
63
  if entry == nil {
44
64
  available = false
45
- canEvaluatePolicy = false
46
65
  reason = kMissingFaceIDUsageEntry
47
66
  errorCode = biometryErrorCodeMap[LAError.biometryNotAvailable.rawValue] ?? ""
48
67
  }
@@ -61,13 +80,13 @@ public class BiometricAuthNative: CAPPlugin {
61
80
  var types = JSArray()
62
81
  types.append(context.biometryType.rawValue)
63
82
 
64
- call.resolve([
65
- "isAvailable": available,
66
- "biometryType": context.biometryType.rawValue,
67
- "biometryTypes": types,
68
- "reason": reason,
69
- "code": errorCode
70
- ])
83
+ return CheckDeviceBiometryResult(
84
+ isAvailable: available,
85
+ biometryType: context.biometryType.rawValue,
86
+ biometryTypes: types,
87
+ reason: reason,
88
+ code: errorCode
89
+ )
71
90
  }
72
91
 
73
92
  /**
@@ -77,11 +96,13 @@ public class BiometricAuthNative: CAPPlugin {
77
96
  * @returns {Promise<void>}
78
97
  * @rejects {BiometricResultError}
79
98
  */
80
- @objc func authenticate(_ call: CAPPluginCall) {
99
+ @objc func internalAuthenticate(_ call: CAPPluginCall) {
81
100
  // Make sure the app can evaluate policy, otherwise evaluatePolicy() will crash
82
- guard canEvaluatePolicy else {
101
+ let checkResult = checkDeviceBiometry()
102
+
103
+ guard checkResult.isAvailable else {
83
104
  call.reject(
84
- kMissingFaceIDUsageEntry,
105
+ checkResult.reason,
85
106
  biometryErrorCodeMap[LAError.biometryNotAvailable.rawValue]
86
107
  )
87
108
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aparajita/capacitor-biometric-auth",
3
- "version": "5.2.0",
3
+ "version": "6.0.0",
4
4
  "description": "Provides access to the native biometric auth APIs for Capacitor apps",
5
5
  "author": "Aparajita Fishman",
6
6
  "license": "MIT",
@@ -9,7 +9,7 @@
9
9
  "types": "dist/esm/index.d.ts",
10
10
  "unpkg": "dist/plugin.js",
11
11
  "engines": {
12
- "node": ">=16.15.1"
12
+ "node": ">=18"
13
13
  },
14
14
  "files": [
15
15
  "android/src/main/",
@@ -56,35 +56,35 @@
56
56
  "@aparajita/eslint-config-base": "^1.1.6",
57
57
  "@aparajita/prettier-config": "^2.0.0",
58
58
  "@aparajita/swiftly": "^1.0.4",
59
- "@capacitor/cli": "^5.4.1",
60
- "@commitlint/cli": "^17.7.2",
61
- "@commitlint/config-conventional": "^17.7.0",
59
+ "@capacitor/cli": "^5.5.1",
60
+ "@commitlint/cli": "^18.4.1",
61
+ "@commitlint/config-conventional": "^18.4.0",
62
62
  "@ionic/swiftlint-config": "^1.1.2",
63
- "@rollup/plugin-json": "^6.0.0",
64
- "@types/node": "^20.8.0",
65
- "@typescript-eslint/eslint-plugin": "^6.7.3",
66
- "@typescript-eslint/parser": "^6.7.3",
67
- "commit-and-tag-version": "^11.2.3",
68
- "eslint": "^8.50.0",
63
+ "@rollup/plugin-json": "^6.0.1",
64
+ "@types/node": "^20.9.0",
65
+ "@typescript-eslint/eslint-plugin": "^6.11.0",
66
+ "@typescript-eslint/parser": "^6.11.0",
67
+ "commit-and-tag-version": "^12.0.0",
68
+ "eslint": "^8.53.0",
69
69
  "eslint-config-prettier": "^9.0.0",
70
70
  "eslint-config-standard": "^17.1.0",
71
71
  "eslint-import-resolver-typescript": "^3.6.1",
72
- "eslint-plugin-import": "^2.28.1",
73
- "eslint-plugin-n": "^16.1.0",
72
+ "eslint-plugin-import": "^2.29.0",
73
+ "eslint-plugin-n": "^16.3.1",
74
74
  "eslint-plugin-promise": "^6.1.1",
75
75
  "nodemon": "^3.0.1",
76
- "prettier": "^3.0.3",
77
- "prettier-plugin-java": "^2.3.1",
76
+ "prettier": "^3.1.0",
77
+ "prettier-plugin-java": "^2.4.0",
78
78
  "rimraf": "^5.0.5",
79
- "rollup": "^3.29.4",
79
+ "rollup": "^4.4.0",
80
80
  "swiftlint": "^1.0.2",
81
81
  "typescript": "~5.2.2"
82
82
  },
83
83
  "dependencies": {
84
- "@capacitor/android": "^5.4.1",
84
+ "@capacitor/android": "^5.5.1",
85
85
  "@capacitor/app": "^5.0.6",
86
- "@capacitor/core": "^5.4.1",
87
- "@capacitor/ios": "^5.4.1"
86
+ "@capacitor/core": "^5.5.1",
87
+ "@capacitor/ios": "^5.5.1"
88
88
  },
89
89
  "scripts": {
90
90
  "clean": "rimraf dist",