@atlasauth/expo-passkeys 0.1.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 +68 -0
- package/dist/client.d.ts +20 -0
- package/dist/client.js +21 -0
- package/dist/client.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/passkey-native.d.ts +40 -0
- package/dist/passkey-native.js +18 -0
- package/dist/passkey-native.js.map +1 -0
- package/package.json +37 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @atlasauth/expo-passkeys
|
|
2
|
+
|
|
3
|
+
Passkey registration and sign-in for Expo & React Native, a thin companion to
|
|
4
|
+
[`@atlasauth/expo`](../expo).
|
|
5
|
+
|
|
6
|
+
The server describes the WebAuthn ceremony (`begin`), the platform authenticator
|
|
7
|
+
performs it, and the result is POSTed back (`finish`). On the web the browser
|
|
8
|
+
runs the ceremony with `navigator.credentials`; on native there is no
|
|
9
|
+
`navigator`, so this package delegates the ceremony to
|
|
10
|
+
[`react-native-passkey`](https://github.com/f-23/react-native-passkey) — an
|
|
11
|
+
**optional peer you pass in** — and owns only the two HTTP calls and the body
|
|
12
|
+
mapping between them.
|
|
13
|
+
|
|
14
|
+
## Native dependency
|
|
15
|
+
|
|
16
|
+
```sh
|
|
17
|
+
npx expo install react-native-passkey
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`react-native-passkey` is a native module: it needs a config plugin and a
|
|
21
|
+
**custom dev client / prebuild** (it does not run in Expo Go), plus an associated
|
|
22
|
+
domain (iOS `webcredentials:`) / Digital Asset Links (Android) for your RP id.
|
|
23
|
+
This package never imports it, so the import stays optional and your build keeps
|
|
24
|
+
compiling without it.
|
|
25
|
+
|
|
26
|
+
## Register (signed-in user)
|
|
27
|
+
|
|
28
|
+
```ts
|
|
29
|
+
import { Passkey } from 'react-native-passkey';
|
|
30
|
+
import { registerPasskey } from '@atlasauth/expo-passkeys';
|
|
31
|
+
import { useAuth } from '@atlasauth/expo';
|
|
32
|
+
|
|
33
|
+
const { getToken } = useAuth();
|
|
34
|
+
|
|
35
|
+
await registerPasskey({
|
|
36
|
+
config: {
|
|
37
|
+
frontendApi: 'https://fapi.your-instance.atlasauth.net',
|
|
38
|
+
publishableKey: 'pk_live_…',
|
|
39
|
+
getToken, // the /me/passkeys route is authenticated
|
|
40
|
+
},
|
|
41
|
+
passkey: Passkey,
|
|
42
|
+
name: 'My iPhone',
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Sign in
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Passkey } from 'react-native-passkey';
|
|
50
|
+
import { authenticateWithPasskey } from '@atlasauth/expo-passkeys';
|
|
51
|
+
|
|
52
|
+
const result = await authenticateWithPasskey({
|
|
53
|
+
config: {
|
|
54
|
+
frontendApi: 'https://fapi.your-instance.atlasauth.net',
|
|
55
|
+
publishableKey: 'pk_live_…',
|
|
56
|
+
},
|
|
57
|
+
passkey: Passkey,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (result.status === 'complete' && result.ticket) {
|
|
61
|
+
// hand result.ticket to @atlasauth/expo to exchange for a session
|
|
62
|
+
} else if (result.status === 'needs_more') {
|
|
63
|
+
// a second factor is still owed — result.attemptId / result.attemptStatus
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`toRegistrationFinishBody` / `toAssertionFinishBody` are exported as pure helpers
|
|
68
|
+
if you want to drive the two HTTP calls yourself.
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { FapiClient } from '@atlasauth/js';
|
|
2
|
+
/**
|
|
3
|
+
* How a helper reaches an Atlas instance. The begin/finish FAPI calls run from
|
|
4
|
+
* the device, so — unlike the web, where a cookie carries the session — the
|
|
5
|
+
* app's session token (for the signed-in `me` routes) rides in an
|
|
6
|
+
* `Authorization: Bearer` header. `getToken` is optional: a sign-in ceremony has
|
|
7
|
+
* no session yet, a register ceremony does.
|
|
8
|
+
*/
|
|
9
|
+
export interface AtlasFapiConfig {
|
|
10
|
+
/** The instance's FAPI origin, e.g. `https://fapi.acme.atlasauth.net`. */
|
|
11
|
+
frontendApi: string;
|
|
12
|
+
/** The publishable key — sent as `x-publishable-key` on every request. */
|
|
13
|
+
publishableKey: string;
|
|
14
|
+
/** The current session token, for authenticated (`/me/*`) routes. */
|
|
15
|
+
getToken?: () => string | null | Promise<string | null>;
|
|
16
|
+
/** Injectable for tests; defaults to the platform `fetch`. */
|
|
17
|
+
fetchImpl?: typeof fetch;
|
|
18
|
+
}
|
|
19
|
+
/** Build a bearer-authenticated `@atlasauth/js` FapiClient from the config. */
|
|
20
|
+
export declare function createFapiClient(config: AtlasFapiConfig): FapiClient;
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFapiClient = createFapiClient;
|
|
4
|
+
const js_1 = require("@atlasauth/js");
|
|
5
|
+
/** Build a bearer-authenticated `@atlasauth/js` FapiClient from the config. */
|
|
6
|
+
function createFapiClient(config) {
|
|
7
|
+
const base = config.fetchImpl ?? fetch;
|
|
8
|
+
const fetchImpl = async (input, init) => {
|
|
9
|
+
const headers = new Headers(init?.headers);
|
|
10
|
+
const token = config.getToken ? await config.getToken() : null;
|
|
11
|
+
if (token)
|
|
12
|
+
headers.set('authorization', `Bearer ${token}`);
|
|
13
|
+
return base(input, { ...init, headers });
|
|
14
|
+
};
|
|
15
|
+
return new js_1.FapiClient({
|
|
16
|
+
publishableKey: config.publishableKey,
|
|
17
|
+
baseUrl: config.frontendApi,
|
|
18
|
+
fetchImpl,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":";;AAqBA,4CAaC;AAlCD,sCAA2C;AAoB3C,+EAA+E;AAC/E,SAAgB,gBAAgB,CAAC,MAAuB;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,IAAI,KAAK,CAAC;IACvC,MAAM,SAAS,GAAiB,KAAK,EAAE,KAAwB,EAAE,IAAkB,EAAE,EAAE;QACrF,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAC/D,IAAI,KAAK;YAAE,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,KAAK,EAAE,CAAC,CAAC;QAC3D,OAAO,IAAI,CAAC,KAAK,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3C,CAAC,CAAC;IACF,OAAO,IAAI,eAAU,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,OAAO,EAAE,MAAM,CAAC,WAAW;QAC3B,SAAS;KACV,CAAC,CAAC;AACL,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @atlasauth/expo-passkeys — passkey register / sign-in for Expo & React Native.
|
|
3
|
+
*
|
|
4
|
+
* Passkeys on native are a two-call dance that mirrors the web (see
|
|
5
|
+
* `@atlasauth/js`'s `createPasskey` / `getPasskeyAssertion`): the server's
|
|
6
|
+
* `begin` response describes the ceremony, the platform authenticator performs
|
|
7
|
+
* it, and the result is POSTed to `finish`. The browser runs the ceremony with
|
|
8
|
+
* `navigator.credentials`; on native there is no `navigator`, so this helper
|
|
9
|
+
* delegates the ceremony to `react-native-passkey` (an optional peer, passed in)
|
|
10
|
+
* and owns only the two HTTP calls and the body mapping between them.
|
|
11
|
+
*
|
|
12
|
+
* register: POST /v1/client/me/passkeys/begin → Passkey.create → /finish
|
|
13
|
+
* sign in : POST /v1/client/sign_ins/passkey/begin → Passkey.get → /finish
|
|
14
|
+
*/
|
|
15
|
+
import type { FieldError, PasskeyAssertionBody, PasskeyRegistrationBody } from '@atlasauth/js';
|
|
16
|
+
import { type AtlasFapiConfig } from './client';
|
|
17
|
+
import type { PasskeyCreateResult, PasskeyGetResult, ReactNativePasskey } from './passkey-native';
|
|
18
|
+
export * from './client';
|
|
19
|
+
export * from './passkey-native';
|
|
20
|
+
/** Map a `begin` response + a `Passkey.create` result to the `/finish` body. */
|
|
21
|
+
export declare function toRegistrationFinishBody(begin: Record<string, unknown>, credential: PasskeyCreateResult, name?: string): PasskeyRegistrationBody;
|
|
22
|
+
/** Map a `begin` response + a `Passkey.get` result to the `/finish` body. */
|
|
23
|
+
export declare function toAssertionFinishBody(begin: Record<string, unknown>, assertion: PasskeyGetResult): PasskeyAssertionBody;
|
|
24
|
+
export interface RegisterPasskeyResult {
|
|
25
|
+
ok: boolean;
|
|
26
|
+
errors: FieldError[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Register a passkey for the signed-in user. Requires `config.getToken` (the
|
|
30
|
+
* `/me/*` route is authenticated). Runs begin → platform ceremony → finish.
|
|
31
|
+
*/
|
|
32
|
+
export declare function registerPasskey(opts: {
|
|
33
|
+
config: AtlasFapiConfig;
|
|
34
|
+
passkey: ReactNativePasskey;
|
|
35
|
+
/** A human label for the credential (shown in the user's device list). */
|
|
36
|
+
name?: string;
|
|
37
|
+
}): Promise<RegisterPasskeyResult>;
|
|
38
|
+
export interface PasskeySignInResult {
|
|
39
|
+
status: 'complete' | 'needs_more' | 'error';
|
|
40
|
+
/** Present when complete — exchange it for a session token. */
|
|
41
|
+
ticket?: string;
|
|
42
|
+
attemptId?: string;
|
|
43
|
+
attemptStatus?: string;
|
|
44
|
+
errors: FieldError[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sign in with a passkey. Runs begin → platform ceremony → finish and returns
|
|
48
|
+
* the attempt outcome: `complete` carries a one-time `ticket` to exchange for a
|
|
49
|
+
* session (hand it to `@atlasauth/expo`), `needs_more` means a factor is still
|
|
50
|
+
* owed.
|
|
51
|
+
*/
|
|
52
|
+
export declare function authenticateWithPasskey(opts: {
|
|
53
|
+
config: AtlasFapiConfig;
|
|
54
|
+
passkey: ReactNativePasskey;
|
|
55
|
+
}): Promise<PasskeySignInResult>;
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @atlasauth/expo-passkeys — passkey register / sign-in for Expo & React Native.
|
|
4
|
+
*
|
|
5
|
+
* Passkeys on native are a two-call dance that mirrors the web (see
|
|
6
|
+
* `@atlasauth/js`'s `createPasskey` / `getPasskeyAssertion`): the server's
|
|
7
|
+
* `begin` response describes the ceremony, the platform authenticator performs
|
|
8
|
+
* it, and the result is POSTed to `finish`. The browser runs the ceremony with
|
|
9
|
+
* `navigator.credentials`; on native there is no `navigator`, so this helper
|
|
10
|
+
* delegates the ceremony to `react-native-passkey` (an optional peer, passed in)
|
|
11
|
+
* and owns only the two HTTP calls and the body mapping between them.
|
|
12
|
+
*
|
|
13
|
+
* register: POST /v1/client/me/passkeys/begin → Passkey.create → /finish
|
|
14
|
+
* sign in : POST /v1/client/sign_ins/passkey/begin → Passkey.get → /finish
|
|
15
|
+
*/
|
|
16
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
19
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
20
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
21
|
+
}
|
|
22
|
+
Object.defineProperty(o, k2, desc);
|
|
23
|
+
}) : (function(o, m, k, k2) {
|
|
24
|
+
if (k2 === undefined) k2 = k;
|
|
25
|
+
o[k2] = m[k];
|
|
26
|
+
}));
|
|
27
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
28
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
29
|
+
};
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
exports.toRegistrationFinishBody = toRegistrationFinishBody;
|
|
32
|
+
exports.toAssertionFinishBody = toAssertionFinishBody;
|
|
33
|
+
exports.registerPasskey = registerPasskey;
|
|
34
|
+
exports.authenticateWithPasskey = authenticateWithPasskey;
|
|
35
|
+
const client_1 = require("./client");
|
|
36
|
+
__exportStar(require("./client"), exports);
|
|
37
|
+
__exportStar(require("./passkey-native"), exports);
|
|
38
|
+
/* ── pure body mapping (unit-tested without a device) ──────────────────────── */
|
|
39
|
+
/** Map a `begin` response + a `Passkey.create` result to the `/finish` body. */
|
|
40
|
+
function toRegistrationFinishBody(begin, credential, name) {
|
|
41
|
+
return {
|
|
42
|
+
// Echo the server's challenge string — the server matched/stored it.
|
|
43
|
+
challenge: begin.challenge,
|
|
44
|
+
attestation_object: credential.response.attestationObject,
|
|
45
|
+
client_data_json: credential.response.clientDataJSON,
|
|
46
|
+
...(name ? { name } : {}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Map a `begin` response + a `Passkey.get` result to the `/finish` body. */
|
|
50
|
+
function toAssertionFinishBody(begin, assertion) {
|
|
51
|
+
return {
|
|
52
|
+
handle: begin.handle,
|
|
53
|
+
challenge: begin.challenge,
|
|
54
|
+
credential_id: assertion.rawId ?? assertion.id ?? '',
|
|
55
|
+
authenticator_data: assertion.response.authenticatorData,
|
|
56
|
+
client_data_json: assertion.response.clientDataJSON,
|
|
57
|
+
signature: assertion.response.signature,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Register a passkey for the signed-in user. Requires `config.getToken` (the
|
|
62
|
+
* `/me/*` route is authenticated). Runs begin → platform ceremony → finish.
|
|
63
|
+
*/
|
|
64
|
+
async function registerPasskey(opts) {
|
|
65
|
+
const client = (0, client_1.createFapiClient)(opts.config);
|
|
66
|
+
const begin = await client.post('/v1/client/me/passkeys/begin', {});
|
|
67
|
+
if (!begin.ok || !begin.data)
|
|
68
|
+
return { ok: false, errors: begin.errors };
|
|
69
|
+
const credential = await opts.passkey.create(begin.data);
|
|
70
|
+
const body = toRegistrationFinishBody(begin.data, credential, opts.name);
|
|
71
|
+
const finish = await client.post('/v1/client/me/passkeys/finish', body);
|
|
72
|
+
return { ok: finish.ok, errors: finish.errors };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Sign in with a passkey. Runs begin → platform ceremony → finish and returns
|
|
76
|
+
* the attempt outcome: `complete` carries a one-time `ticket` to exchange for a
|
|
77
|
+
* session (hand it to `@atlasauth/expo`), `needs_more` means a factor is still
|
|
78
|
+
* owed.
|
|
79
|
+
*/
|
|
80
|
+
async function authenticateWithPasskey(opts) {
|
|
81
|
+
const client = (0, client_1.createFapiClient)(opts.config);
|
|
82
|
+
const begin = await client.post('/v1/client/sign_ins/passkey/begin', {});
|
|
83
|
+
if (!begin.ok || !begin.data)
|
|
84
|
+
return { status: 'error', errors: begin.errors };
|
|
85
|
+
const assertion = await opts.passkey.get(begin.data);
|
|
86
|
+
const body = toAssertionFinishBody(begin.data, assertion);
|
|
87
|
+
const finish = await client.post('/v1/client/sign_ins/passkey/finish', body);
|
|
88
|
+
if (!finish.ok || !finish.data)
|
|
89
|
+
return { status: 'error', errors: finish.errors };
|
|
90
|
+
return {
|
|
91
|
+
status: finish.data.ticket ? 'complete' : 'needs_more',
|
|
92
|
+
...(finish.data.ticket ? { ticket: finish.data.ticket } : {}),
|
|
93
|
+
...(finish.data.id ? { attemptId: finish.data.id } : {}),
|
|
94
|
+
...(finish.data.status ? { attemptStatus: finish.data.status } : {}),
|
|
95
|
+
errors: [],
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The slice of [`react-native-passkey`](https://github.com/f-23/react-native-passkey)
|
|
3
|
+
* this helper uses, declared structurally.
|
|
4
|
+
*
|
|
5
|
+
* `react-native-passkey` is an OPTIONAL peer that bridges to the platform
|
|
6
|
+
* authenticator (iOS ASAuthorization / Android Credential Manager). This package
|
|
7
|
+
* never imports it — the app passes the `Passkey` object in — so the package
|
|
8
|
+
* compiles and typechecks without the native module, and without a config plugin
|
|
9
|
+
* / prebuild in CI. The real `Passkey` is structurally compatible with the shapes
|
|
10
|
+
* below.
|
|
11
|
+
*
|
|
12
|
+
* `Passkey.create` / `Passkey.get` accept the server's WebAuthn options JSON
|
|
13
|
+
* verbatim (the `begin` response) and return the ceremony result with base64url
|
|
14
|
+
* string fields — exactly what the Atlas `finish` routes want.
|
|
15
|
+
*/
|
|
16
|
+
/** Result of `Passkey.create` — a WebAuthn attestation. */
|
|
17
|
+
export interface PasskeyCreateResult {
|
|
18
|
+
id?: string;
|
|
19
|
+
rawId?: string;
|
|
20
|
+
response: {
|
|
21
|
+
attestationObject: string;
|
|
22
|
+
clientDataJSON: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Result of `Passkey.get` — a WebAuthn assertion. */
|
|
26
|
+
export interface PasskeyGetResult {
|
|
27
|
+
id?: string;
|
|
28
|
+
rawId?: string;
|
|
29
|
+
response: {
|
|
30
|
+
authenticatorData: string;
|
|
31
|
+
clientDataJSON: string;
|
|
32
|
+
signature: string;
|
|
33
|
+
userHandle?: string | null;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** The `Passkey` object `react-native-passkey` exports. */
|
|
37
|
+
export interface ReactNativePasskey {
|
|
38
|
+
create(request: Record<string, unknown>): Promise<PasskeyCreateResult>;
|
|
39
|
+
get(request: Record<string, unknown>): Promise<PasskeyGetResult>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The slice of [`react-native-passkey`](https://github.com/f-23/react-native-passkey)
|
|
4
|
+
* this helper uses, declared structurally.
|
|
5
|
+
*
|
|
6
|
+
* `react-native-passkey` is an OPTIONAL peer that bridges to the platform
|
|
7
|
+
* authenticator (iOS ASAuthorization / Android Credential Manager). This package
|
|
8
|
+
* never imports it — the app passes the `Passkey` object in — so the package
|
|
9
|
+
* compiles and typechecks without the native module, and without a config plugin
|
|
10
|
+
* / prebuild in CI. The real `Passkey` is structurally compatible with the shapes
|
|
11
|
+
* below.
|
|
12
|
+
*
|
|
13
|
+
* `Passkey.create` / `Passkey.get` accept the server's WebAuthn options JSON
|
|
14
|
+
* verbatim (the `begin` response) and return the ceremony result with base64url
|
|
15
|
+
* string fields — exactly what the Atlas `finish` routes want.
|
|
16
|
+
*/
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
//# sourceMappingURL=passkey-native.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passkey-native.js","sourceRoot":"","sources":["../src/passkey-native.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG"}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atlasauth/expo-passkeys",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"main": "./dist/index.js",
|
|
5
|
+
"types": "./dist/index.d.ts",
|
|
6
|
+
"dependencies": {
|
|
7
|
+
"@atlasauth/js": "0.2.0"
|
|
8
|
+
},
|
|
9
|
+
"peerDependencies": {
|
|
10
|
+
"react-native": ">=0.73",
|
|
11
|
+
"react-native-passkey": ">=3"
|
|
12
|
+
},
|
|
13
|
+
"peerDependenciesMeta": {
|
|
14
|
+
"react-native": {
|
|
15
|
+
"optional": true
|
|
16
|
+
},
|
|
17
|
+
"react-native-passkey": {
|
|
18
|
+
"optional": true
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"devDependencies": {
|
|
22
|
+
"typescript": "^5.6.3",
|
|
23
|
+
"vitest": "^2.1.4"
|
|
24
|
+
},
|
|
25
|
+
"publishConfig": {
|
|
26
|
+
"access": "public"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"README.md"
|
|
31
|
+
],
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.json",
|
|
34
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
35
|
+
"test": "vitest run"
|
|
36
|
+
}
|
|
37
|
+
}
|