@symbiote-native/local-auth 0.3.0 → 1.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
@@ -3,11 +3,11 @@
3
3
  A wrapper package for [SymbioteNative](../../README.md) that makes
4
4
  [`expo-local-authentication`](https://github.com/expo/expo/tree/main/packages/expo-local-authentication)
5
5
  — FaceID/TouchID on iOS, the Fingerprint/Biometric API on Android — usable from **every**
6
- adapter, React, Vue, and Angular, not just React. Unlike this repo's other Expo wrapper
6
+ adapter, React, Vue, Svelte, Solid, and Angular, not just React. Unlike this repo's other Expo wrapper
7
7
  ([`@symbiote-native/sensors`](../sensors), an `EventEmitter` + live-subscription surface),
8
8
  every function here is a one-shot async call with no per-instance state, so there is no hook/
9
- composable/service to wrap — the React, Vue, and Angular entry points are plain re-exports of
10
- the same `core`.
9
+ composable/service to wrap — the React, Vue, Svelte, Solid, and Angular entry points are plain
10
+ re-exports of the same `core`.
11
11
 
12
12
  ## Install
13
13
 
@@ -26,12 +26,12 @@ Unlike a plain RN native module, `expo-local-authentication`'s native code is di
26
26
  into the native host app **once**, covering this package and every other
27
27
  `expo-modules-core` package with zero further changes:
28
28
 
29
- | Platform | Touches |
30
- |---|---|
31
- | iOS | `ios/Podfile` — add `use_expo_modules!` |
32
- | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
33
- | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
34
- | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
29
+ | Platform | Touches |
30
+ | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
31
+ | iOS | `ios/Podfile` — add `use_expo_modules!` |
32
+ | iOS | `AppDelegate.swift` — Expo's runtime-bootstrap hook |
33
+ | Android | `settings.gradle` / `app/build.gradle` — resolve and include the Expo Gradle projects |
34
+ | Android | `MainApplication.kt` — Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
35
35
 
36
36
  Full mechanics live in the `symbiote-expo-native-module` project skill. Reference
37
37
  implementation: `examples/expo-react/ios/Podfile` and
@@ -52,13 +52,13 @@ src/core/ hasHardwareAsync / isEnrolledAsync / getEnrolledLevelAsync /
52
52
  AuthenticationType, SecurityLevel, and the option/result/error types.
53
53
  native-module.ts resolves the native module via expo-modules-core's
54
54
  requireNativeModule.
55
- src/react/ @symbiote-native/local-auth/react — export * from '../core'
56
- src/vue/ @symbiote-native/local-auth/vue — export * from '../core'
57
55
  src/angular/ @symbiote-native/local-auth/angular — export * from '../core'
58
56
  ```
59
57
 
60
- No per-adapter lifecycle wrapper exists because there's nothing to subscribe to or clean up —
61
- each adapter entry is a single-file re-export.
58
+ `./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto
59
+ `src/core/` — no physical per-framework file, since there's nothing to subscribe to or clean up.
60
+ `./angular` stays a physical file/subpath since Angular ships through a separate `ngc`/AOT build
61
+ (`build-ngc/`).
62
62
 
63
63
  ## Use it
64
64
 
@@ -77,7 +77,8 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/rea
77
77
  function LocalAuthScreen() {
78
78
  const [hasHardware, setHasHardware] = useState(false);
79
79
  const [isEnrolled, setIsEnrolled] = useState(false);
80
- const [authResult, setAuthResult] = useState<ILocalAuthenticationResult | null>(null);
80
+ const [authResult, setAuthResult] =
81
+ useState<ILocalAuthenticationResult | null>(null);
81
82
 
82
83
  useEffect(() => {
83
84
  hasHardwareAsync().then(setHasHardware);
@@ -85,12 +86,18 @@ function LocalAuthScreen() {
85
86
  }, []);
86
87
 
87
88
  const handleAuthenticate = () => {
88
- authenticateAsync({ promptMessage: 'Confirm it is you' }).then(setAuthResult);
89
+ authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
90
+ setAuthResult,
91
+ );
89
92
  };
90
93
 
91
94
  return (
92
95
  <View>
93
- <Text>{hasHardware && isEnrolled ? 'Ready to authenticate' : 'No biometrics enrolled'}</Text>
96
+ <Text>
97
+ {hasHardware && isEnrolled
98
+ ? 'Ready to authenticate'
99
+ : 'No biometrics enrolled'}
100
+ </Text>
94
101
  <Pressable onPress={handleAuthenticate}>
95
102
  <Text>Authenticate</Text>
96
103
  </Pressable>
@@ -99,7 +106,11 @@ function LocalAuthScreen() {
99
106
  <Text>Cancel</Text>
100
107
  </Pressable>
101
108
  )}
102
- {authResult && <Text>{authResult.success ? 'Success' : `Failed: ${authResult.error}`}</Text>}
109
+ {authResult && (
110
+ <Text>
111
+ {authResult.success ? 'Success' : `Failed: ${authResult.error}`}
112
+ </Text>
113
+ )}
103
114
  </View>
104
115
  );
105
116
  }
@@ -136,18 +147,126 @@ function handleAuthenticate(): void {
136
147
 
137
148
  <template>
138
149
  <View>
139
- <Text>{{ hasHardware && isEnrolled ? 'Ready to authenticate' : 'No biometrics enrolled' }}</Text>
150
+ <Text>{{
151
+ hasHardware && isEnrolled
152
+ ? 'Ready to authenticate'
153
+ : 'No biometrics enrolled'
154
+ }}</Text>
140
155
  <Pressable @press="handleAuthenticate">
141
156
  <Text>Authenticate</Text>
142
157
  </Pressable>
143
158
  <Pressable v-if="Platform.OS === 'android'" @press="cancelAuthenticate">
144
159
  <Text>Cancel</Text>
145
160
  </Pressable>
146
- <Text v-if="authResult">{{ authResult.success ? 'Success' : `Failed: ${authResult.error}` }}</Text>
161
+ <Text v-if="authResult">{{
162
+ authResult.success ? 'Success' : `Failed: ${authResult.error}`
163
+ }}</Text>
147
164
  </View>
148
165
  </template>
149
166
  ```
150
167
 
168
+ ```svelte
169
+ <!-- Svelte -->
170
+ <script lang="ts">
171
+ import { Platform, Pressable, Text, View } from '@symbiote-native/svelte';
172
+ import {
173
+ authenticateAsync,
174
+ cancelAuthenticate,
175
+ hasHardwareAsync,
176
+ isEnrolledAsync,
177
+ } from '@symbiote-native/local-auth/svelte';
178
+ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/svelte';
179
+
180
+ let hasHardware = $state(false);
181
+ let isEnrolled = $state(false);
182
+ let authResult = $state<ILocalAuthenticationResult | null>(null);
183
+
184
+ $effect(() => {
185
+ hasHardwareAsync().then(value => (hasHardware = value));
186
+ isEnrolledAsync().then(value => (isEnrolled = value));
187
+ });
188
+
189
+ function handleAuthenticate(): void {
190
+ authenticateAsync({ promptMessage: 'Confirm it is you' }).then(value => {
191
+ authResult = value;
192
+ });
193
+ }
194
+ </script>
195
+
196
+ <View>
197
+ <Text>
198
+ {hasHardware && isEnrolled ? 'Ready to authenticate' : 'No biometrics enrolled'}
199
+ </Text>
200
+ <Pressable onPress={handleAuthenticate}>
201
+ <Text>Authenticate</Text>
202
+ </Pressable>
203
+ {#if Platform.OS === 'android'}
204
+ <Pressable onPress={() => cancelAuthenticate()}>
205
+ <Text>Cancel</Text>
206
+ </Pressable>
207
+ {/if}
208
+ {#if authResult}
209
+ <Text>{authResult.success ? 'Success' : `Failed: ${authResult.error}`}</Text>
210
+ {/if}
211
+ </View>
212
+ ```
213
+
214
+ ```tsx
215
+ // Solid
216
+ import { Show, createSignal, onMount } from 'solid-js';
217
+ import { Platform, Pressable, Text, View } from '@symbiote-native/solid';
218
+ import {
219
+ authenticateAsync,
220
+ cancelAuthenticate,
221
+ hasHardwareAsync,
222
+ isEnrolledAsync,
223
+ } from '@symbiote-native/local-auth/solid';
224
+ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/solid';
225
+
226
+ function LocalAuthScreen() {
227
+ const [hasHardware, setHasHardware] = createSignal(false);
228
+ const [isEnrolled, setIsEnrolled] = createSignal(false);
229
+ const [authResult, setAuthResult] =
230
+ createSignal<ILocalAuthenticationResult | null>(null);
231
+
232
+ onMount(() => {
233
+ void hasHardwareAsync().then(setHasHardware);
234
+ void isEnrolledAsync().then(setIsEnrolled);
235
+ });
236
+
237
+ const handleAuthenticate = () => {
238
+ void authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
239
+ setAuthResult,
240
+ );
241
+ };
242
+
243
+ return (
244
+ <View>
245
+ <Text>
246
+ {hasHardware() && isEnrolled()
247
+ ? 'Ready to authenticate'
248
+ : 'No biometrics enrolled'}
249
+ </Text>
250
+ <Pressable onPress={handleAuthenticate}>
251
+ <Text>Authenticate</Text>
252
+ </Pressable>
253
+ {Platform.OS === 'android' && (
254
+ <Pressable onPress={() => cancelAuthenticate()}>
255
+ <Text>Cancel</Text>
256
+ </Pressable>
257
+ )}
258
+ <Show when={authResult()} keyed>
259
+ {result => (
260
+ <Text>
261
+ {result.success ? 'Success' : `Failed: ${result.error}`}
262
+ </Text>
263
+ )}
264
+ </Show>
265
+ </View>
266
+ );
267
+ }
268
+ ```
269
+
151
270
  ```ts
152
271
  // Angular
153
272
  import { Component, signal } from '@angular/core';
@@ -165,7 +284,11 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/ang
165
284
  imports: [Pressable, Text, View],
166
285
  template: `
167
286
  <View>
168
- <Text>{{ hasHardware() && isEnrolled() ? 'Ready to authenticate' : 'No biometrics enrolled' }}</Text>
287
+ <Text>{{
288
+ hasHardware() && isEnrolled()
289
+ ? 'Ready to authenticate'
290
+ : 'No biometrics enrolled'
291
+ }}</Text>
169
292
  <Pressable (press)="handleAuthenticate()">
170
293
  <Text>Authenticate</Text>
171
294
  </Pressable>
@@ -175,7 +298,9 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/ang
175
298
  </Pressable>
176
299
  }
177
300
  @if (authResult(); as result) {
178
- <Text>{{ result.success ? 'Success' : 'Failed: ' + result.error }}</Text>
301
+ <Text>{{
302
+ result.success ? 'Success' : 'Failed: ' + result.error
303
+ }}</Text>
179
304
  }
180
305
  </View>
181
306
  `,
@@ -192,7 +317,9 @@ export class LocalAuthScreen {
192
317
  }
193
318
 
194
319
  handleAuthenticate(): void {
195
- authenticateAsync({ promptMessage: 'Confirm it is you' }).then(value => this.authResult.set(value));
320
+ authenticateAsync({ promptMessage: 'Confirm it is you' }).then(value =>
321
+ this.authResult.set(value),
322
+ );
196
323
  }
197
324
 
198
325
  handleCancel(): void {
@@ -202,17 +329,19 @@ export class LocalAuthScreen {
202
329
  ```
203
330
 
204
331
  There's no per-instance service to `inject()` in the Angular case — every function is a plain
205
- free function off the core package, called straight from the constructor. All three examples are
206
- trimmed from the real canary demo screens (`examples/expo-react/screens/LocalAuthScreen.tsx`,
332
+ free function off the core package, called straight from the constructor. The React/Vue/Angular
333
+ examples are trimmed from the real canary demo screens
334
+ (`examples/expo-react/screens/LocalAuthScreen.tsx`,
207
335
  `examples/expo-vue-sfc/screens/LocalAuthScreen.vue`, `examples/expo-vue-tsx/screens/LocalAuthScreen.tsx`,
208
336
  `examples/expo-angular/src/screens/LocalAuthScreen.ts`), which also cover
209
- `getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card.
337
+ `getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card. No
338
+ canary demo screen exists yet for Svelte/Solid — the two snippets above follow the same surface.
210
339
 
211
340
  ## API
212
341
 
213
342
  Free functions, no event stream, no per-instance state — upstream ships a handful of async
214
- functions and two enums, not a subscribable sensor, so the React/Vue/Angular entry points above
215
- are plain re-exports of `core` with nothing adapter-specific to add.
343
+ functions and two enums, not a subscribable sensor, so the React/Vue/Svelte/Solid/Angular entry
344
+ points above are plain re-exports of `core` with nothing adapter-specific to add.
216
345
 
217
346
  ```ts
218
347
  hasHardwareAsync(): Promise<boolean>
@@ -229,10 +358,15 @@ from upstream's `LocalAuthentication.types.ts`, renamed with this repo's `I`-pre
229
358
  for exported types (`ts-js-best-practices`).
230
359
 
231
360
  ```ts
232
- import { authenticateAsync, hasHardwareAsync } from '@symbiote-native/local-auth';
361
+ import {
362
+ authenticateAsync,
363
+ hasHardwareAsync,
364
+ } from '@symbiote-native/local-auth';
233
365
  // or the framework-scoped entry points — identical surface, re-exported verbatim:
234
366
  import { authenticateAsync } from '@symbiote-native/local-auth/react';
235
367
  import { authenticateAsync } from '@symbiote-native/local-auth/vue';
368
+ import { authenticateAsync } from '@symbiote-native/local-auth/svelte';
369
+ import { authenticateAsync } from '@symbiote-native/local-auth/solid';
236
370
  import { authenticateAsync } from '@symbiote-native/local-auth/angular';
237
371
  ```
238
372
 
@@ -241,7 +375,7 @@ import { authenticateAsync } from '@symbiote-native/local-auth/angular';
241
375
  - **`not_enrolled` on Android almost always means the device's own lock screen has no PIN,
242
376
  pattern, or password set.** A real symptom on a fresh emulator or factory-reset device:
243
377
  `authenticateAsync` resolves `{ success: false, error: 'not_enrolled', warning:
244
- 'KeyguardManager#isDeviceSecure() returned false' }`. This is **not** a missing app
378
+ 'KeyguardManager#isDeviceSecure() returned false' }`. This is **not** a missing app
245
379
  permission — the manifest permission this package needs is an ordinary build-time merge with
246
380
  no runtime prompt, so there's nothing for your app to request. The fix lives on the device:
247
381
  Settings → Security → Screen lock → set a PIN/pattern/password, then optionally enroll a
@@ -259,10 +393,11 @@ a view or per-instance state. Tests inject a fake native-module object in place
259
393
  `src/core/types.test.ts`, `vitest`) — no `installFabric()`, no ViewConfig. Native rendering itself
260
394
  is verified on-device (see the parent [README](../../README.md) for the project's testing model).
261
395
 
262
- Native autolinking wiring is done in the four Expo canary apps
263
- (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`)
264
- — iOS Podfile/`AppDelegate.swift` + `NSFaceIDUsageDescription`, Android Gradle/
265
- `MainApplication.kt` + `USE_BIOMETRIC`, all four confirmed present. The one remaining gap: this
266
- package isn't yet demoed in the plain, non-Expo `examples/react`/`vue-sfc`/`vue-tsx`/`angular`
267
- canaries, since an `expo-modules-core` package needs the `expo-modules-autolinking` wiring only
268
- the `examples/expo-*` apps have set up so far.
396
+ Native autolinking wiring is done in all six Expo canary apps
397
+ (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
398
+ `examples/expo-solid`, `examples/expo-angular`) — iOS Podfile/`AppDelegate.swift` +
399
+ `NSFaceIDUsageDescription`, Android Gradle/`MainApplication.kt` + `USE_BIOMETRIC`, all six
400
+ confirmed present. The one remaining gap: this package isn't yet demoed in the plain, non-Expo
401
+ `examples/react`/`vue-sfc`/`vue-tsx`/`svelte`/`solid`/`angular` canaries, since an
402
+ `expo-modules-core` package needs the `expo-modules-autolinking` wiring only the
403
+ `examples/expo-*` apps have set up so far.
@@ -55,7 +55,8 @@ export async function authenticateAsync(options = {}) {
55
55
  throw new UnavailabilityError(NATIVE_MODULE_NAME, 'authenticateAsync');
56
56
  }
57
57
  if (options.promptMessage !== undefined) {
58
- invariant(typeof options.promptMessage === 'string' && options.promptMessage.length > 0, 'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.');
58
+ invariant(typeof options.promptMessage === 'string' &&
59
+ options.promptMessage.length > 0, 'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.');
59
60
  }
60
61
  return expoLocalAuthentication.authenticateAsync({
61
62
  ...options,
@@ -55,7 +55,8 @@ export async function authenticateAsync(options = {}) {
55
55
  throw new UnavailabilityError(NATIVE_MODULE_NAME, 'authenticateAsync');
56
56
  }
57
57
  if (options.promptMessage !== undefined) {
58
- invariant(typeof options.promptMessage === 'string' && options.promptMessage.length > 0, 'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.');
58
+ invariant(typeof options.promptMessage === 'string' &&
59
+ options.promptMessage.length > 0, 'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.');
59
60
  }
60
61
  return expoLocalAuthentication.authenticateAsync({
61
62
  ...options,
@@ -1 +1 @@
1
- {"version":3,"file":"local-authentication.js","sourceRoot":"","sources":["../../src/core/local-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAQ1D,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AACvD,MAAM,sBAAsB,GAAG,cAAc,CAAC;AAC9C,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAEtC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;IACrD,IAAI,CAAC,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;QAC/D,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mCAAmC,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC,uBAAuB,CAAC,eAAe,EAAE,CAAC;QAC7C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,uBAAuB,CAAC,eAAe,EAAE,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;QACnD,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,uBAAuB,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAuC,EAAE;IAEzC,IAAI,CAAC,uBAAuB,CAAC,iBAAiB,EAAE,CAAC;QAC/C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACxC,SAAS,CACP,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAC7E,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IAED,OAAO,uBAAuB,CAAC,iBAAiB,CAAC;QAC/C,GAAG,OAAO;QACV,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,sBAAsB;QAC9D,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,oBAAoB;KACzD,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;QAChD,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"local-authentication.js","sourceRoot":"","sources":["../../src/core/local-authentication.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,SAAS,MAAM,WAAW,CAAC;AAElC,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAQ1D,MAAM,kBAAkB,GAAG,2BAA2B,CAAC;AACvD,MAAM,sBAAsB,GAAG,cAAc,CAAC;AAC9C,MAAM,oBAAoB,GAAG,QAAQ,CAAC;AAEtC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,IAAI,CAAC,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;QAC9C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,kBAAkB,CAAC,CAAC;IACxE,CAAC;IACD,OAAO,uBAAuB,CAAC,gBAAgB,EAAE,CAAC;AACpD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iCAAiC;IAGrD,IAAI,CAAC,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;QAC/D,MAAM,IAAI,mBAAmB,CAC3B,kBAAkB,EAClB,mCAAmC,CACpC,CAAC;IACJ,CAAC;IACD,OAAO,uBAAuB,CAAC,iCAAiC,EAAE,CAAC;AACrE,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe;IACnC,IAAI,CAAC,uBAAuB,CAAC,eAAe,EAAE,CAAC;QAC7C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,uBAAuB,CAAC,eAAe,EAAE,CAAC;AACnD,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;QACnD,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,uBAAuB,CAAC,CAAC;IAC7E,CAAC;IACD,OAAO,uBAAuB,CAAC,qBAAqB,EAAE,CAAC;AACzD,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,UAAuC,EAAE;IAEzC,IAAI,CAAC,uBAAuB,CAAC,iBAAiB,EAAE,CAAC;QAC/C,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,mBAAmB,CAAC,CAAC;IACzE,CAAC;IAED,IAAI,OAAO,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QACxC,SAAS,CACP,OAAO,OAAO,CAAC,aAAa,KAAK,QAAQ;YACvC,OAAO,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC,EAClC,4FAA4F,CAC7F,CAAC;IACJ,CAAC;IAED,OAAO,uBAAuB,CAAC,iBAAiB,CAAC;QAC/C,GAAG,OAAO;QACV,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,sBAAsB;QAC9D,WAAW,EAAE,OAAO,CAAC,WAAW,IAAI,oBAAoB;KACzD,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IAAI,CAAC,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;QAChD,MAAM,IAAI,mBAAmB,CAAC,kBAAkB,EAAE,oBAAoB,CAAC,CAAC;IAC1E,CAAC;IACD,MAAM,uBAAuB,CAAC,kBAAkB,EAAE,CAAC;AACrD,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAQxD,MAAM,qCAAqC,GAAG,yBAAyB,CAAC;AAgBxE,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CACxD,qCAAqC,CACtC,CAAC"}
1
+ {"version":3,"file":"native-module.js","sourceRoot":"","sources":["../../src/core/native-module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAQxD,MAAM,qCAAqC,GAAG,yBAAyB,CAAC;AAmBxE,MAAM,CAAC,MAAM,uBAAuB,GAClC,mBAAmB,CACjB,qCAAqC,CACtC,CAAC"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@symbiote-native/local-auth",
3
- "version": "0.3.0",
4
- "description": "expo-local-authentication wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, and Angular adapters. FaceID/TouchID on iOS, the Fingerprint/Biometric API on Android.",
3
+ "version": "1.0.0",
4
+ "description": "expo-local-authentication wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. FaceID/TouchID on iOS, the Fingerprint/Biometric API on Android.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",
@@ -13,6 +13,18 @@
13
13
  "url": "https://github.com/OneEyed1366/symbiote-native/issues"
14
14
  },
15
15
  "author": "Andrey Prokopenko <psevdoproger@gmail.com>",
16
+ "keywords": [
17
+ "react-native",
18
+ "symbiote-native",
19
+ "expo-local-authentication",
20
+ "react",
21
+ "vue",
22
+ "svelte",
23
+ "solid",
24
+ "angular",
25
+ "biometrics",
26
+ "face-id"
27
+ ],
16
28
  "type": "module",
17
29
  "main": "./build/core/index.js",
18
30
  "module": "./build/core/index.js",
@@ -23,16 +35,20 @@
23
35
  "default": "./build/core/index.js"
24
36
  },
25
37
  "./vue": {
26
- "types": "./build/vue/index.d.ts",
27
- "default": "./build/vue/index.js"
38
+ "types": "./build/core/index.d.ts",
39
+ "default": "./build/core/index.js"
28
40
  },
29
41
  "./react": {
30
- "types": "./build/react/index.d.ts",
31
- "default": "./build/react/index.js"
42
+ "types": "./build/core/index.d.ts",
43
+ "default": "./build/core/index.js"
32
44
  },
33
45
  "./svelte": {
34
- "types": "./build/svelte/index.d.ts",
35
- "default": "./build/svelte/index.js"
46
+ "types": "./build/core/index.d.ts",
47
+ "default": "./build/core/index.js"
48
+ },
49
+ "./solid": {
50
+ "types": "./build/core/index.d.ts",
51
+ "default": "./build/core/index.js"
36
52
  },
37
53
  "./angular": {
38
54
  "types": "./build-ngc/angular/index.d.ts",
@@ -58,17 +74,19 @@
58
74
  "invariant": "^2.2.4"
59
75
  },
60
76
  "peerDependencies": {
61
- "@symbiote-native/angular": ">=0.6.2",
62
- "@symbiote-native/engine": ">=0.1.7",
63
- "@symbiote-native/react": ">=0.2.8",
64
- "@symbiote-native/svelte": ">=0.1.0",
65
- "@symbiote-native/vue": ">=0.3.8",
66
77
  "@angular/core": ">=20",
67
78
  "@vue/runtime-core": "^3.5.13",
68
79
  "react": ">=19.0.0",
69
80
  "react-native": ">=0.86",
81
+ "solid-js": ">=1.9.0",
70
82
  "svelte": ">=5.56.0",
71
- "vue": ">=3.5.0"
83
+ "vue": ">=3.5.0",
84
+ "@symbiote-native/angular": "^1.0.0",
85
+ "@symbiote-native/engine": "^0.4.0",
86
+ "@symbiote-native/react": "^1.0.0",
87
+ "@symbiote-native/solid": "^1.0.0",
88
+ "@symbiote-native/svelte": "^1.0.0",
89
+ "@symbiote-native/vue": "^1.0.0"
72
90
  },
73
91
  "peerDependenciesMeta": {
74
92
  "@symbiote-native/angular": {
@@ -77,6 +95,9 @@
77
95
  "@symbiote-native/react": {
78
96
  "optional": true
79
97
  },
98
+ "@symbiote-native/solid": {
99
+ "optional": true
100
+ },
80
101
  "@symbiote-native/svelte": {
81
102
  "optional": true
82
103
  },
@@ -92,6 +113,9 @@
92
113
  "react": {
93
114
  "optional": true
94
115
  },
116
+ "solid-js": {
117
+ "optional": true
118
+ },
95
119
  "svelte": {
96
120
  "optional": true
97
121
  },
@@ -108,14 +132,16 @@
108
132
  "@types/react": "^19.2.0",
109
133
  "@vue/runtime-core": "^3.5.13",
110
134
  "react": "19.2.3",
135
+ "solid-js": "^1.9.14",
111
136
  "svelte": "^5.56.0",
112
137
  "typescript": "~6.0.0",
113
- "@symbiote-native/angular": "0.7.0",
114
- "@symbiote-native/engine": "0.2.0",
115
- "@symbiote-native/react": "0.3.0",
116
- "@symbiote-native/svelte": "0.2.0",
117
- "@symbiote-native/test-utils": "0.1.6",
118
- "@symbiote-native/vue": "0.4.0"
138
+ "@symbiote-native/angular": "1.0.0",
139
+ "@symbiote-native/engine": "0.4.0",
140
+ "@symbiote-native/react": "1.0.0",
141
+ "@symbiote-native/solid": "1.0.0",
142
+ "@symbiote-native/svelte": "1.0.0",
143
+ "@symbiote-native/test-utils": "0.3.0",
144
+ "@symbiote-native/vue": "1.0.0"
119
145
  },
120
146
  "scripts": {
121
147
  "typecheck": "tsc --build",
@@ -28,9 +28,14 @@ export async function hasHardwareAsync(): Promise<boolean> {
28
28
  * multiple authentication methods — e.g. `[FINGERPRINT, FACIAL_RECOGNITION]` means the device
29
29
  * supports both. Returns an empty array if none are supported.
30
30
  */
31
- export async function supportedAuthenticationTypesAsync(): Promise<AuthenticationType[]> {
31
+ export async function supportedAuthenticationTypesAsync(): Promise<
32
+ AuthenticationType[]
33
+ > {
32
34
  if (!expoLocalAuthentication.supportedAuthenticationTypesAsync) {
33
- throw new UnavailabilityError(NATIVE_MODULE_NAME, 'supportedAuthenticationTypesAsync');
35
+ throw new UnavailabilityError(
36
+ NATIVE_MODULE_NAME,
37
+ 'supportedAuthenticationTypesAsync',
38
+ );
34
39
  }
35
40
  return expoLocalAuthentication.supportedAuthenticationTypesAsync();
36
41
  }
@@ -72,7 +77,8 @@ export async function authenticateAsync(
72
77
 
73
78
  if (options.promptMessage !== undefined) {
74
79
  invariant(
75
- typeof options.promptMessage === 'string' && options.promptMessage.length > 0,
80
+ typeof options.promptMessage === 'string' &&
81
+ options.promptMessage.length > 0,
76
82
  'LocalAuthentication.authenticateAsync: `options.promptMessage` must be a non-empty string.',
77
83
  );
78
84
  }
@@ -17,11 +17,15 @@ export type INativeLocalAuthenticationModule = {
17
17
  isEnrolledAsync?(): Promise<boolean>;
18
18
  getEnrolledLevelAsync?(): Promise<SecurityLevel>;
19
19
  authenticateAsync?(
20
- options: ILocalAuthenticationOptions & { promptMessage: string; cancelLabel: string },
20
+ options: ILocalAuthenticationOptions & {
21
+ promptMessage: string;
22
+ cancelLabel: string;
23
+ },
21
24
  ): Promise<ILocalAuthenticationResult>;
22
25
  cancelAuthenticate?(): Promise<void>;
23
26
  };
24
27
 
25
- export const expoLocalAuthentication = requireNativeModule<INativeLocalAuthenticationModule>(
26
- EXPO_LOCAL_AUTHENTICATION_MODULE_NAME,
27
- );
28
+ export const expoLocalAuthentication =
29
+ requireNativeModule<INativeLocalAuthenticationModule>(
30
+ EXPO_LOCAL_AUTHENTICATION_MODULE_NAME,
31
+ );
@@ -1 +0,0 @@
1
- export * from '../core';
@@ -1,6 +0,0 @@
1
- // @symbiote-native/local-auth/react: the React entry over the framework-agnostic core.
2
- // Upstream ships free async functions and two enums, no per-instance state and no event
3
- // stream (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a hook
4
- // to wrap, so this is a plain re-export, mirroring how Pedometer's free functions pass through
5
- // packages/sensors/src/react/index.ts untouched.
6
- export * from '../core/index.js';
@@ -1 +0,0 @@
1
- export * from '../core';
@@ -1,6 +0,0 @@
1
- // @symbiote-native/local-auth/svelte: the Svelte entry over the framework-agnostic core.
2
- // Upstream ships free async functions and two enums, no per-instance state and no event stream
3
- // (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a rune to wrap,
4
- // so this is a plain re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes
5
- // — never React's `hooks` or Vue's `composables`), and this package has none to fill.
6
- export * from '../core/index.js';
@@ -1 +0,0 @@
1
- export * from '../core';
@@ -1,4 +0,0 @@
1
- // @symbiote-native/local-auth/vue: the Vue entry over the framework-agnostic core. Same
2
- // reasoning as the React entry — no per-instance state or event stream to wire onto Vue's
3
- // reactivity, so this is a plain re-export.
4
- export * from '../core/index.js';
@@ -1,6 +0,0 @@
1
- // @symbiote-native/local-auth/react: the React entry over the framework-agnostic core.
2
- // Upstream ships free async functions and two enums, no per-instance state and no event
3
- // stream (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a hook
4
- // to wrap, so this is a plain re-export, mirroring how Pedometer's free functions pass through
5
- // packages/sensors/src/react/index.ts untouched.
6
- export * from '../core';
@@ -1,6 +0,0 @@
1
- // @symbiote-native/local-auth/svelte: the Svelte entry over the framework-agnostic core.
2
- // Upstream ships free async functions and two enums, no per-instance state and no event stream
3
- // (unlike the sensor family in @symbiote-native/sensors) — there is nothing for a rune to wrap,
4
- // so this is a plain re-export. Svelte's lifecycle bucket is `runes/` (adapters/svelte/src/runes
5
- // — never React's `hooks` or Vue's `composables`), and this package has none to fill.
6
- export * from '../core';
package/src/vue/index.ts DELETED
@@ -1,4 +0,0 @@
1
- // @symbiote-native/local-auth/vue: the Vue entry over the framework-agnostic core. Same
2
- // reasoning as the React entry — no per-instance state or event stream to wire onto Vue's
3
- // reactivity, so this is a plain re-export.
4
- export * from '../core';