@atlasauth/expo 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,7 +1,21 @@
1
1
  # @atlasauth/expo changelog
2
2
 
3
+ ## 0.5.1 — 2026-11-12
4
+
5
+ - Picks up `@atlasauth/design` 0.4.0 (zod 4, fixes a zod 3 memory-exhaustion advisory). No API change.
6
+
7
+ ## 0.5.0 — 2026-11-12
8
+
9
+ - `useAppApproval`, `exchangeLegacyToken` (upgrade bridge), `signInWithApple` / `signInWithGoogle` / `signInWithIdToken`.
10
+
3
11
  ## Unreleased
4
12
 
13
+ ### Added
14
+
15
+ - `useAppApproval(id)`: approve-in-your-app for the signed-in app. Reads the pending request, polls while it is pending, and offers `approve(number)` and `deny()` with the shuffled number choices.
16
+ - `exchangeLegacyToken(client, { source, token, clear, storage })`: the upgrade bridge. Trades the legacy session token an existing app still holds for an Atlas session, with no sign-in screen.
17
+ - `signInWithApple`, `signInWithGoogle` and `signInWithIdToken`: native system-sheet sign-in over the provider's client.
18
+
5
19
  ## 0.4.0 — 2026-10-26
6
20
 
7
21
  ### Changed
@@ -0,0 +1,22 @@
1
+ import { type AppApprovalDetails, type FapiClient } from '@atlasauth/js';
2
+ export interface AppApprovalController {
3
+ /** The request, or null while loading / when it does not exist. */
4
+ details: AppApprovalDetails | null;
5
+ loading: boolean;
6
+ /** Approve with the number the person tapped. Resolves true when the answer was accepted. */
7
+ approve(number: string): Promise<boolean>;
8
+ /** "This was not me". */
9
+ deny(): Promise<boolean>;
10
+ error: string | null;
11
+ }
12
+ /** Controller logic, separated from React so it is testable with a fake client. */
13
+ export declare function createAppApprovalActions(client: FapiClient, id: string): {
14
+ load: () => Promise<AppApprovalDetails | null>;
15
+ approve: (number: string) => Promise<boolean>;
16
+ deny: () => Promise<boolean>;
17
+ };
18
+ /**
19
+ * @param id the approval id from your push or webhook; null does nothing.
20
+ * @param pollMs while the request is pending, re-read it this often (default 3000; 0 disables).
21
+ */
22
+ export declare function useAppApproval(id: string | null, pollMs?: number): AppApprovalController;
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAppApprovalActions = createAppApprovalActions;
4
+ exports.useAppApproval = useAppApproval;
5
+ /**
6
+ * Approve-in-your-app for the signed-in app: read the pending request (its id
7
+ * arrives in your push notification, or by polling), show the person the
8
+ * shuffled number choices, and answer. A thin hook over the `@atlasauth/js`
9
+ * calls, bound to the provider's bearer-authenticated client.
10
+ */
11
+ const react_1 = require("react");
12
+ const js_1 = require("@atlasauth/js");
13
+ const policies_1 = require("./policies");
14
+ /** Controller logic, separated from React so it is testable with a fake client. */
15
+ function createAppApprovalActions(client, id) {
16
+ return {
17
+ load: () => (0, js_1.getAppApproval)(client, id),
18
+ approve: async (number) => (await (0, js_1.approveSignIn)(client, { id, number })).ok,
19
+ deny: async () => (await (0, js_1.denySignIn)(client, id)).ok,
20
+ };
21
+ }
22
+ /**
23
+ * @param id the approval id from your push or webhook; null does nothing.
24
+ * @param pollMs while the request is pending, re-read it this often (default 3000; 0 disables).
25
+ */
26
+ function useAppApproval(id, pollMs = 3000) {
27
+ const client = (0, policies_1.useExpoFapiClient)();
28
+ const [details, setDetails] = (0, react_1.useState)(null);
29
+ const [loading, setLoading] = (0, react_1.useState)(id !== null);
30
+ const [error, setError] = (0, react_1.useState)(null);
31
+ const actions = (0, react_1.useRef)(null);
32
+ actions.current = id ? createAppApprovalActions(client, id) : null;
33
+ (0, react_1.useEffect)(() => {
34
+ if (!id)
35
+ return;
36
+ let live = true;
37
+ let timer;
38
+ const tick = async () => {
39
+ const next = await actions.current?.load();
40
+ if (!live)
41
+ return;
42
+ setDetails(next ?? null);
43
+ setLoading(false);
44
+ if (pollMs > 0 && next?.status === 'pending')
45
+ timer = setTimeout(tick, pollMs);
46
+ };
47
+ setLoading(true);
48
+ void tick();
49
+ return () => { live = false; if (timer)
50
+ clearTimeout(timer); };
51
+ }, [id, pollMs]);
52
+ const answer = (0, react_1.useCallback)(async (run) => {
53
+ const ok = (await run()) ?? false;
54
+ setError(ok ? null : 'The answer was not accepted.');
55
+ if (ok)
56
+ setDetails((d) => (d ? { ...d, status: 'approved' } : d));
57
+ return ok;
58
+ }, []);
59
+ return {
60
+ details,
61
+ loading,
62
+ error,
63
+ approve: (n) => answer(() => actions.current?.approve(n)),
64
+ deny: async () => {
65
+ const ok = (await actions.current?.deny()) ?? false;
66
+ setError(ok ? null : 'The answer was not accepted.');
67
+ if (ok)
68
+ setDetails((d) => (d ? { ...d, status: 'denied' } : d));
69
+ return ok;
70
+ },
71
+ };
72
+ }
73
+ //# sourceMappingURL=app-approval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-approval.js","sourceRoot":"","sources":["../src/app-approval.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Upgrade bridge for apps that already have signed-in users: trade the legacy
3
+ * session token the app still holds for an Atlas session, with no sign-in screen.
4
+ *
5
+ * The exchange asks for the tokens in the response body (a native app has no
6
+ * cookies). Pass the provider's client (`useExpoFapiClient()`) and the session
7
+ * JWT is stored by the token cache like any other; pass `storage` as well to keep
8
+ * the refresh token for `createNativeSessionManager`.
9
+ */
10
+ import { type FapiClient, type NativeSession } from '@atlasauth/js';
11
+ import type { TokenStorage } from './token-cache';
12
+ export type LegacyTokenExchangeOutcome = {
13
+ status: 'none';
14
+ } | {
15
+ status: 'complete';
16
+ session: NativeSession;
17
+ userId: string;
18
+ } | {
19
+ status: 'needs_second_factor' | 'needs_mfa_enrollment';
20
+ attemptId: string;
21
+ } | {
22
+ status: 'refused';
23
+ outcome: string;
24
+ };
25
+ export declare function exchangeLegacyToken(client: FapiClient, input: {
26
+ /** The legacy provider key, e.g. `better_auth`, `firebase`, `supabase`. */
27
+ source: string;
28
+ /** The token the app holds; absent means nothing to do. */
29
+ token?: string | null;
30
+ /** Called after a success or a final refusal: delete the legacy token here. */
31
+ clear?: () => void | Promise<void>;
32
+ /** Where to keep the refresh token (a slot dedicated to the native session). */
33
+ storage?: TokenStorage;
34
+ }): Promise<LegacyTokenExchangeOutcome>;
package/dist/bridge.js ADDED
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.exchangeLegacyToken = exchangeLegacyToken;
4
+ /**
5
+ * Upgrade bridge for apps that already have signed-in users: trade the legacy
6
+ * session token the app still holds for an Atlas session, with no sign-in screen.
7
+ *
8
+ * The exchange asks for the tokens in the response body (a native app has no
9
+ * cookies). Pass the provider's client (`useExpoFapiClient()`) and the session
10
+ * JWT is stored by the token cache like any other; pass `storage` as well to keep
11
+ * the refresh token for `createNativeSessionManager`.
12
+ */
13
+ const js_1 = require("@atlasauth/js");
14
+ /** Refusals after which the legacy token will never exchange, so it is safe to delete. */
15
+ const FINAL = new Set(['invalid', 'signature_invalid', 'expired', 'reused', 'user_unavailable', 'user_unknown']);
16
+ async function exchangeLegacyToken(client, input) {
17
+ if (!input.token)
18
+ return { status: 'none' };
19
+ const clear = async () => { try {
20
+ await input.clear?.();
21
+ }
22
+ catch { /* best effort */ } };
23
+ let response;
24
+ try {
25
+ response = await client.post((0, js_1.migrationBrowserExchangePath)(input.source), { token: input.token, delivery: 'body' });
26
+ }
27
+ catch {
28
+ return { status: 'refused', outcome: 'error' };
29
+ }
30
+ const d = response.data;
31
+ if (response.ok && d?.jwt && d.session_id) {
32
+ const session = { sessionToken: d.jwt, refreshToken: d.refresh_token ?? '', sessionId: d.session_id, expiresInSeconds: d.expires_in ?? 0 };
33
+ if (input.storage && session.refreshToken)
34
+ await input.storage.setToken((0, js_1.serializeNativeSession)(session));
35
+ await clear();
36
+ return { status: 'complete', session, userId: d.user_id ?? '' };
37
+ }
38
+ if (response.ok && d?.id && (d.status === 'needs_second_factor' || d.status === 'needs_mfa_enrollment')) {
39
+ await clear();
40
+ return { status: d.status, attemptId: d.id };
41
+ }
42
+ const reason = response.errors[0]?.meta?.bridge_outcome;
43
+ const outcome = typeof reason === 'string' ? reason : 'error';
44
+ if (FINAL.has(outcome))
45
+ await clear();
46
+ return { status: 'refused', outcome };
47
+ }
48
+ //# sourceMappingURL=bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bridge.js","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":";;AAqBA,kDAuCC;AA5DD;;;;;;;;GAQG;AACH,sCAA0H;AAS1H,0FAA0F;AAC1F,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE,mBAAmB,EAAE,SAAS,EAAE,QAAQ,EAAE,kBAAkB,EAAE,cAAc,CAAC,CAAC,CAAC;AAE1G,KAAK,UAAU,mBAAmB,CACvC,MAAkB,EAClB,KASC;IAED,IAAI,CAAC,KAAK,CAAC,KAAK;QAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;IAC5C,MAAM,KAAK,GAAG,KAAK,IAAI,EAAE,GAAG,IAAI,CAAC;QAAC,MAAM,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC;IACzF,IAAI,QAAQ,CAAC;IACb,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAC1B,IAAA,iCAA4B,EAAC,KAAK,CAAC,MAAM,CAAC,EAC1C,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,CACzC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACjD,CAAC;IACD,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC;IACxB,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,UAAU,EAAE,CAAC;QAC1C,MAAM,OAAO,GAAkB,EAAE,YAAY,EAAE,CAAC,CAAC,GAAG,EAAE,YAAY,EAAE,CAAC,CAAC,aAAa,IAAI,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC,UAAU,EAAE,gBAAgB,EAAE,CAAC,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;QAC1J,IAAI,KAAK,CAAC,OAAO,IAAI,OAAO,CAAC,YAAY;YAAE,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAA,2BAAsB,EAAC,OAAO,CAAC,CAAC,CAAC;QACzG,MAAM,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;IAClE,CAAC;IACD,IAAI,QAAQ,CAAC,EAAE,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,KAAK,qBAAqB,IAAI,CAAC,CAAC,MAAM,KAAK,sBAAsB,CAAC,EAAE,CAAC;QACxG,MAAM,KAAK,EAAE,CAAC;QACd,OAAO,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC;IAC/C,CAAC;IACD,MAAM,MAAM,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,CAAC;IACxD,MAAM,OAAO,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAC9D,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,MAAM,KAAK,EAAE,CAAC;IACtC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC;AACxC,CAAC"}
package/dist/index.d.ts CHANGED
@@ -4,3 +4,6 @@ export * from './AtlasProvider';
4
4
  export * from './native-session';
5
5
  export { useAuth, useUser, useSession, useSessionList, useOrganization, SignedIn, SignedOut, Protect, AtlasLoading, AtlasLoaded, } from '@atlasauth/react';
6
6
  export * from './policies';
7
+ export * from './social';
8
+ export * from './bridge';
9
+ export * from './app-approval';
package/dist/index.js CHANGED
@@ -46,4 +46,8 @@ Object.defineProperty(exports, "AtlasLoading", { enumerable: true, get: function
46
46
  Object.defineProperty(exports, "AtlasLoaded", { enumerable: true, get: function () { return react_1.AtlasLoaded; } });
47
47
  // Policy acceptance (terms, privacy, any policy): native components + hooks bound to this provider.
48
48
  __exportStar(require("./policies"), exports);
49
+ // Native social sign-in, the upgrade bridge for apps with existing users, and approve-in-your-app.
50
+ __exportStar(require("./social"), exports);
51
+ __exportStar(require("./bridge"), exports);
52
+ __exportStar(require("./app-approval"), exports);
49
53
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2EAA2E;AAC3E,kEAAkE;AAClE,gDAA8B;AAC9B,gDAA8B;AAC9B,kDAAgC;AAEhC,8EAA8E;AAC9E,mDAAiC;AAEjC;;;;;;;;;;GAUG;AACH,0CAW0B;AAVxB,gGAAA,OAAO,OAAA;AACP,gGAAA,OAAO,OAAA;AACP,mGAAA,UAAU,OAAA;AACV,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,iGAAA,QAAQ,OAAA;AACR,kGAAA,SAAS,OAAA;AACT,gGAAA,OAAO,OAAA;AACP,qGAAA,YAAY,OAAA;AACZ,oGAAA,WAAW,OAAA;AAGb,oGAAoG;AACpG,6CAA2B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2EAA2E;AAC3E,kEAAkE;AAClE,gDAA8B;AAC9B,gDAA8B;AAC9B,kDAAgC;AAEhC,8EAA8E;AAC9E,mDAAiC;AAEjC;;;;;;;;;;GAUG;AACH,0CAW0B;AAVxB,gGAAA,OAAO,OAAA;AACP,gGAAA,OAAO,OAAA;AACP,mGAAA,UAAU,OAAA;AACV,uGAAA,cAAc,OAAA;AACd,wGAAA,eAAe,OAAA;AACf,iGAAA,QAAQ,OAAA;AACR,kGAAA,SAAS,OAAA;AACT,gGAAA,OAAO,OAAA;AACP,qGAAA,YAAY,OAAA;AACZ,oGAAA,WAAW,OAAA;AAGb,oGAAoG;AACpG,6CAA2B;AAE3B,mGAAmG;AACnG,2CAAyB;AACzB,2CAAyB;AACzB,iDAA+B"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Native Google / Apple sign-in for the provider's client: the system sheet
3
+ * yields a provider ID token, which is sent to Atlas (`POST
4
+ * /v1/client/sign_ins/id_token`) and traded for a session. The provider's token
5
+ * cache stores the returned session JWT.
6
+ */
7
+ import { type FapiClient, type FieldError } from '@atlasauth/js';
8
+ export interface NativeSocialResult {
9
+ status: 'complete' | 'needs_more' | 'error' | 'cancelled';
10
+ jwt?: string;
11
+ attemptId?: string;
12
+ errors: FieldError[];
13
+ }
14
+ /** Send a provider ID token (with the nonce it was minted with) and finish the sign-in. */
15
+ export declare function signInWithIdToken(client: FapiClient, input: {
16
+ provider: string;
17
+ idToken: string;
18
+ nonce?: string;
19
+ }): Promise<NativeSocialResult>;
20
+ /** The slice of `expo-apple-authentication` used here. */
21
+ export interface AppleAuthenticationLike {
22
+ signInAsync(options: {
23
+ requestedScopes?: unknown[];
24
+ nonce?: string;
25
+ }): Promise<{
26
+ identityToken: string | null;
27
+ }>;
28
+ AppleAuthenticationScope: {
29
+ EMAIL: unknown;
30
+ FULL_NAME: unknown;
31
+ };
32
+ }
33
+ /** Sign in with the Apple system sheet. A dismissed sheet resolves as `cancelled`. */
34
+ export declare function signInWithApple(client: FapiClient, apple: AppleAuthenticationLike): Promise<NativeSocialResult>;
35
+ /** The slice of `@react-native-google-signin/google-signin` used here. */
36
+ export interface GoogleSigninLike {
37
+ signIn(options?: {
38
+ nonce?: string;
39
+ }): Promise<unknown>;
40
+ }
41
+ /** Sign in with the Google system sheet (use `@atlasauth/expo-google-signin` for Play Services checks). */
42
+ export declare function signInWithGoogle(client: FapiClient, google: GoogleSigninLike): Promise<NativeSocialResult>;
package/dist/social.js ADDED
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signInWithIdToken = signInWithIdToken;
4
+ exports.signInWithApple = signInWithApple;
5
+ exports.signInWithGoogle = signInWithGoogle;
6
+ /**
7
+ * Native Google / Apple sign-in for the provider's client: the system sheet
8
+ * yields a provider ID token, which is sent to Atlas (`POST
9
+ * /v1/client/sign_ins/id_token`) and traded for a session. The provider's token
10
+ * cache stores the returned session JWT.
11
+ */
12
+ const js_1 = require("@atlasauth/js");
13
+ /** Send a provider ID token (with the nonce it was minted with) and finish the sign-in. */
14
+ async function signInWithIdToken(client, input) {
15
+ const flow = await (0, js_1.nativeSignIn)(client, input);
16
+ if (flow.errors.length)
17
+ return { status: 'error', errors: flow.errors };
18
+ const attempt = flow.attempt;
19
+ if (!flow.ticket)
20
+ return { status: 'needs_more', ...(attempt?.id ? { attemptId: attempt.id } : {}), errors: [] };
21
+ const ex = await client.post('/v1/client/tickets/exchange', { attempt_id: attempt?.id ?? '', ticket: flow.ticket });
22
+ if (!ex.ok || !ex.data?.jwt)
23
+ return { status: 'error', errors: ex.errors.length ? ex.errors : [{ code: 'EXCHANGE_FAILED', message: 'Could not complete sign-in.' }] };
24
+ return { status: 'complete', jwt: ex.data.jwt, ...(attempt?.id ? { attemptId: attempt.id } : {}), errors: [] };
25
+ }
26
+ /** Sign in with the Apple system sheet. A dismissed sheet resolves as `cancelled`. */
27
+ async function signInWithApple(client, apple) {
28
+ const nonce = (await (0, js_1.mintNativeNonce)(client, 'apple')) ?? undefined;
29
+ if (!nonce)
30
+ return { status: 'error', errors: [{ code: 'NONCE', message: 'Could not start Apple sign-in.' }] };
31
+ let idToken;
32
+ try {
33
+ ({ identityToken: idToken } = await apple.signInAsync({ requestedScopes: [apple.AppleAuthenticationScope.EMAIL, apple.AppleAuthenticationScope.FULL_NAME], nonce }));
34
+ }
35
+ catch {
36
+ return { status: 'cancelled', errors: [] };
37
+ }
38
+ if (!idToken)
39
+ return { status: 'cancelled', errors: [] };
40
+ return signInWithIdToken(client, { provider: 'apple', idToken, nonce });
41
+ }
42
+ /** Sign in with the Google system sheet (use `@atlasauth/expo-google-signin` for Play Services checks). */
43
+ async function signInWithGoogle(client, google) {
44
+ const nonce = (await (0, js_1.mintNativeNonce)(client, 'google')) ?? undefined;
45
+ if (!nonce)
46
+ return { status: 'error', errors: [{ code: 'NONCE', message: 'Could not start Google sign-in.' }] };
47
+ let response;
48
+ try {
49
+ response = await google.signIn({ nonce });
50
+ }
51
+ catch {
52
+ return { status: 'cancelled', errors: [] };
53
+ }
54
+ const r = response;
55
+ const idToken = r?.data?.idToken ?? r?.idToken;
56
+ if (!idToken)
57
+ return { status: 'cancelled', errors: [] };
58
+ return signInWithIdToken(client, { provider: 'google', idToken, nonce });
59
+ }
60
+ //# sourceMappingURL=social.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"social.js","sourceRoot":"","sources":["../src/social.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@atlasauth/expo",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "dependencies": {
7
- "@atlasauth/js": "0.24.0",
8
- "@atlasauth/react": "^0.21.0",
7
+ "@atlasauth/js": "0.38.0",
8
+ "@atlasauth/react": "^0.34.1",
9
9
  "@atlasauth/react-native": "^0.7.0"
10
10
  },
11
11
  "peerDependencies": {
@@ -36,7 +36,7 @@
36
36
  "@types/react": "^18.3.12",
37
37
  "react": "^18.3.1",
38
38
  "typescript": "^5.6.3",
39
- "vitest": "^2.1.4"
39
+ "vitest": "^3.2.7"
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public"