@symbiote-native/local-auth 0.4.0 → 2.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.
Files changed (2) hide show
  1. package/README.md +167 -58
  2. package/package.json +27 -15
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
 
@@ -55,16 +55,17 @@ src/core/ hasHardwareAsync / isEnrolledAsync / getEnrolledLevelAsync /
55
55
  src/angular/ @symbiote-native/local-auth/angular — export * from '../core'
56
56
  ```
57
57
 
58
- `./react`, `./vue`, and `./svelte` are `exports`-map aliases straight onto `src/core/` — no
59
- physical per-framework file, since there's nothing to subscribe to or clean up. `./angular` stays
60
- a physical file/subpath since Angular ships through a separate `ngc`/AOT build (`build-ngc/`).
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/`).
61
62
 
62
63
  ## Use it
63
64
 
64
65
  ```tsx
65
66
  // React
66
67
  import { useEffect, useState } from 'react';
67
- import { Platform, Pressable, Text, View } from '@symbiote-native/react';
68
+ import { Platform } from '@symbiote-native/react';
68
69
  import {
69
70
  authenticateAsync,
70
71
  cancelAuthenticate,
@@ -91,26 +92,26 @@ function LocalAuthScreen() {
91
92
  };
92
93
 
93
94
  return (
94
- <View>
95
- <Text>
95
+ <view>
96
+ <text>
96
97
  {hasHardware && isEnrolled
97
98
  ? 'Ready to authenticate'
98
99
  : 'No biometrics enrolled'}
99
- </Text>
100
- <Pressable onPress={handleAuthenticate}>
101
- <Text>Authenticate</Text>
102
- </Pressable>
100
+ </text>
101
+ <pressable onPress={handleAuthenticate}>
102
+ <text>Authenticate</text>
103
+ </pressable>
103
104
  {Platform.OS === 'android' && (
104
- <Pressable onPress={() => cancelAuthenticate()}>
105
- <Text>Cancel</Text>
106
- </Pressable>
105
+ <pressable onPress={() => cancelAuthenticate()}>
106
+ <text>Cancel</text>
107
+ </pressable>
107
108
  )}
108
109
  {authResult && (
109
- <Text>
110
+ <text>
110
111
  {authResult.success ? 'Success' : `Failed: ${authResult.error}`}
111
- </Text>
112
+ </text>
112
113
  )}
113
- </View>
114
+ </view>
114
115
  );
115
116
  }
116
117
  ```
@@ -119,7 +120,7 @@ function LocalAuthScreen() {
119
120
  <!-- Vue -->
120
121
  <script setup lang="ts">
121
122
  import { onMounted, ref } from 'vue';
122
- import { Platform, Pressable, Text, View } from '@symbiote-native/vue';
123
+ import { Platform } from '@symbiote-native/vue';
123
124
  import {
124
125
  authenticateAsync,
125
126
  cancelAuthenticate,
@@ -145,29 +146,132 @@ function handleAuthenticate(): void {
145
146
  </script>
146
147
 
147
148
  <template>
148
- <View>
149
- <Text>{{
149
+ <view>
150
+ <text>{{
150
151
  hasHardware && isEnrolled
151
152
  ? 'Ready to authenticate'
152
153
  : 'No biometrics enrolled'
153
- }}</Text>
154
- <Pressable @press="handleAuthenticate">
155
- <Text>Authenticate</Text>
156
- </Pressable>
157
- <Pressable v-if="Platform.OS === 'android'" @press="cancelAuthenticate">
158
- <Text>Cancel</Text>
159
- </Pressable>
160
- <Text v-if="authResult">{{
154
+ }}</text>
155
+ <pressable @press="handleAuthenticate">
156
+ <text>Authenticate</text>
157
+ </pressable>
158
+ <pressable v-if="Platform.OS === 'android'" @press="cancelAuthenticate">
159
+ <text>Cancel</text>
160
+ </pressable>
161
+ <text v-if="authResult">{{
161
162
  authResult.success ? 'Success' : `Failed: ${authResult.error}`
162
- }}</Text>
163
- </View>
163
+ }}</text>
164
+ </view>
164
165
  </template>
165
166
  ```
166
167
 
168
+ ```svelte
169
+ <!-- Svelte -->
170
+ <script lang="ts">
171
+ import { Platform } 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
199
+ ? 'Ready to authenticate'
200
+ : 'No biometrics enrolled'}
201
+ </text>
202
+ <pressable onPress={handleAuthenticate}>
203
+ <text>Authenticate</text>
204
+ </pressable>
205
+ {#if Platform.OS === 'android'}
206
+ <pressable onPress={() => cancelAuthenticate()}>
207
+ <text>Cancel</text>
208
+ </pressable>
209
+ {/if}
210
+ {#if authResult}
211
+ <text>{authResult.success ? 'Success' : `Failed: ${authResult.error}`}</text
212
+ >
213
+ {/if}
214
+ </view>
215
+ ```
216
+
217
+ ```tsx
218
+ // Solid
219
+ import { Show, createSignal, onMount } from 'solid-js';
220
+ import { Platform } from '@symbiote-native/solid';
221
+ import {
222
+ authenticateAsync,
223
+ cancelAuthenticate,
224
+ hasHardwareAsync,
225
+ isEnrolledAsync,
226
+ } from '@symbiote-native/local-auth/solid';
227
+ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/solid';
228
+
229
+ function LocalAuthScreen() {
230
+ const [hasHardware, setHasHardware] = createSignal(false);
231
+ const [isEnrolled, setIsEnrolled] = createSignal(false);
232
+ const [authResult, setAuthResult] =
233
+ createSignal<ILocalAuthenticationResult | null>(null);
234
+
235
+ onMount(() => {
236
+ void hasHardwareAsync().then(setHasHardware);
237
+ void isEnrolledAsync().then(setIsEnrolled);
238
+ });
239
+
240
+ const handleAuthenticate = () => {
241
+ void authenticateAsync({ promptMessage: 'Confirm it is you' }).then(
242
+ setAuthResult,
243
+ );
244
+ };
245
+
246
+ return (
247
+ <view>
248
+ <text>
249
+ {hasHardware() && isEnrolled()
250
+ ? 'Ready to authenticate'
251
+ : 'No biometrics enrolled'}
252
+ </text>
253
+ <pressable onPress={handleAuthenticate}>
254
+ <text>Authenticate</text>
255
+ </pressable>
256
+ {Platform.OS === 'android' && (
257
+ <pressable onPress={() => cancelAuthenticate()}>
258
+ <text>Cancel</text>
259
+ </pressable>
260
+ )}
261
+ <Show when={authResult()} keyed>
262
+ {result => (
263
+ <text>{result.success ? 'Success' : `Failed: ${result.error}`}</text>
264
+ )}
265
+ </Show>
266
+ </view>
267
+ );
268
+ }
269
+ ```
270
+
167
271
  ```ts
168
272
  // Angular
169
273
  import { Component, signal } from '@angular/core';
170
- import { Platform, Pressable, Text, View } from '@symbiote-native/angular';
274
+ import { Platform, SYMBIOTE_ELEMENTS } from '@symbiote-native/angular';
171
275
  import {
172
276
  authenticateAsync,
173
277
  cancelAuthenticate,
@@ -178,28 +282,28 @@ import type { ILocalAuthenticationResult } from '@symbiote-native/local-auth/ang
178
282
 
179
283
  @Component({
180
284
  standalone: true,
181
- imports: [Pressable, Text, View],
285
+ imports: [SYMBIOTE_ELEMENTS],
182
286
  template: `
183
- <View>
184
- <Text>{{
287
+ <view>
288
+ <text>{{
185
289
  hasHardware() && isEnrolled()
186
290
  ? 'Ready to authenticate'
187
291
  : 'No biometrics enrolled'
188
- }}</Text>
189
- <Pressable (press)="handleAuthenticate()">
190
- <Text>Authenticate</Text>
191
- </Pressable>
292
+ }}</text>
293
+ <pressable (press)="handleAuthenticate()">
294
+ <text>Authenticate</text>
295
+ </pressable>
192
296
  @if (Platform.OS === 'android') {
193
- <Pressable (press)="handleCancel()">
194
- <Text>Cancel</Text>
195
- </Pressable>
297
+ <pressable (press)="handleCancel()">
298
+ <text>Cancel</text>
299
+ </pressable>
196
300
  }
197
301
  @if (authResult(); as result) {
198
- <Text>{{
302
+ <text>{{
199
303
  result.success ? 'Success' : 'Failed: ' + result.error
200
- }}</Text>
304
+ }}</text>
201
305
  }
202
- </View>
306
+ </view>
203
307
  `,
204
308
  })
205
309
  export class LocalAuthScreen {
@@ -226,17 +330,19 @@ export class LocalAuthScreen {
226
330
  ```
227
331
 
228
332
  There's no per-instance service to `inject()` in the Angular case — every function is a plain
229
- free function off the core package, called straight from the constructor. All three examples are
230
- trimmed from the real canary demo screens (`examples/expo-react/screens/LocalAuthScreen.tsx`,
333
+ free function off the core package, called straight from the constructor. The React/Vue/Angular
334
+ examples are trimmed from the real canary demo screens
335
+ (`examples/expo-react/screens/LocalAuthScreen.tsx`,
231
336
  `examples/expo-vue-sfc/screens/LocalAuthScreen.vue`, `examples/expo-vue-tsx/screens/LocalAuthScreen.tsx`,
232
337
  `examples/expo-angular/src/screens/LocalAuthScreen.ts`), which also cover
233
- `getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card.
338
+ `getEnrolledLevelAsync`/`supportedAuthenticationTypesAsync` and render a capabilities card. No
339
+ canary demo screen exists yet for Svelte/Solid — the two snippets above follow the same surface.
234
340
 
235
341
  ## API
236
342
 
237
343
  Free functions, no event stream, no per-instance state — upstream ships a handful of async
238
- functions and two enums, not a subscribable sensor, so the React/Vue/Angular entry points above
239
- are plain re-exports of `core` with nothing adapter-specific to add.
344
+ functions and two enums, not a subscribable sensor, so the React/Vue/Svelte/Solid/Angular entry
345
+ points above are plain re-exports of `core` with nothing adapter-specific to add.
240
346
 
241
347
  ```ts
242
348
  hasHardwareAsync(): Promise<boolean>
@@ -260,6 +366,8 @@ import {
260
366
  // or the framework-scoped entry points — identical surface, re-exported verbatim:
261
367
  import { authenticateAsync } from '@symbiote-native/local-auth/react';
262
368
  import { authenticateAsync } from '@symbiote-native/local-auth/vue';
369
+ import { authenticateAsync } from '@symbiote-native/local-auth/svelte';
370
+ import { authenticateAsync } from '@symbiote-native/local-auth/solid';
263
371
  import { authenticateAsync } from '@symbiote-native/local-auth/angular';
264
372
  ```
265
373
 
@@ -286,10 +394,11 @@ a view or per-instance state. Tests inject a fake native-module object in place
286
394
  `src/core/types.test.ts`, `vitest`) — no `installFabric()`, no ViewConfig. Native rendering itself
287
395
  is verified on-device (see the parent [README](../../README.md) for the project's testing model).
288
396
 
289
- Native autolinking wiring is done in the four Expo canary apps
290
- (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-angular`)
291
- — iOS Podfile/`AppDelegate.swift` + `NSFaceIDUsageDescription`, Android Gradle/
292
- `MainApplication.kt` + `USE_BIOMETRIC`, all four confirmed present. The one remaining gap: this
293
- package isn't yet demoed in the plain, non-Expo `examples/react`/`vue-sfc`/`vue-tsx`/`angular`
294
- canaries, since an `expo-modules-core` package needs the `expo-modules-autolinking` wiring only
295
- the `examples/expo-*` apps have set up so far.
397
+ Native autolinking wiring is done in all six Expo canary apps
398
+ (`examples/expo-react`, `examples/expo-vue-sfc`, `examples/expo-vue-tsx`, `examples/expo-svelte`,
399
+ `examples/expo-solid`, `examples/expo-angular`) — iOS Podfile/`AppDelegate.swift` +
400
+ `NSFaceIDUsageDescription`, Android Gradle/`MainApplication.kt` + `USE_BIOMETRIC`, all six
401
+ confirmed present. The one remaining gap: this package isn't yet demoed in the plain, non-Expo
402
+ `examples/react`/`vue-sfc`/`vue-tsx`/`svelte`/`solid`/`angular` canaries, since an
403
+ `expo-modules-core` package needs the `expo-modules-autolinking` wiring only the
404
+ `examples/expo-*` apps have set up so far.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@symbiote-native/local-auth",
3
- "version": "0.4.0",
3
+ "version": "2.0.0",
4
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": {
@@ -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",
@@ -62,19 +74,19 @@
62
74
  "invariant": "^2.2.4"
63
75
  },
64
76
  "peerDependencies": {
65
- "@symbiote-native/angular": ">=0.6.2",
66
- "@symbiote-native/engine": ">=0.1.7",
67
- "@symbiote-native/react": ">=0.2.8",
68
- "@symbiote-native/solid": ">=0.0.1",
69
- "@symbiote-native/svelte": ">=0.1.0",
70
- "@symbiote-native/vue": ">=0.3.8",
71
77
  "@angular/core": ">=20",
72
78
  "@vue/runtime-core": "^3.5.13",
73
79
  "react": ">=19.0.0",
74
80
  "react-native": ">=0.86",
75
81
  "solid-js": ">=1.9.0",
76
82
  "svelte": ">=5.56.0",
77
- "vue": ">=3.5.0"
83
+ "vue": ">=3.5.0",
84
+ "@symbiote-native/angular": "^2.0.0",
85
+ "@symbiote-native/engine": "^0.5.0",
86
+ "@symbiote-native/react": "^2.0.0",
87
+ "@symbiote-native/solid": "^2.0.0",
88
+ "@symbiote-native/svelte": "^2.0.0",
89
+ "@symbiote-native/vue": "^2.0.0"
78
90
  },
79
91
  "peerDependenciesMeta": {
80
92
  "@symbiote-native/angular": {
@@ -123,13 +135,13 @@
123
135
  "solid-js": "^1.9.14",
124
136
  "svelte": "^5.56.0",
125
137
  "typescript": "~6.0.0",
126
- "@symbiote-native/angular": "0.8.0",
127
- "@symbiote-native/engine": "0.3.0",
128
- "@symbiote-native/react": "0.4.0",
129
- "@symbiote-native/solid": "0.1.0",
130
- "@symbiote-native/svelte": "0.3.0",
131
- "@symbiote-native/test-utils": "0.2.0",
132
- "@symbiote-native/vue": "0.5.0"
138
+ "@symbiote-native/angular": "2.0.0",
139
+ "@symbiote-native/engine": "0.5.0",
140
+ "@symbiote-native/react": "2.0.0",
141
+ "@symbiote-native/solid": "2.0.0",
142
+ "@symbiote-native/svelte": "2.0.0",
143
+ "@symbiote-native/test-utils": "0.3.0",
144
+ "@symbiote-native/vue": "2.0.0"
133
145
  },
134
146
  "scripts": {
135
147
  "typecheck": "tsc --build",