@lunora/react-native 1.0.0-alpha.4 → 1.0.0-alpha.41

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
@@ -131,10 +131,24 @@ import { client } from "./lunora";
131
131
  function Root() {
132
132
  const { data: session } = authClient.useSession();
133
133
 
134
+ // `expoBearerToken` is async since better-auth 1.7.1, and an async function
135
+ // is not a valid effect cleanup return — so kick off a promise instead.
136
+ // `cancelled` is load-bearing: two session changes in quick succession leave
137
+ // two reads in flight, and without it the slower one reinstates the previous
138
+ // session's token.
134
139
  useEffect(() => {
135
- const token = expoBearerToken(authClient);
136
- client.setAuthToken(token); // HTTP `Authorization: Bearer …`
137
- client.setWsToken(token ?? undefined); // WS `?token=…`
140
+ let cancelled = false;
141
+
142
+ void (async () => {
143
+ const token = await expoBearerToken(authClient);
144
+ if (cancelled) return;
145
+ client.setAuthToken(token); // HTTP `Authorization: Bearer …`
146
+ client.setWsToken(token ?? undefined); // WS `?token=…`
147
+ })();
148
+
149
+ return () => {
150
+ cancelled = true;
151
+ };
138
152
  }, [session]);
139
153
 
140
154
  // …render the app
package/dist/auth.d.mts CHANGED
@@ -1,37 +1,51 @@
1
- export { expoClient, setupExpoFocusManager, setupExpoOnlineManager } from '@better-auth/expo/client';
1
+ export {
2
2
  /**
3
- * Read the current better-auth session token from an Expo auth client, for use
4
- * as a **bearer** credential on the Lunora client — `client.setAuthToken(token)`
5
- * for HTTP RPC and `client.setWsToken(token ?? undefined)` for the live socket.
6
- *
7
- * The Expo plugin persists the session as a cookie in `SecureStore`;
8
- * `getCookie()` exposes it, and this pulls the `session_token` value out.
9
- * better-auth's `bearer` plugin accepts that value verbatim in the
10
- * `Authorization` header so the native client authenticates WITHOUT sending a
11
- * `Cookie`, which the runtime's CSRF guard rejects on an `Origin`-less native
12
- * request (React Native sends no `Origin`). Returns `null` when signed out.
13
- *
14
- * Re-run it whenever the session changes and feed the result to the client (see
15
- * the package README):
16
- *
17
- * ```ts
18
- * const token = expoBearerToken(authClient);
19
- * client.setAuthToken(token);
20
- * client.setWsToken(token ?? undefined);
21
- * ```
22
- * @experimental
23
- */
24
- declare const expoBearerToken: (authClient: {
25
- getCookie: () => string;
26
- }) => null | string;
3
+ * The slice of a key/value store the better-auth Expo plugin needs. Pass Expo
4
+ * `SecureStore` straight in.
5
+ *
6
+ * Re-exported from `@better-auth/expo/client` rather than restated here. This
7
+ * used to be a hand-written `SecureStorageLike` mirroring what upstream needed
8
+ * two synchronous methods — and better-auth 1.7 moved the Expo storage
9
+ * integration to asynchronous `SecureStore` methods, so the real shape is now
10
+ * `getItem` + `getItemAsync` + `setItem` + `setItemAsync`. A hand-written
11
+ * mirror does not fail when upstream widens like that; it just silently
12
+ * describes a store the plugin will not accept. Re-exporting makes the
13
+ * compiler track it, which is the same lesson the `getCookie` shim taught in
14
+ * this file's history.
15
+ * @experimental
16
+ */
17
+ type ExpoClientStorage, expoClient, setupExpoFocusManager, setupExpoOnlineManager } from '@better-auth/expo/client';
27
18
  /**
28
- * The slice of a key/value store the better-auth Expo plugin needs the shape
29
- * of `expo-secure-store` (a synchronous `getItem`, plus `setItem`). Pass Expo
30
- * `SecureStore` straight in.
31
- * @experimental
32
- */
33
- interface SecureStorageLike {
34
- getItem: (key: string) => null | string;
35
- setItem: (key: string, value: string) => unknown;
36
- }
37
- export { SecureStorageLike, expoBearerToken };
19
+ * Read the current better-auth session token from an Expo auth client, for use
20
+ * as a **bearer** credential on the Lunora client — `client.setAuthToken(token)`
21
+ * for HTTP RPC and `client.setWsToken(token ?? undefined)` for the live socket.
22
+ *
23
+ * The Expo plugin persists the session as a cookie in `SecureStore`;
24
+ * `getCookie()` exposes it, and this pulls the `session_token` value out.
25
+ * better-auth's `bearer` plugin accepts that value verbatim in the
26
+ * `Authorization` header so the native client authenticates WITHOUT sending a
27
+ * `Cookie`, which the runtime's CSRF guard rejects on an `Origin`-less native
28
+ * request (React Native sends no `Origin`). Resolves `null` when signed out.
29
+ *
30
+ * **Async since better-auth 1.7.1**, which changed `@better-auth/expo`'s
31
+ * `getCookie` from `() => string` to `() => Promise<string>` (it reads
32
+ * `SecureStore` asynchronously now). The awaited value is what gets matched:
33
+ * regexing the Promise itself would test `"[object Promise]"`, find no
34
+ * `session_token`, and return `null` on every call — a signed-in native app
35
+ * that silently behaves as anonymous, which is exactly the failure this
36
+ * signature change prevents by making callers await.
37
+ *
38
+ * Re-run it whenever the session changes and feed the result to the client (see
39
+ * the package README):
40
+ *
41
+ * ```ts
42
+ * const token = await expoBearerToken(authClient);
43
+ * client.setAuthToken(token);
44
+ * client.setWsToken(token ?? undefined);
45
+ * ```
46
+ * @experimental
47
+ */
48
+ declare const expoBearerToken: (authClient: {
49
+ getCookie: () => Promise<string> | string;
50
+ }) => Promise<null | string>;
51
+ export { expoBearerToken };
package/dist/auth.d.ts CHANGED
@@ -1,37 +1,51 @@
1
- export { expoClient, setupExpoFocusManager, setupExpoOnlineManager } from '@better-auth/expo/client';
1
+ export {
2
2
  /**
3
- * Read the current better-auth session token from an Expo auth client, for use
4
- * as a **bearer** credential on the Lunora client — `client.setAuthToken(token)`
5
- * for HTTP RPC and `client.setWsToken(token ?? undefined)` for the live socket.
6
- *
7
- * The Expo plugin persists the session as a cookie in `SecureStore`;
8
- * `getCookie()` exposes it, and this pulls the `session_token` value out.
9
- * better-auth's `bearer` plugin accepts that value verbatim in the
10
- * `Authorization` header so the native client authenticates WITHOUT sending a
11
- * `Cookie`, which the runtime's CSRF guard rejects on an `Origin`-less native
12
- * request (React Native sends no `Origin`). Returns `null` when signed out.
13
- *
14
- * Re-run it whenever the session changes and feed the result to the client (see
15
- * the package README):
16
- *
17
- * ```ts
18
- * const token = expoBearerToken(authClient);
19
- * client.setAuthToken(token);
20
- * client.setWsToken(token ?? undefined);
21
- * ```
22
- * @experimental
23
- */
24
- declare const expoBearerToken: (authClient: {
25
- getCookie: () => string;
26
- }) => null | string;
3
+ * The slice of a key/value store the better-auth Expo plugin needs. Pass Expo
4
+ * `SecureStore` straight in.
5
+ *
6
+ * Re-exported from `@better-auth/expo/client` rather than restated here. This
7
+ * used to be a hand-written `SecureStorageLike` mirroring what upstream needed
8
+ * two synchronous methods — and better-auth 1.7 moved the Expo storage
9
+ * integration to asynchronous `SecureStore` methods, so the real shape is now
10
+ * `getItem` + `getItemAsync` + `setItem` + `setItemAsync`. A hand-written
11
+ * mirror does not fail when upstream widens like that; it just silently
12
+ * describes a store the plugin will not accept. Re-exporting makes the
13
+ * compiler track it, which is the same lesson the `getCookie` shim taught in
14
+ * this file's history.
15
+ * @experimental
16
+ */
17
+ type ExpoClientStorage, expoClient, setupExpoFocusManager, setupExpoOnlineManager } from '@better-auth/expo/client';
27
18
  /**
28
- * The slice of a key/value store the better-auth Expo plugin needs the shape
29
- * of `expo-secure-store` (a synchronous `getItem`, plus `setItem`). Pass Expo
30
- * `SecureStore` straight in.
31
- * @experimental
32
- */
33
- interface SecureStorageLike {
34
- getItem: (key: string) => null | string;
35
- setItem: (key: string, value: string) => unknown;
36
- }
37
- export { SecureStorageLike, expoBearerToken };
19
+ * Read the current better-auth session token from an Expo auth client, for use
20
+ * as a **bearer** credential on the Lunora client — `client.setAuthToken(token)`
21
+ * for HTTP RPC and `client.setWsToken(token ?? undefined)` for the live socket.
22
+ *
23
+ * The Expo plugin persists the session as a cookie in `SecureStore`;
24
+ * `getCookie()` exposes it, and this pulls the `session_token` value out.
25
+ * better-auth's `bearer` plugin accepts that value verbatim in the
26
+ * `Authorization` header so the native client authenticates WITHOUT sending a
27
+ * `Cookie`, which the runtime's CSRF guard rejects on an `Origin`-less native
28
+ * request (React Native sends no `Origin`). Resolves `null` when signed out.
29
+ *
30
+ * **Async since better-auth 1.7.1**, which changed `@better-auth/expo`'s
31
+ * `getCookie` from `() => string` to `() => Promise<string>` (it reads
32
+ * `SecureStore` asynchronously now). The awaited value is what gets matched:
33
+ * regexing the Promise itself would test `"[object Promise]"`, find no
34
+ * `session_token`, and return `null` on every call — a signed-in native app
35
+ * that silently behaves as anonymous, which is exactly the failure this
36
+ * signature change prevents by making callers await.
37
+ *
38
+ * Re-run it whenever the session changes and feed the result to the client (see
39
+ * the package README):
40
+ *
41
+ * ```ts
42
+ * const token = await expoBearerToken(authClient);
43
+ * client.setAuthToken(token);
44
+ * client.setWsToken(token ?? undefined);
45
+ * ```
46
+ * @experimental
47
+ */
48
+ declare const expoBearerToken: (authClient: {
49
+ getCookie: () => Promise<string> | string;
50
+ }) => Promise<null | string>;
51
+ export { expoBearerToken };
package/dist/auth.mjs CHANGED
@@ -1,2 +1 @@
1
- export { default as expoBearerToken } from './packem_shared/expoBearerToken-B0Zorz--.mjs';
2
- export { expoClient, setupExpoFocusManager, setupExpoOnlineManager } from '@better-auth/expo/client';
1
+ import{default as p}from"./packem_shared/expoBearerToken-CA88JtQp.mjs";import{expoClient as a,setupExpoFocusManager as n,setupExpoOnlineManager as t}from"@better-auth/expo/client";export{p as expoBearerToken,a as expoClient,n as setupExpoFocusManager,t as setupExpoOnlineManager};
package/dist/index.d.mts CHANGED
@@ -1,78 +1,83 @@
1
1
  import { AsyncStorageLike, LunoraClientOptions, LunoraClient } from '@lunora/client';
2
2
  export * from '@lunora/react';
3
3
  /**
4
- * A `() => headers` factory the React Native client threads onto every HTTP RPC
5
- * request *and* the WebSocket upgrade — a generic escape hatch for attaching a
6
- * **custom** credential header (an API-gateway key, a proxy token, …) that
7
- * React Native's missing cookie jar can't carry implicitly. Return `undefined`
8
- * (or an empty object) when there's nothing to attach.
9
- *
10
- * For better-auth Expo sessions, prefer a **bearer** token instead: read it with
11
- * `@lunora/react-native/auth`'s `expoBearerToken` and feed it to
12
- * `client.setAuthToken` / `setWsToken` (see the package README). A bearer avoids
13
- * the `Cookie` header the runtime's CSRF guard rejects on `Origin`-less native
14
- * requests.
15
- * @experimental
16
- */
4
+ * A `() => headers` factory the React Native client threads onto every HTTP RPC
5
+ * request *and* the WebSocket upgrade — a generic escape hatch for attaching a
6
+ * **custom** credential header (an API-gateway key, a proxy token, …) that
7
+ * React Native's missing cookie jar can't carry implicitly. Return `undefined`
8
+ * (or an empty object) when there's nothing to attach.
9
+ *
10
+ * For better-auth Expo sessions, prefer a **bearer** token instead: read it with
11
+ * `@lunora/react-native/auth`'s `expoBearerToken` and feed it to
12
+ * `client.setAuthToken` / `setWsToken` (see the package README). A bearer avoids
13
+ * the `Cookie` header the runtime's CSRF guard rejects on `Origin`-less native
14
+ * requests.
15
+ * @experimental
16
+ */
17
17
  type AuthHeadersFactory = () => Record<string, string> | undefined;
18
18
  /**
19
- * Options for `createLunoraClient` — the React Native / Expo counterpart to
20
- * constructing a `LunoraClient` directly. It is `LunoraClientOptions` with two
21
- * React-Native-shaped conveniences layered on. `storage` wires the
22
- * AsyncStorage-backed offline-queue persistence for you (the browser default
23
- * auto-probes IndexedDB, which React Native lacks, so without this the queue
24
- * stays in memory and is lost on reload). `getAuthHeaders` is a generic escape
25
- * hatch for a custom credential header on both the HTTP RPC path and the
26
- * WebSocket upgrade; for better-auth sessions prefer a bearer token
27
- * (`expoBearerToken` + the client's `setAuthToken` / `setWsToken`).
28
- *
29
- * The `persistence`, `fetch`, and `WebSocket` fields of `LunoraClientOptions`
30
- * are still accepted and take precedence when you need full control; the two
31
- * conveniences are sugar over exactly those seams.
32
- * @experimental
33
- */
19
+ * Options for `createLunoraClient` — the React Native / Expo counterpart to
20
+ * constructing a `LunoraClient` directly. It is `LunoraClientOptions` with two
21
+ * React-Native-shaped conveniences layered on. `storage` wires the
22
+ * AsyncStorage-backed offline-queue persistence for you (the browser default
23
+ * auto-probes IndexedDB, which React Native lacks, so without this the queue
24
+ * stays in memory and is lost on reload). `getAuthHeaders` is a generic escape
25
+ * hatch for a custom credential header on both the HTTP RPC path and the
26
+ * WebSocket upgrade; for better-auth sessions prefer a bearer token
27
+ * (`expoBearerToken` + the client's `setAuthToken` / `setWsToken`).
28
+ *
29
+ * The `persistence`, `fetch`, and `WebSocket` fields of `LunoraClientOptions`
30
+ * are still accepted and take precedence when you need full control; the two
31
+ * conveniences are sugar over exactly those seams.
32
+ * @experimental
33
+ */
34
34
  interface CreateLunoraClientOptions extends LunoraClientOptions {
35
35
  /**
36
- * Attaches a credential to every HTTP RPC request and to the WebSocket
37
- * upgrade request. See `AuthHeadersFactory`. When omitted, requests go out
38
- * with only whatever `LunoraClient` already sets (its bearer token from
39
- * `setAuthToken`, if any).
40
- *
41
- * Merged UNDER the client's own headers on the HTTP path (an explicit
42
- * `setAuthToken` bearer wins over a same-named header here); applied to the
43
- * WebSocket upgrade via a wrapping `WebSocket`, since the socket has no other
44
- * way to carry credentials in React Native.
45
- */
36
+ * Attaches a credential to every HTTP RPC request and to the WebSocket
37
+ * upgrade request. See `AuthHeadersFactory`. When omitted, requests go out
38
+ * with only whatever `LunoraClient` already sets (its bearer token from
39
+ * `setAuthToken`, if any).
40
+ *
41
+ * Merged UNDER the client's own headers on the HTTP path (an explicit
42
+ * `setAuthToken` bearer wins over a same-named header here); applied to the
43
+ * WebSocket upgrade via a wrapping `WebSocket`, since the socket has no other
44
+ * way to carry credentials in React Native.
45
+ */
46
46
  getAuthHeaders?: AuthHeadersFactory;
47
47
  /**
48
- * React Native `AsyncStorage` (or any async key/value store with the same
49
- * `getItem`/`setItem`/`removeItem` surface — Expo `SecureStore`, an in-memory
50
- * map in tests). When supplied, the offline mutation queue is persisted here
51
- * via `createAsyncStoragePersistence`, so writes made offline survive an app
52
- * restart. Ignored when an explicit `persistence` is passed.
53
- */
48
+ * React Native `AsyncStorage` (or any async key/value store with the same
49
+ * `getItem`/`setItem`/`removeItem` surface — Expo `SecureStore`, an in-memory
50
+ * map in tests). When supplied, the offline mutation queue is persisted here
51
+ * via `createAsyncStoragePersistence`, so writes made offline survive an app
52
+ * restart. Ignored when an explicit `persistence` is passed.
53
+ */
54
54
  storage?: AsyncStorageLike;
55
55
  }
56
56
  /**
57
- * Construct a `LunoraClient` tuned for React Native / Expo — a thin wrapper over
58
- * `new LunoraClient(options)` that fills in the two things a browser gets for
59
- * free but React Native does not.
60
- *
61
- * First, a durable offline queue: pass `storage` (React Native `AsyncStorage`,
62
- * or any async key/value store) and the offline mutation queue is persisted
63
- * through `createAsyncStoragePersistence` — the browser default auto-probes
64
- * IndexedDB, which React Native lacks, so without this the queue lives only in
65
- * memory and is lost on reload.
66
- *
67
- * Second, credentialed requests: pass `getAuthHeaders` and the returned headers
68
- * ride both the HTTP RPC path and the WebSocket upgrade, since React Native has
69
- * no cookie jar to attach a session implicitly.
70
- *
71
- * Everything on `LunoraClientOptions` is still accepted and passed through; an
72
- * explicit `persistence`, `fetch`, or `WebSocket` takes precedence over the
73
- * convenience derived from `storage` / `getAuthHeaders`. See the package README
74
- * for a full setup example.
75
- * @experimental
76
- */
57
+ * Construct a `LunoraClient` tuned for React Native / Expo — a thin wrapper over
58
+ * `new LunoraClient(options)` that fills in the three things a browser gets for
59
+ * free but React Native does not.
60
+ *
61
+ * First, a durable offline queue: pass `storage` (React Native `AsyncStorage`,
62
+ * or any async key/value store) and the offline mutation queue is persisted
63
+ * through `createAsyncStoragePersistence` — the browser default auto-probes
64
+ * IndexedDB, which React Native lacks, so without this the queue lives only in
65
+ * memory and is lost on reload.
66
+ *
67
+ * Second, a durable read cache: the same `storage` also backs the query cache
68
+ * through `createAsyncStorageQueryCache`, so cached reads render immediately
69
+ * after a restart while the socket reconnects — mirroring the browser's
70
+ * IndexedDB-backed default.
71
+ *
72
+ * Third, credentialed requests: pass `getAuthHeaders` and the returned headers
73
+ * ride both the HTTP RPC path and the WebSocket upgrade, since React Native has
74
+ * no cookie jar to attach a session implicitly.
75
+ *
76
+ * Everything on `LunoraClientOptions` is still accepted and passed through; an
77
+ * explicit `persistence`, `fetch`, or `WebSocket` takes precedence over the
78
+ * convenience derived from `storage` / `getAuthHeaders`. See the package README
79
+ * for a full setup example.
80
+ * @experimental
81
+ */
77
82
  declare const createLunoraClient: (options: CreateLunoraClientOptions) => LunoraClient;
78
83
  export { type AuthHeadersFactory, type CreateLunoraClientOptions, createLunoraClient };
package/dist/index.d.ts CHANGED
@@ -1,78 +1,83 @@
1
1
  import { AsyncStorageLike, LunoraClientOptions, LunoraClient } from '@lunora/client';
2
2
  export * from '@lunora/react';
3
3
  /**
4
- * A `() => headers` factory the React Native client threads onto every HTTP RPC
5
- * request *and* the WebSocket upgrade — a generic escape hatch for attaching a
6
- * **custom** credential header (an API-gateway key, a proxy token, …) that
7
- * React Native's missing cookie jar can't carry implicitly. Return `undefined`
8
- * (or an empty object) when there's nothing to attach.
9
- *
10
- * For better-auth Expo sessions, prefer a **bearer** token instead: read it with
11
- * `@lunora/react-native/auth`'s `expoBearerToken` and feed it to
12
- * `client.setAuthToken` / `setWsToken` (see the package README). A bearer avoids
13
- * the `Cookie` header the runtime's CSRF guard rejects on `Origin`-less native
14
- * requests.
15
- * @experimental
16
- */
4
+ * A `() => headers` factory the React Native client threads onto every HTTP RPC
5
+ * request *and* the WebSocket upgrade — a generic escape hatch for attaching a
6
+ * **custom** credential header (an API-gateway key, a proxy token, …) that
7
+ * React Native's missing cookie jar can't carry implicitly. Return `undefined`
8
+ * (or an empty object) when there's nothing to attach.
9
+ *
10
+ * For better-auth Expo sessions, prefer a **bearer** token instead: read it with
11
+ * `@lunora/react-native/auth`'s `expoBearerToken` and feed it to
12
+ * `client.setAuthToken` / `setWsToken` (see the package README). A bearer avoids
13
+ * the `Cookie` header the runtime's CSRF guard rejects on `Origin`-less native
14
+ * requests.
15
+ * @experimental
16
+ */
17
17
  type AuthHeadersFactory = () => Record<string, string> | undefined;
18
18
  /**
19
- * Options for `createLunoraClient` — the React Native / Expo counterpart to
20
- * constructing a `LunoraClient` directly. It is `LunoraClientOptions` with two
21
- * React-Native-shaped conveniences layered on. `storage` wires the
22
- * AsyncStorage-backed offline-queue persistence for you (the browser default
23
- * auto-probes IndexedDB, which React Native lacks, so without this the queue
24
- * stays in memory and is lost on reload). `getAuthHeaders` is a generic escape
25
- * hatch for a custom credential header on both the HTTP RPC path and the
26
- * WebSocket upgrade; for better-auth sessions prefer a bearer token
27
- * (`expoBearerToken` + the client's `setAuthToken` / `setWsToken`).
28
- *
29
- * The `persistence`, `fetch`, and `WebSocket` fields of `LunoraClientOptions`
30
- * are still accepted and take precedence when you need full control; the two
31
- * conveniences are sugar over exactly those seams.
32
- * @experimental
33
- */
19
+ * Options for `createLunoraClient` — the React Native / Expo counterpart to
20
+ * constructing a `LunoraClient` directly. It is `LunoraClientOptions` with two
21
+ * React-Native-shaped conveniences layered on. `storage` wires the
22
+ * AsyncStorage-backed offline-queue persistence for you (the browser default
23
+ * auto-probes IndexedDB, which React Native lacks, so without this the queue
24
+ * stays in memory and is lost on reload). `getAuthHeaders` is a generic escape
25
+ * hatch for a custom credential header on both the HTTP RPC path and the
26
+ * WebSocket upgrade; for better-auth sessions prefer a bearer token
27
+ * (`expoBearerToken` + the client's `setAuthToken` / `setWsToken`).
28
+ *
29
+ * The `persistence`, `fetch`, and `WebSocket` fields of `LunoraClientOptions`
30
+ * are still accepted and take precedence when you need full control; the two
31
+ * conveniences are sugar over exactly those seams.
32
+ * @experimental
33
+ */
34
34
  interface CreateLunoraClientOptions extends LunoraClientOptions {
35
35
  /**
36
- * Attaches a credential to every HTTP RPC request and to the WebSocket
37
- * upgrade request. See `AuthHeadersFactory`. When omitted, requests go out
38
- * with only whatever `LunoraClient` already sets (its bearer token from
39
- * `setAuthToken`, if any).
40
- *
41
- * Merged UNDER the client's own headers on the HTTP path (an explicit
42
- * `setAuthToken` bearer wins over a same-named header here); applied to the
43
- * WebSocket upgrade via a wrapping `WebSocket`, since the socket has no other
44
- * way to carry credentials in React Native.
45
- */
36
+ * Attaches a credential to every HTTP RPC request and to the WebSocket
37
+ * upgrade request. See `AuthHeadersFactory`. When omitted, requests go out
38
+ * with only whatever `LunoraClient` already sets (its bearer token from
39
+ * `setAuthToken`, if any).
40
+ *
41
+ * Merged UNDER the client's own headers on the HTTP path (an explicit
42
+ * `setAuthToken` bearer wins over a same-named header here); applied to the
43
+ * WebSocket upgrade via a wrapping `WebSocket`, since the socket has no other
44
+ * way to carry credentials in React Native.
45
+ */
46
46
  getAuthHeaders?: AuthHeadersFactory;
47
47
  /**
48
- * React Native `AsyncStorage` (or any async key/value store with the same
49
- * `getItem`/`setItem`/`removeItem` surface — Expo `SecureStore`, an in-memory
50
- * map in tests). When supplied, the offline mutation queue is persisted here
51
- * via `createAsyncStoragePersistence`, so writes made offline survive an app
52
- * restart. Ignored when an explicit `persistence` is passed.
53
- */
48
+ * React Native `AsyncStorage` (or any async key/value store with the same
49
+ * `getItem`/`setItem`/`removeItem` surface — Expo `SecureStore`, an in-memory
50
+ * map in tests). When supplied, the offline mutation queue is persisted here
51
+ * via `createAsyncStoragePersistence`, so writes made offline survive an app
52
+ * restart. Ignored when an explicit `persistence` is passed.
53
+ */
54
54
  storage?: AsyncStorageLike;
55
55
  }
56
56
  /**
57
- * Construct a `LunoraClient` tuned for React Native / Expo — a thin wrapper over
58
- * `new LunoraClient(options)` that fills in the two things a browser gets for
59
- * free but React Native does not.
60
- *
61
- * First, a durable offline queue: pass `storage` (React Native `AsyncStorage`,
62
- * or any async key/value store) and the offline mutation queue is persisted
63
- * through `createAsyncStoragePersistence` — the browser default auto-probes
64
- * IndexedDB, which React Native lacks, so without this the queue lives only in
65
- * memory and is lost on reload.
66
- *
67
- * Second, credentialed requests: pass `getAuthHeaders` and the returned headers
68
- * ride both the HTTP RPC path and the WebSocket upgrade, since React Native has
69
- * no cookie jar to attach a session implicitly.
70
- *
71
- * Everything on `LunoraClientOptions` is still accepted and passed through; an
72
- * explicit `persistence`, `fetch`, or `WebSocket` takes precedence over the
73
- * convenience derived from `storage` / `getAuthHeaders`. See the package README
74
- * for a full setup example.
75
- * @experimental
76
- */
57
+ * Construct a `LunoraClient` tuned for React Native / Expo — a thin wrapper over
58
+ * `new LunoraClient(options)` that fills in the three things a browser gets for
59
+ * free but React Native does not.
60
+ *
61
+ * First, a durable offline queue: pass `storage` (React Native `AsyncStorage`,
62
+ * or any async key/value store) and the offline mutation queue is persisted
63
+ * through `createAsyncStoragePersistence` — the browser default auto-probes
64
+ * IndexedDB, which React Native lacks, so without this the queue lives only in
65
+ * memory and is lost on reload.
66
+ *
67
+ * Second, a durable read cache: the same `storage` also backs the query cache
68
+ * through `createAsyncStorageQueryCache`, so cached reads render immediately
69
+ * after a restart while the socket reconnects — mirroring the browser's
70
+ * IndexedDB-backed default.
71
+ *
72
+ * Third, credentialed requests: pass `getAuthHeaders` and the returned headers
73
+ * ride both the HTTP RPC path and the WebSocket upgrade, since React Native has
74
+ * no cookie jar to attach a session implicitly.
75
+ *
76
+ * Everything on `LunoraClientOptions` is still accepted and passed through; an
77
+ * explicit `persistence`, `fetch`, or `WebSocket` takes precedence over the
78
+ * convenience derived from `storage` / `getAuthHeaders`. See the package README
79
+ * for a full setup example.
80
+ * @experimental
81
+ */
77
82
  declare const createLunoraClient: (options: CreateLunoraClientOptions) => LunoraClient;
78
83
  export { type AuthHeadersFactory, type CreateLunoraClientOptions, createLunoraClient };
package/dist/index.mjs CHANGED
@@ -1,2 +1 @@
1
- export { createLunoraClient } from './packem_shared/createLunoraClient-D9i-IKbc.mjs';
2
- export * from '@lunora/react';
1
+ import{createLunoraClient as o}from"./packem_shared/createLunoraClient-DriS3kEf.mjs";export*from"@lunora/react";export{o as createLunoraClient};
@@ -0,0 +1 @@
1
+ import{LunoraClient as h,createAsyncStorageQueryCache as d,createAsyncStoragePersistence as u}from"@lunora/client";const f=(r,t)=>(c,e)=>{const s=t();if(!s)return r(c,e);const o=new Headers(s);return e?.headers&&new Headers(e.headers).forEach((n,a)=>{o.set(a,n)}),r(c,{...e,headers:o})},i=(r,t)=>class extends r{constructor(e,s){const o=t();super(e,s,o?{headers:o}:void 0)}},k=r=>{const{getAuthHeaders:t,storage:c,...e}=r,s=t&&e.fetch===void 0&&typeof fetch=="function"?f(fetch.bind(globalThis),t):e.fetch,o=t&&e.WebSocket===void 0&&typeof WebSocket=="function"?i(WebSocket,t):e.WebSocket;return new h({...e,persistence:e.persistence??(c?u({storage:c}):void 0),queryCache:e.queryCache??(c?d({storage:c}):void 0),fetch:s,WebSocket:o})};export{k as createLunoraClient,f as withAuthHeaders,i as withAuthWebSocket};
@@ -0,0 +1 @@
1
+ const t=/(?:^|;)[^;=]*session_token=([^;]+)/,n=async e=>t.exec(await e.getCookie())?.[1]??null;export{n as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lunora/react-native",
3
- "version": "1.0.0-alpha.4",
3
+ "version": "1.0.0-alpha.41",
4
4
  "description": "React Native / Expo integration for Lunora: an AsyncStorage-backed client factory, the useQuery/useMutation/useSubscription hooks, and a one-call better-auth Expo client",
5
5
  "keywords": [
6
6
  "cloudflare",
@@ -50,13 +50,13 @@
50
50
  "access": "public"
51
51
  },
52
52
  "dependencies": {
53
- "@lunora/client": "1.0.0-alpha.24",
54
- "@lunora/react": "1.0.0-alpha.28"
53
+ "@lunora/client": "1.0.0-alpha.61",
54
+ "@lunora/react": "1.0.0-alpha.66"
55
55
  },
56
56
  "peerDependencies": {
57
- "@better-auth/expo": "^1.6.23",
57
+ "@better-auth/expo": "^1.7.1",
58
58
  "@tanstack/react-query": "^5.101.0",
59
- "better-auth": "^1.6.23",
59
+ "better-auth": "^1.7.1",
60
60
  "react": "^19.2.7"
61
61
  },
62
62
  "peerDependenciesMeta": {
@@ -1,36 +0,0 @@
1
- import { LunoraClient, createAsyncStoragePersistence } from '@lunora/client';
2
-
3
- const withAuthHeaders = (fetchImpl, getAuthHeaders) => (input, init) => {
4
- const extra = getAuthHeaders();
5
- if (!extra) {
6
- return fetchImpl(input, init);
7
- }
8
- const merged = new Headers(extra);
9
- if (init?.headers) {
10
- new Headers(init.headers).forEach((value, key) => {
11
- merged.set(key, value);
12
- });
13
- }
14
- return fetchImpl(input, { ...init, headers: merged });
15
- };
16
- const withAuthWebSocket = (WebSocketImpl, getAuthHeaders) => class AuthWebSocket extends WebSocketImpl {
17
- constructor(url, protocols) {
18
- const headers = getAuthHeaders();
19
- super(url, protocols, headers ? { headers } : void 0);
20
- }
21
- };
22
- const createLunoraClient = (options) => {
23
- const { getAuthHeaders, storage, ...rest } = options;
24
- const authedFetch = getAuthHeaders && rest.fetch === void 0 && typeof fetch === "function" ? withAuthHeaders(fetch.bind(globalThis), getAuthHeaders) : rest.fetch;
25
- const authedWebSocket = getAuthHeaders && rest.WebSocket === void 0 && typeof WebSocket === "function" ? withAuthWebSocket(WebSocket, getAuthHeaders) : rest.WebSocket;
26
- return new LunoraClient({
27
- ...rest,
28
- // Auto-wire AsyncStorage persistence unless the caller passed an explicit
29
- // `persistence` (including `false` to opt out).
30
- persistence: rest.persistence ?? (storage ? createAsyncStoragePersistence({ storage }) : void 0),
31
- fetch: authedFetch,
32
- WebSocket: authedWebSocket
33
- });
34
- };
35
-
36
- export { createLunoraClient, withAuthHeaders, withAuthWebSocket };
@@ -1,7 +0,0 @@
1
- const SESSION_TOKEN_COOKIE = /(?:^|;)[^;=]*session_token=([^;]+)/;
2
- const expoBearerToken = (authClient) => {
3
- const match = SESSION_TOKEN_COOKIE.exec(authClient.getCookie());
4
- return match?.[1] ?? null;
5
- };
6
-
7
- export { expoBearerToken as default };