better-near-auth 1.4.1 → 1.4.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-near-auth",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Sign in with NEAR (SIWN) plugin for Better Auth",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",
@@ -60,13 +60,19 @@ import { useRouter } from "@tanstack/react-router";
60
60
  import { useQuery } from "@tanstack/react-query";
61
61
  import type { Auth } from "./auth-types.gen";
62
62
  import { getAccount, getHostUrl, getNetworkId } from "@/app";
63
+ import type { ClientRuntimeConfig } from "./app";
63
64
 
64
- export function createAuthClient() {
65
+ interface AuthClientOpts {
66
+ runtimeConfig?: Partial<ClientRuntimeConfig>;
67
+ }
68
+
69
+ export function createAuthClient(opts?: AuthClientOpts) {
70
+ const config = opts?.runtimeConfig;
65
71
  return createBetterAuthClient({
66
- baseURL: getHostUrl(),
72
+ baseURL: getHostUrl(config),
67
73
  fetchOptions: { credentials: "include" },
68
74
  plugins: [
69
- siwnClient({ recipient: getAccount(), networkId: getNetworkId() }),
75
+ siwnClient({ recipient: getAccount(config), networkId: getNetworkId(config) }),
70
76
  ],
71
77
  });
72
78
  }
@@ -82,7 +88,7 @@ export function useAuthClient(): AuthClient {
82
88
  The auth client is created once in the router setup (not per component call) and accessed via context:
83
89
 
84
90
  ```typescript
85
- // hydrate.tsx
91
+ // hydrate.tsx — browser, no runtimeConfig needed (reads window.__RUNTIME_CONFIG__)
86
92
  import { createAuthClient } from "./auth";
87
93
 
88
94
  const { router } = createRouter({
@@ -91,23 +97,19 @@ const { router } = createRouter({
91
97
  },
92
98
  });
93
99
 
94
- // router.server.tsx — same pattern
100
+ // router.server.tsx — server, MUST pass runtimeConfig
95
101
  context: {
96
- authClient: createAuthClient(),
102
+ authClient: createAuthClient({ runtimeConfig: renderOptions.runtimeConfig }),
97
103
  }
98
104
  ```
99
105
 
100
106
  ## Type Inference from AuthClient
101
107
 
102
- Don't manually define `Organization`, `Passkey`, or other entity types. Infer them from the client's API responses:
108
+ Don't manually define `Organization`, `Passkey`, or other entity types. Use `$Infer` to get them directly from the auth client's type system:
103
109
 
104
110
  ```typescript
105
- type UnwrapListResponse<T> = T extends (...args: any[]) => Promise<{
106
- data: (infer U)[] | null; error: any
107
- }> ? U : never;
108
-
109
- export type Organization = UnwrapListResponse<AuthClient["organization"]["list"]>;
110
- export type Passkey = UnwrapListResponse<AuthClient["passkey"]["listUserPasskeys"]>;
111
+ export type Organization = AuthClient["$Infer"]["Organization"];
112
+ export type Passkey = AuthClient["$Infer"]["Passkey"];
111
113
  ```
112
114
 
113
115
  This automatically includes any additional fields the server configured. Single source of truth: the `AuthClient` type, which is itself derived from the plugin list.
@@ -217,7 +219,15 @@ This means UI can display the user's NEAR account even when the wallet is discon
217
219
 
218
220
  ## SSR Safety
219
221
 
220
- `siwnClient()` is SSR-safe — wallet resources are lazily initialized on first client-side access. On the server they sit dormant. This means `createAuthClient()` can safely run in `router.server.tsx` without accessing browser APIs.
222
+ `siwnClient()` is SSR-safe — wallet resources are lazily initialized on first client-side access. On the server they sit dormant. However, `createAuthClient()` calls `getHostUrl()`, `getAccount()`, and `getNetworkId()`, which read `window.__RUNTIME_CONFIG__` by default. On the server, you **must** pass `{ runtimeConfig }` so these helpers read from the provided config instead of the browser-only `window` object:
223
+
224
+ ```typescript
225
+ // Server (router.server.tsx) — MUST pass runtimeConfig
226
+ createAuthClient({ runtimeConfig: renderOptions.runtimeConfig })
227
+
228
+ // Client (hydrate.tsx) — no config needed, reads window.__RUNTIME_CONFIG__
229
+ createAuthClient()
230
+ ```
221
231
 
222
232
  Methods that work on server (via `$fetch` only): `nonce`, `verify`, `view`, `relayTransaction`, `getRelayStatus`, `getRelayerInfo`, `relayHistory`, `getProfile`, `listAccounts`.
223
233
 
@@ -261,9 +271,10 @@ export const authClient = createAuthClient({
261
271
  });
262
272
 
263
273
  // OR: Router context singleton (SSR)
264
- export function createAuthClient() {
274
+ export function createAuthClient(opts?: AuthClientOpts) {
275
+ const config = opts?.runtimeConfig;
265
276
  return createBetterAuthClient({
266
- plugins: [siwnClient({ recipient: getAccount() })],
277
+ plugins: [siwnClient({ recipient: getAccount(config) })],
267
278
  });
268
279
  }
269
280
  // Create once in router setup, access via useAuthClient()
@@ -346,14 +357,35 @@ Correct (SSR):
346
357
 
347
358
  ```typescript
348
359
  // Factory — one instance per router/request
349
- export function createAuthClient() {
360
+ export function createAuthClient(opts?: AuthClientOpts) {
361
+ const config = opts?.runtimeConfig;
350
362
  return createBetterAuthClient({
351
- plugins: [siwnClient({ recipient: getAccount() })],
363
+ plugins: [siwnClient({ recipient: getAccount(config) })],
352
364
  });
353
365
  }
354
- // Created in createRouter() context
366
+ // Created in createRouter() context with runtimeConfig
355
367
  ```
356
368
 
357
369
  On the server, a module-level singleton's `$fetch` and session state would be shared across concurrent requests. Router context isolates one client per request on server, one per app on client.
358
370
 
359
371
  Source: router.server.tsx:60-71
372
+
373
+ ### MEDIUM Calling createAuthClient() without runtimeConfig on the server
374
+
375
+ Wrong:
376
+
377
+ ```typescript
378
+ // router.server.tsx — throws "Runtime config is only available in the browser"
379
+ authClient: createAuthClient(),
380
+ ```
381
+
382
+ Correct:
383
+
384
+ ```typescript
385
+ // router.server.tsx — pass runtimeConfig from renderOptions
386
+ authClient: createAuthClient({ runtimeConfig: renderOptions.runtimeConfig }),
387
+ ```
388
+
389
+ `getHostUrl()`, `getAccount()`, and `getNetworkId()` read `window.__RUNTIME_CONFIG__` by default. On the server, `window` is undefined, so they throw. Always pass `{ runtimeConfig }` when calling `createAuthClient()` in `router.server.tsx` or `getRouteHead()`.
390
+
391
+ Source: auth.ts:18-27