@terreno/mcp 57.21.0 → 58.0.0-beta.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.
Files changed (244) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +57 -3
  3. package/dist/bootstrap.d.ts.map +1 -1
  4. package/dist/bootstrap.js +17 -16
  5. package/dist/bootstrap.js.map +1 -1
  6. package/dist/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
  7. package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
  8. package/dist/docs/prompts/debug.md +6 -4
  9. package/dist/docs/prompts/screen.md +11 -17
  10. package/dist/docs/resources/overview.md +5 -3
  11. package/dist/docs/resources/patterns.md +157 -238
  12. package/dist/docs/resources/syncdb.md +44 -16
  13. package/dist/docs/templates/bootstrap/CLAUDE.template.md +2 -2
  14. package/dist/docs/templates/bootstrap/cursorrules.template.md +7 -5
  15. package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
  16. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
  17. package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
  18. package/dist/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
  19. package/dist/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
  20. package/dist/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
  21. package/dist/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
  22. package/dist/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
  23. package/dist/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
  24. package/dist/docs/upgrades/58.0.0-beta.1.md +504 -0
  25. package/dist/docs/upgrades/58.0.0.md +495 -4
  26. package/dist/docs/versioned/next/README.md +2 -2
  27. package/dist/docs/versioned/next/approval-rubric.md +2 -2
  28. package/dist/docs/versioned/next/explanation/admin-interface.md +6 -5
  29. package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
  30. package/dist/docs/versioned/next/explanation/ai-workflows.md +5 -3
  31. package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
  32. package/dist/docs/versioned/next/explanation/authentication.md +62 -43
  33. package/dist/docs/versioned/next/explanation/dependency-management.md +1 -1
  34. package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  35. package/dist/docs/versioned/next/explanation/example-coverage.md +2 -2
  36. package/dist/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
  37. package/dist/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
  38. package/dist/docs/versioned/next/explanation/local-first-data.md +11 -9
  39. package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
  40. package/dist/docs/versioned/next/explanation/modular-api-design.md +2 -2
  41. package/dist/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
  42. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
  43. package/dist/docs/versioned/next/explanation/source-rules.md +2 -2
  44. package/dist/docs/versioned/next/explanation/typescript-configuration.md +2 -4
  45. package/dist/docs/versioned/next/explanation/versioning-policy.md +3 -3
  46. package/dist/docs/versioned/next/how-to/README.md +3 -2
  47. package/dist/docs/versioned/next/how-to/add-feature-flags.md +50 -25
  48. package/dist/docs/versioned/next/how-to/add-organizations.md +2 -1
  49. package/dist/docs/versioned/next/how-to/admin-custom-home.md +8 -2
  50. package/dist/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
  51. package/dist/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
  52. package/dist/docs/versioned/next/how-to/build-admin-screens.md +10 -8
  53. package/dist/docs/versioned/next/how-to/build-for-web.md +1 -1
  54. package/dist/docs/versioned/next/how-to/call-external-apis.md +2 -2
  55. package/dist/docs/versioned/next/how-to/circleci.md +12 -6
  56. package/dist/docs/versioned/next/how-to/configure-better-auth.md +114 -60
  57. package/dist/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
  58. package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
  59. package/dist/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
  60. package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
  61. package/dist/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
  62. package/dist/docs/versioned/next/how-to/github-actions-ci.md +2 -2
  63. package/dist/docs/versioned/next/how-to/in-app-notifications.md +1 -1
  64. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
  65. package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
  66. package/dist/docs/versioned/next/how-to/product-announcements.md +6 -3
  67. package/dist/docs/versioned/next/how-to/rate-limiting.md +38 -16
  68. package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
  69. package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -3
  70. package/dist/docs/versioned/next/how-to/set-up-social-login.md +1 -1
  71. package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
  72. package/dist/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
  73. package/dist/docs/versioned/next/how-to/upgrade-banner.md +5 -3
  74. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
  75. package/dist/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  76. package/dist/docs/versioned/next/how-to/websocket-integration.md +113 -402
  77. package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  78. package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
  79. package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
  80. package/dist/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
  81. package/dist/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
  82. package/dist/docs/versioned/next/reference/README.md +1 -1
  83. package/dist/docs/versioned/next/reference/admin-frontend.md +85 -70
  84. package/dist/docs/versioned/next/reference/admin-spa.md +10 -4
  85. package/dist/docs/versioned/next/reference/ai.md +36 -4
  86. package/dist/docs/versioned/next/reference/announcements.md +3 -3
  87. package/dist/docs/versioned/next/reference/api.md +39 -24
  88. package/dist/docs/versioned/next/reference/cli.md +3 -4
  89. package/dist/docs/versioned/next/reference/environment-variables.md +16 -16
  90. package/dist/docs/versioned/next/reference/feature-flags.md +48 -7
  91. package/dist/docs/versioned/next/reference/jobs.md +2 -2
  92. package/dist/docs/versioned/next/reference/legacy/rtk.md +25 -600
  93. package/dist/docs/versioned/next/reference/mcp-server.md +441 -1158
  94. package/dist/docs/versioned/next/reference/syncdb.md +426 -16
  95. package/dist/docs/versioned/next/reference/test.md +28 -3
  96. package/dist/docs/versioned/next/reference/ui.md +132 -19
  97. package/dist/docs/versioned/next/tasks/native-module-baseline.md +8 -4
  98. package/dist/docs/versioned/next/tasks/rate-limiting.md +9 -2
  99. package/dist/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
  100. package/dist/docs/versioned/next/tasks/remove-rtk.md +155 -0
  101. package/dist/local/localTools.d.ts.map +1 -1
  102. package/dist/local/localTools.js +3 -54
  103. package/dist/local/localTools.js.map +1 -1
  104. package/dist/local/tools/runtime.d.ts +0 -21
  105. package/dist/local/tools/runtime.d.ts.map +1 -1
  106. package/dist/local/tools/runtime.js +0 -176
  107. package/dist/local/tools/runtime.js.map +1 -1
  108. package/dist/local/tools/syncDb.d.ts +0 -11
  109. package/dist/local/tools/syncDb.d.ts.map +1 -1
  110. package/dist/local/tools/syncDb.js +1 -5
  111. package/dist/local/tools/syncDb.js.map +1 -1
  112. package/dist/packageGuidelines.d.ts +3 -2
  113. package/dist/packageGuidelines.d.ts.map +1 -1
  114. package/dist/packageGuidelines.js +4 -11
  115. package/dist/packageGuidelines.js.map +1 -1
  116. package/dist/prompts.d.ts.map +1 -1
  117. package/dist/prompts.js +17 -18
  118. package/dist/prompts.js.map +1 -1
  119. package/dist/resources.d.ts.map +1 -1
  120. package/dist/resources.js +1 -8
  121. package/dist/resources.js.map +1 -1
  122. package/dist/search/chunker.d.ts +1 -1
  123. package/dist/search/chunker.d.ts.map +1 -1
  124. package/dist/search/inferPackages.d.ts +1 -1
  125. package/dist/search/inferPackages.js +5 -5
  126. package/dist/search/inferPackages.js.map +1 -1
  127. package/dist/tools.d.ts +6 -0
  128. package/dist/tools.d.ts.map +1 -1
  129. package/dist/tools.js +126 -119
  130. package/dist/tools.js.map +1 -1
  131. package/package.json +6 -6
  132. package/src/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
  133. package/src/docs/guidelines/ui/guidelines/core.md +1 -1
  134. package/src/docs/prompts/debug.md +6 -4
  135. package/src/docs/prompts/screen.md +11 -17
  136. package/src/docs/resources/overview.md +5 -3
  137. package/src/docs/resources/patterns.md +157 -238
  138. package/src/docs/resources/syncdb.md +44 -16
  139. package/src/docs/templates/bootstrap/CLAUDE.template.md +2 -2
  140. package/src/docs/templates/bootstrap/cursorrules.template.md +7 -5
  141. package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
  142. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
  143. package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
  144. package/src/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
  145. package/src/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
  146. package/src/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
  147. package/src/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
  148. package/src/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
  149. package/src/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
  150. package/src/docs/upgrades/58.0.0-beta.1.md +504 -0
  151. package/src/docs/upgrades/58.0.0.md +495 -4
  152. package/src/docs/versioned/next/README.md +2 -2
  153. package/src/docs/versioned/next/approval-rubric.md +2 -2
  154. package/src/docs/versioned/next/explanation/admin-interface.md +6 -5
  155. package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
  156. package/src/docs/versioned/next/explanation/ai-workflows.md +5 -3
  157. package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
  158. package/src/docs/versioned/next/explanation/authentication.md +62 -43
  159. package/src/docs/versioned/next/explanation/dependency-management.md +1 -1
  160. package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
  161. package/src/docs/versioned/next/explanation/example-coverage.md +2 -2
  162. package/src/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
  163. package/src/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
  164. package/src/docs/versioned/next/explanation/local-first-data.md +11 -9
  165. package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
  166. package/src/docs/versioned/next/explanation/modular-api-design.md +2 -2
  167. package/src/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
  168. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
  169. package/src/docs/versioned/next/explanation/source-rules.md +2 -2
  170. package/src/docs/versioned/next/explanation/typescript-configuration.md +2 -4
  171. package/src/docs/versioned/next/explanation/versioning-policy.md +3 -3
  172. package/src/docs/versioned/next/how-to/README.md +3 -2
  173. package/src/docs/versioned/next/how-to/add-feature-flags.md +50 -25
  174. package/src/docs/versioned/next/how-to/add-organizations.md +2 -1
  175. package/src/docs/versioned/next/how-to/admin-custom-home.md +8 -2
  176. package/src/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
  177. package/src/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
  178. package/src/docs/versioned/next/how-to/build-admin-screens.md +10 -8
  179. package/src/docs/versioned/next/how-to/build-for-web.md +1 -1
  180. package/src/docs/versioned/next/how-to/call-external-apis.md +2 -2
  181. package/src/docs/versioned/next/how-to/circleci.md +12 -6
  182. package/src/docs/versioned/next/how-to/configure-better-auth.md +114 -60
  183. package/src/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
  184. package/src/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
  185. package/src/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
  186. package/src/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
  187. package/src/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
  188. package/src/docs/versioned/next/how-to/github-actions-ci.md +2 -2
  189. package/src/docs/versioned/next/how-to/in-app-notifications.md +1 -1
  190. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
  191. package/src/docs/versioned/next/how-to/password-reset.md +1 -1
  192. package/src/docs/versioned/next/how-to/product-announcements.md +6 -3
  193. package/src/docs/versioned/next/how-to/rate-limiting.md +38 -16
  194. package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
  195. package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -3
  196. package/src/docs/versioned/next/how-to/set-up-social-login.md +1 -1
  197. package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
  198. package/src/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
  199. package/src/docs/versioned/next/how-to/upgrade-banner.md +5 -3
  200. package/src/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
  201. package/src/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
  202. package/src/docs/versioned/next/how-to/websocket-integration.md +113 -402
  203. package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
  204. package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
  205. package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
  206. package/src/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
  207. package/src/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
  208. package/src/docs/versioned/next/reference/README.md +1 -1
  209. package/src/docs/versioned/next/reference/admin-frontend.md +85 -70
  210. package/src/docs/versioned/next/reference/admin-spa.md +10 -4
  211. package/src/docs/versioned/next/reference/ai.md +36 -4
  212. package/src/docs/versioned/next/reference/announcements.md +3 -3
  213. package/src/docs/versioned/next/reference/api.md +39 -24
  214. package/src/docs/versioned/next/reference/cli.md +3 -4
  215. package/src/docs/versioned/next/reference/environment-variables.md +16 -16
  216. package/src/docs/versioned/next/reference/feature-flags.md +48 -7
  217. package/src/docs/versioned/next/reference/jobs.md +2 -2
  218. package/src/docs/versioned/next/reference/legacy/rtk.md +25 -600
  219. package/src/docs/versioned/next/reference/mcp-server.md +441 -1158
  220. package/src/docs/versioned/next/reference/syncdb.md +426 -16
  221. package/src/docs/versioned/next/reference/test.md +28 -3
  222. package/src/docs/versioned/next/reference/ui.md +132 -19
  223. package/src/docs/versioned/next/tasks/native-module-baseline.md +8 -4
  224. package/src/docs/versioned/next/tasks/rate-limiting.md +9 -2
  225. package/src/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
  226. package/src/docs/versioned/next/tasks/remove-rtk.md +155 -0
  227. package/dist/docs/guidelines/rtk/guidelines/core.md +0 -26
  228. package/dist/docs/resources/rtk.md +0 -181
  229. package/dist/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
  230. package/dist/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
  231. package/dist/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
  232. package/dist/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
  233. package/dist/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
  234. package/dist/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
  235. package/dist/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
  236. package/src/docs/guidelines/rtk/guidelines/core.md +0 -26
  237. package/src/docs/resources/rtk.md +0 -181
  238. package/src/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
  239. package/src/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
  240. package/src/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
  241. package/src/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
  242. package/src/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
  243. package/src/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
  244. package/src/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
@@ -1,600 +1,25 @@
1
- # @terreno/rtk (legacy)
2
-
3
- > **Deprecated for data synchronization.** `@terreno/rtk` remains published through the **56.x** line (and the stable **0.x** line) with a deprecation notice. It will **not** be published in the next major Terreno release. For collection reads/writes, offline sync, and realtime convergence, use [`@terreno/syncdb`](../syncdb.md) and the [migration guide](../../how-to/migrate-rtk-to-syncdb.md).
4
- >
5
- > RTK Query remains the correct tool for **non-synced** generated OpenAPI routes
6
- > such as `/auth/me` and AI APIs, Better Auth session Redux wiring, feature
7
- > flags, and sockets. Built-in admin RPC uses host-bound fetch; ObjectId/API-only
8
- > admin CRUD keeps RTK compatibility in Terreno 57, then `useAdminApi` and the
9
- > required admin `api` prop are removed in Terreno 58.
10
-
11
- Redux Toolkit Query utilities for frontends using @terreno/api backends. JWT auth, token storage, SDK code generation from OpenAPI, and real-time WebSocket connections.
12
-
13
- ## Table of Contents
14
-
15
- - [Authentication](#authentication)
16
- - [WebSocket Integration](#websocket-integration)
17
- - [Cache Management](#cache-management)
18
- - [Token Management](#token-management)
19
- - [SDK Generation](#sdk-generation)
20
- - [Debugging](#debugging)
21
- - [Platform Detection](#platform-detection)
22
-
23
- ## Authentication
24
-
25
- ### generateAuthSlice
26
-
27
- Creates a complete Redux auth system with JWT token management, automatic token refresh, and secure storage.
28
-
29
- ``````typescript
30
- import {generateAuthSlice} from "@terreno/rtk";
31
- import {configureStore} from "@reduxjs/toolkit";
32
- import {openapi} from "./openApiSdk";
33
-
34
- const {authReducer, logout, setUserId, middleware} = generateAuthSlice(openapi);
35
-
36
- export const store = configureStore({
37
- reducer: {
38
- auth: authReducer,
39
- [openapi.reducerPath]: openapi.reducer,
40
- },
41
- middleware: (getDefault) =>
42
- getDefault().concat(openapi.middleware, ...middleware),
43
- });
44
- ``````
45
-
46
- **Returns:**
47
- - `authReducer` — Redux reducer for auth state
48
- - `authSlice` — Full Redux slice
49
- - `logout` — Action to clear tokens and reset state. A logout that ends a signed-in session also purges the API's RTK Query cache (`resetApiState`), so the next user never gets the previous user's cached responses
50
- - `setUserId` — Action to set current user ID
51
- - `tokenRefreshedSuccess` — Action signaling token refresh
52
- - `middleware` — Login/logout listener middleware array
53
-
54
- **Auth State:**
55
- ``````typescript
56
- {
57
- userId: string | null;
58
- error: string | null;
59
- lastTokenRefreshTimestamp: number | null;
60
- }
61
- ``````
62
-
63
- **Built-in Endpoints:**
64
- - `emailLogin` — POST `/auth/login`
65
- - `emailSignUp` — POST `/auth/signup`
66
- - `googleLogin` — POST `/auth/google`
67
- - `createEmailUser` — Create user without login (admin use)
68
- - `resetPassword` — POST `/resetPassword`
69
-
70
- **Token Storage:**
71
- - **Native (iOS/Android):** Secure encrypted storage via `expo-secure-store`
72
- - **Web:** `@react-native-async-storage/async-storage` with SSR safety
73
- - Automatic storage/retrieval on login/logout via listener middleware
74
-
75
- **Selectors:**
76
- ``````typescript
77
- import {selectCurrentUserId, useSelectCurrentUserId} from "@terreno/rtk";
78
-
79
- // Hook version
80
- const userId = useSelectCurrentUserId();
81
-
82
- // Selector version
83
- const userId = selectCurrentUserId(state);
84
- ``````
85
-
86
- ### Better Auth Integration
87
-
88
- For apps using Better Auth instead of JWT:
89
-
90
- ``````typescript
91
- import {configureStore} from "@reduxjs/toolkit";
92
- import {createBetterAuthClient, generateBetterAuthSlice} from "@terreno/rtk";
93
- import {api} from "./sdk"; // the app's generated RTK Query API
94
-
95
- // Create Better Auth client. `scheme` is the app's deep-link scheme (required).
96
- export const authClient = createBetterAuthClient({
97
- baseURL: process.env.EXPO_PUBLIC_API_URL || "http://localhost:4000",
98
- scheme: "myapp",
99
- });
100
-
101
- // Generate the session Redux slice; list the APIs whose cache belongs to the signed-in user
102
- const betterAuth = generateBetterAuthSlice({apisToReset: [api], authClient});
103
-
104
- export const store = configureStore({
105
- reducer: {
106
- betterAuth: betterAuth.reducer, // selectors read `state.betterAuth`
107
- // ... other reducers
108
- },
109
- middleware: (getDefault) => getDefault().concat(...betterAuth.middleware),
110
- });
111
-
112
- // Call after sign-in, sign-out, and at app startup to copy the session into Redux
113
- void betterAuth.syncSession(store.dispatch);
114
- ``````
115
-
116
- **Key exports:**
117
- - `createBetterAuthClient({baseURL, scheme, storagePrefix?, webAuth?, bearerStorage?, features?})` — Factory for Better Auth client with Expo support. `webAuth: "bearer"` authenticates the web client with a stored bearer token and no cookies, for a frontend and API on different sites ([how-to](../../how-to/configure-better-auth.md#frontend-and-api-on-different-sites)). `features` (`BetterAuthClientFeatures`: `twoFactor?: boolean | {onTwoFactorRedirect}`, `passkey?`, `multiSession?`, `lastLoginMethod?`, `oneTimeToken?`) adds `twoFactorClient`, `passkeyClient` (`@better-auth/passkey/client`), `multiSessionClient`, `lastLoginMethodClient` and `oneTimeTokenClient` after the Expo plugin, and types their namespaces (`client.twoFactor.enable`, `client.passkey.addPasskey`, `client.multiSession.listDeviceSessions`, `client.getLastUsedLoginMethod`, `client.oneTimeToken.verify`). Without `features`, or when its type does not name the features (a config typed `BetterAuthClientConfig`), the return type is `BetterAuthClient`, unchanged. In bearer mode `multiSession` lists no sessions and 2FA and passkey challenges need cookies ([how-to](../../how-to/configure-better-auth.md#client-features))
118
- - `BetterAuthClient` — The client type without `features` (also `ReturnType<typeof createBetterAuthClient>`). `BetterAuthClientWithFeatures<Features>` — the client type with them. `BetterAuthClientFor<Features>` — which of the two `createBetterAuthClient` returns. `BetterAuthHandoffClient` — what the OAuth handoff helpers accept: any client from `createBetterAuthClient`
119
- - `BetterAuthOAuthProvider` — `"google" | "github" | "apple"` or any generic OAuth provider id, such as `"mock-oidc"`
120
- - `getBetterAuthBearerToken({storagePrefix?, bearerStorage?})` — Reads the token bearer mode stored, or `null`
121
- - `signInSocialWithHandoff({client, provider, callbackURL?, errorCallbackURL?})` — Starts a social or generic OAuth sign-in for a web bearer client: stores a random nonce in `sessionStorage` and navigates the page to the server's `{basePath}/oauth-handoff/start` with `handoff=<nonce>` on the callback URLs. Throws on native and in cookie mode ([how-to](../../how-to/configure-better-auth.md#social-sign-in-in-bearer-mode))
122
- - `completeBetterAuthOAuthHandoff({client}): Promise<boolean>` — On the return page, removes `#ott` and `handoff` from the URL first, then exchanges the token at `{basePath}/one-time-token/verify` only when `handoff` matches the stored nonce. Resolves `true` only when a bearer token was stored; never rejects. Await it at startup before session reads and Sentry
123
- - `linkSocialWithHandoff({client, provider, callbackURL, errorCallbackURL?}): Promise<{error}>` — Links a provider to the signed-in user. On web in bearer mode it posts the intent to `{basePath}/oauth-handoff/link-intent` with the bearer, then submits the returned single-use token in a hidden POST form to `startURL`, which redirects to the provider; the page comes back to `callbackURL` with no `#ott`. Both URLs may be any page on a web origin (`callbackPaths` do not apply); `errorCallbackURL` defaults to `callbackURL`. In cookie mode and on native it calls `client.linkSocial`. Resolves `{error: null}` once the browser is on its way, or `{error: {status, code?, message?}}` (such as 403 `LINKING_DISABLED`) without submitting anything. `BetterAuthLinkClient` is its client type ([how-to](../../how-to/configure-better-auth.md#linking-a-provider-in-bearer-mode))
124
- - `useAuthMethods(authClient, {skip?}): {methods, isLoading, error, refetch}` — Loads the server's enabled sign-in methods (`BetterAuthMethods`: `providers`, `emailPassword`, `signUp`, `accountLinking`, and feature flags) once per client from `{basePath}/terreno/methods` through the client's `$fetch`, so bearer and cookie modes both work. `methods` is `undefined` while loading and after an error; render no provider buttons until it arrives. `refetch()` requests them again (for Try again); `skip: true` makes no request ([response](../api.md#better-auth-authentication))
125
- - `createRememberedAccountsStore({rememberIdentity, storageKey?, max?, storage?})` — On-device store of accounts that signed in here, for the login screen. Returns `{record(entry), forget(userId), clear(), list(), lastUsedMethod(), isHydrated(), ready, getSnapshot(), subscribe(listener), dispose()}`. Each change re-reads storage first, and on web the store follows `storage` events for its key until `dispose()`, so a stale tab cannot bring back a forgotten account. A failed change rejects to its caller without blocking later changes. Persists `{userId, method, lastUsedAt}` (newest first, at most `max`, default 5, and at most 2048 bytes) under `storageKey` (default `terreno_remembered_accounts`) in SecureStore on native (`WHEN_UNLOCKED_THIS_DEVICE_ONLY` on iOS) or localStorage on web. `rememberIdentity` is required: pass `false` to keep only the method. `name` (in clear), a masked `email`, and an `https://` `image` are kept only with `rememberIdentity: true`. Unreadable or oversized stored data resets to an empty list ([how-to](../../how-to/configure-better-auth.md#4-remember-accounts-on-this-device))
126
- - `useRememberedAccounts(store): {accounts, lastUsedMethod, isHydrated, forget, clear}` — Subscribes a component to the store. `isHydrated` is `false` until native storage has loaded
127
- - `toLoginAccountOptions(accounts)` — Maps `RememberedAccount[]` to `@terreno/ui` `LoginAccountOption[]` (`id` is the `userId`) for `LoginScreen`'s `rememberedAccounts`
128
- - `maskEmail(email)` — `josh@mail.flourish.health` → `j***@m***.health`: first character of the local part and domain, subdomains hidden, top-level domain kept; `undefined` for non-emails
129
- - `getBetterAuthClientWebAuth(client): {webAuth, bearerToken}` — Whether a client from `createBetterAuthClient` is in web bearer mode, and the token it holds; `{webAuth: "cookie", bearerToken: null}` on native, in cookie mode, and for other clients. No request
130
- - `useLoginController({client, rememberedAccounts, onSignedIn, onTwoFactorRequired?, rememberIdentity?, callbackURL?, errorCallbackURL?, error?, onSignUpPress?, onForgotPasswordPress?, providerLabels?, passkeyAutofill?, skipWhenSignedIn?, webAuth?, getBearerToken?, initialEmail?})` — Drives a login screen from a Better Auth client. Returns `{step, methods, isEmailPasswordEnabled, screenProps, twoFactor, signInEmail, signInSocial, signInPasskey, continueAs, forget}`: `step` is `"checking"`, `"login"`, `"twoFactor"` or `"signedIn"`; `isEmailPasswordEnabled` is `false` when the server has email and password off (render `LoginScreen` with `fields={[]}`); `screenProps` spreads into `@terreno/ui` `LoginScreen`; `twoFactor` feeds `TwoFactorChallengeScreen`. A live session calls `onSignedIn` without showing the screen (in bearer mode it first finishes a pending OAuth handoff; with no stored token it asks the server nothing). If `onSignedIn` throws, `step` returns to `"login"` with an error. The passkey button and autofill are web-only. Device sessions (`multiSession.listDeviceSessions`, never in bearer mode) become session cards and hide remembered entries for the same user. Social sign-in uses `signInSocialWithHandoff` in bearer mode and `signIn.social` otherwise. Every finished sign-in is recorded in the store. Server refusals become notices through `getLoginFeedback` ([how-to](../../how-to/configure-better-auth.md#5-drop-in-login-screen))
131
- - `TerrenoLoginScreen` — `LoginScreen` connected through `useLoginController`; takes its options plus `fields?` (default `DEFAULT_LOGIN_FIELDS`), `title?`, `logo?`, `trustDeviceDays?`, `initialTrustDevice?` (pass `false` to start "Trust this device" unchecked), `onStepChange?` (called with each new step: `checking`, `login`, `twoFactor`, `signedIn`), `testID?` (default `login-screen`; spinner `${testID}-checking`). Renders the inline two-factor challenge unless `onTwoFactorRequired` is set
132
- - `useAccountSecurity({client, callbackURL, methods?, errorCallbackURL?, linkError?, providerLabels?, passkeySupported?, minPasswordLength?})` — Drives an account security screen from a Better Auth client. Returns `{screenProps, methods, user, refetch}`; `screenProps` (`linkedAccounts`, `password`, `passkeys`, `twoFactor`, `sessions`, `isLoading`, `error`, `onRetry`) spreads into `@terreno/ui` `AccountSecurityScreen`. A card's props are `undefined` when the server's methods (or the client's features) leave it off; passkeys are web-only. Loads `listAccounts`, `passkey.listUserPasskeys`, `getSession({query: {disableCookieCache: true}})` and `listSessions`; each Better Auth `{error}` becomes a thrown `Error` for the card, and after each change only the affected section reloads. Never calls `unlinkAccount` for the only account, or `deletePasskey` for the only sign-in method (`LAST_SIGN_IN_METHOD_MESSAGE`). Links through `linkSocialWithHandoff`; "Set a password" posts `{basePath}/terreno/set-password`. `SESSION_NOT_FRESH` becomes `SIGN_IN_AGAIN_MESSAGE`; a change refused with `CREDENTIAL_ACCOUNT_NOT_FOUND` switches the password card to set mode (`NO_PASSWORD_YET_MESSAGE`). `getLinkErrorMessage(code)` maps a link's `?error=` to safe text ([how-to](../../how-to/configure-better-auth.md#6-drop-in-security-screen))
133
- - `TerrenoAccountSecurityScreen` — `AccountSecurityScreen` connected through `useAccountSecurity`; takes its options plus `title?`, `description?`, `header?`, `children?`, `withPage?`, `maxWidth?`, `testID?`
134
- - `recordRememberedSignIn({store, user, method, rememberIdentity?})` — Records a sign-in the controller did not finish, such as one completed on the app's own two-factor screen
135
- - `getLoginFeedback({failure, mode?, originalMethod?}): {notice?, error?, isSignUpClosed}` — Maps `{code?, message?, source: "response" | "url"}` to login screen feedback: `account_not_linked` / `OAUTH_LINK_ERROR` / "account not linked" to guidance for the linking mode, `VERIFY_EMAIL_TO_USE_ACCOUNT` to "verify your email", `signup_disabled` / `EMAIL_PASSWORD_SIGN_UP_DISABLED` to "ask your administrator" (and `isSignUpClosed`), `LINKING_DISABLED`, `provider_not_found`; a response's own message otherwise, and never text from a URL
136
- - `generateBetterAuthSlice({authClient, apisToReset?, syncInterval?, signOutTimeoutMs?})` — Returns `{reducer, middleware, actions, slice, syncSession, authClient}`. Pass the app's RTK Query APIs (for example `apisToReset: [terrenoApi]`) so their caches are purged (`util.resetApiState()`) when a signed-in session logs out (`betterAuth/logout` purges once the session clears, so a sign-out that fails keeps both; `auth/logout` purges at once and again when the session clears), when `syncSession` finds the session gone, and when the session changes to another user. Without it, the next account on the same client can be served the previous account's cached responses. `actions.logout()` keeps the session in the state with `isSigningOut: true` (`isLoading` unchanged) while the middleware calls `authClient.signOut()` (`signOutTimeoutMs`, default 10000), then dispatches `clearSession`. A sign-out that fails or times out asks `getSession`: a session that is still live, or a cookie or native client the server cannot be asked about, stays signed in with `error` set to `SIGN_OUT_FAILED_MESSAGE` ("Couldn't sign out. Check your connection and try again."); a web bearer client, which drops its token before the request leaves, ends signed out. The global `auth/logout` action (a refused session) always ends signed out. Either sign-out leaves alone a session for another user that signed in while it was in flight; a sign-out that started signed out asks `getSession` first, and clears a session that landed meanwhile unless the server still has it
137
- - `selectBetterAuthIsAuthenticated`, `selectBetterAuthUserId`, `selectBetterAuthUser`, `selectBetterAuthIsLoading`, `selectBetterAuthIsSigningOut`, `selectBetterAuthError` — Selectors over `state.betterAuth`
138
- - `BetterAuthUser`, `BetterAuthState` — TypeScript types for session/user data
139
-
140
- **Session State (`state.betterAuth`):**
141
- ``````typescript
142
- {
143
- isAuthenticated: boolean;
144
- userId: string | null;
145
- user: BetterAuthUser | null;
146
- isLoading: boolean;
147
- isSigningOut: boolean;
148
- error: string | null;
149
- lastSyncTimestamp: number | null;
150
- }
151
- ``````
152
-
153
- **Learn more:** [Configure Better Auth](../../how-to/configure-better-auth.md)
154
-
155
- ### Token source
156
-
157
- rtk reads the request token from one registered **token source**, typed as syncdb's
158
- `AuthProvider` (`getToken`, `getUserId`, `onAuthChange`, optional `refresh`). A Better Auth
159
- app registers the same adapter it builds for syncdb, once at startup:
160
-
161
- ``````typescript
162
- import {setTokenSource} from "@terreno/rtk";
163
- import {betterAuthAdapter, bridgeBetterAuthReactClient} from "@terreno/syncdb";
164
- import {authClient} from "./betterAuth";
165
-
166
- export const authProvider = betterAuthAdapter(bridgeBetterAuthReactClient(authClient));
167
- setTokenSource(authProvider); // before the first RTK Query request
168
- ``````
169
-
170
- With `webAuth: "bearer"`, also pass the adapter `getBearerToken` so it reads the stored token
171
- without a `get-session` call ([syncdb adapter](../syncdb.md#createsyncdb-configuration)).
172
-
173
- **Exports:**
174
- - `setTokenSource(source | undefined)` — Registers the source. `undefined` restores the default.
175
- - `getTokenSource()` — The registered source, or `undefined` when none is registered.
176
- - `jwtTokenSource` — The legacy JWT source: `getToken` is `getAuthToken`, `refresh` runs
177
- `refreshAuthToken` and resolves `true` on success, `getUserId` reads the stored JWT's `id`
178
- (or `sub`) claim, and `onAuthChange` fires on JWT login, logout, and token refresh.
179
- - `isJwtTokenSource(source)` — `true` for `undefined` or `jwtTokenSource`.
180
-
181
- **Base query behavior (`emptySplitApi` / `staggeredBaseQuery`):**
182
- - **No source registered, or `jwtTokenSource`:** the JWT path below, unchanged. JWT apps need
183
- no code change.
184
- - **Any other source:** sends `authorization: Bearer <token>` from `getToken()` on every
185
- request, or no `authorization` header when the token is null. It never logs out because a
186
- token is missing. On a 401 it calls `refresh()` once (shared by concurrent requests), re-reads
187
- the token, and retries once; only a second 401 dispatches `auth/logout`. The public endpoints
188
- `emailLogin`, `emailSignUp`, `getVersionCheck`, `getZoomSignature`, and `googleLogin` skip
189
- the refresh and retry.
190
-
191
- The registry is module-global, like `baseUrl`: one app, one source. Apps that forked the base
192
- query to read the Better Auth session can point their SDK back at `emptySplitApi` once they
193
- register the source.
194
-
195
- **Other consumers.** The socket, chat, MCP, and offline APIs read the same source. Each takes a
196
- `tokenSource` option that overrides it for that one consumer. Without the option they use the
197
- registered source, else `jwtTokenSource`. Pass `useSocketConnection` a stable object, not a new
198
- one per render, or it re-subscribes to `onAuthChange`.
199
-
200
- | API | Option | How it uses the source |
201
- | --- | --- | --- |
202
- | `useSocketConnection` | `tokenSource`, used when `getAuthToken` is omitted | Reads `getToken()` on every connect and reconnect, refreshes once per auth failure, and reconnects when `onAuthChange` brings a new token. See [useSocketConnection](#usesocketconnection). |
203
- | `useTerrenoChat` | `tokenSource` | Reads `getToken()` per chat request. Sends no `Authorization` header when it returns null. |
204
- | `useMCPTools` | `tokenSource` | Reads `getToken()` before every MCP request, so a refreshed token needs no refetch. A fetch that starts with no token connects without auth. |
205
- | `createOfflineMiddleware` | `tokenSource` | With a non-JWT source, tags queued mutations with `getUserId()` and replays them with `getToken()`. See [Offline queue](#offline-queue). |
206
-
207
- #### Offline queue
208
-
209
- `createOfflineMiddleware({endpoints, api, tokenSource?})` queues the listed mutations while
210
- offline and replays them on reconnect.
211
-
212
- - **JWT source:** the queue reads `state.auth.userId` and the stored JWT, as before.
213
- - **Any other source** (for example syncdb's `betterAuthAdapter`): each queued mutation carries
214
- `userId` from `getUserId()`. Replay runs only while that same user is signed in. It sends
215
- `authorization: Bearer <token>` from `getToken()`, or no header when the token is null.
216
- - **Last user id:** `state.offline.lastUserId` holds the last id the source confirmed. It
217
- refreshes on the first action, on rehydrate, on `onAuthChange`, and on reconnect, so a failed
218
- session read while offline does not untag new mutations.
219
- - **Persist the `offline` slice** with redux-persist (do not blacklist it). Otherwise mutations
220
- made after an app launches offline carry no user and are dropped on reconnect.
221
- - **Unconfirmed ids:** a rehydrated id is unconfirmed until a read in this process returns a
222
- user. Mutations tagged with it carry `userIdConfirmed: false`. A later read keeps them only
223
- when it names the same user and no `onAuthChange` or source swap happened since. Any auth
224
- change drops them, even for the same user.
225
- - **Cost:** syncdb's `betterAuthAdapter` fires `onAuthChange` on the session's first successful
226
- load. After an offline launch that happens when the network returns, so it drops
227
- offline-launch writes the reconnect read has not confirmed yet.
228
- - **Residual risk:** the signed-in user changes while no running instance of the middleware sees
229
- it (for example in another browser tab that shares the persisted slice), between an
230
- unconfirmed write and the next launch's first read. Then that write can replay as a user who
231
- did not make it.
232
- - **Sign-out:** `auth/logout`, `betterAuth/logout` (on a non-JWT source), or the source
233
- reporting no user after `onAuthChange` clears the queue, conflicts, and `lastUserId`, and
234
- discards in-flight enqueues and replays.
235
- - **No user on reconnect:** when the source has no user, or the user changes during a replay,
236
- the rest of the queue stays for a later reconnect.
237
- - **Different user on reconnect:** a replay drops each mutation queued by a user other than the
238
- one signed in.
239
-
240
- Offline-slice exports the middleware uses for the user tag (exported from the package root):
241
-
242
- - `setLastUserId(userId | undefined)` — Action that sets `state.offline.lastUserId`.
243
- - `resolveUnconfirmedMutations(userId)` — Action that keeps unconfirmed mutations tagged with
244
- `userId`, marks them confirmed, and drops the other unconfirmed ones.
245
- - `dropUnconfirmedMutations()` — Action that drops every mutation whose user id is unconfirmed.
246
- - `selectLastUserId(state)` — The last user id the source confirmed, or `undefined` when unknown
247
- or signed out.
248
-
249
- ## WebSocket Integration
250
-
251
- > **Deprecated (removed in Terreno 58):** `realtimeList`, `realtimeDocument`, `setRealtimeSocket`, and `getRealtimeSocket` patch RTK Query caches from `sync` events. Use [`@terreno/syncdb`](../syncdb.md). `useSocketConnection` remains for feature flags and other sockets.
252
-
253
- A `realtimeList` query subscription (`subscribe:query`) may filter only on fields in the router's
254
- `queryFields`, the same as the REST list; the server ignores a subscription that names any other
255
- field. On RBAC routers with field views, each subscriber's create, update, or delete event is
256
- decided against the document as their field view masks it, so a hidden field never changes which
257
- event they receive.
258
-
259
- ### useSocketConnection
260
-
261
- React hook for managing Socket.io connections with automatic reconnection, token refresh, and user feedback.
262
-
263
- ``````typescript
264
- import {useSocketConnection} from "@terreno/rtk";
265
-
266
- // Reads the token source registered with setTokenSource (the JWT in storage by default).
267
- const {socket, isSocketConnected} = useSocketConnection({
268
- baseUrl: "wss://api.example.com",
269
- shouldConnect: !!userId,
270
- onConnect: () => console.info("WebSocket connected"),
271
- onDisconnect: () => console.warn("WebSocket disconnected"),
272
- onConnectError: (error) => console.error("Connection error:", error),
273
- captureEvent: (eventName, data) => analytics.track(eventName, data),
274
- });
275
-
276
- // Use socket for real-time events
277
- useEffect(() => {
278
- if (!socket) return;
279
-
280
- socket.on("notification", (data) => {
281
- console.info("Received notification:", data);
282
- });
283
-
284
- return () => {
285
- socket.off("notification");
286
- };
287
- }, [socket]);
288
- ``````
289
-
290
- **Options:**
291
- - `baseUrl` (string, required) — WebSocket server URL
292
- - `shouldConnect` (boolean, required) — Whether to connect (typically `!!userId`)
293
- - `tokenSource` (`AuthProvider`) — Source for the handshake token. Defaults to the
294
- [registered source](#token-source), else `jwtTokenSource`. Used only when `getAuthToken` is
295
- omitted.
296
- - `getAuthToken` (function) — Legacy getter. When passed, the hook reads the token once per
297
- connect, sends it as a static `auth` payload, and ignores `tokenSource`.
298
- - `onConnect` (function) — Callback on successful connection
299
- - `onDisconnect` (function) — Callback on disconnection
300
- - `onConnectError` (function) — Callback on connection error
301
- - `onReconnectFailed` (function) — Callback after all reconnection attempts fail
302
- - `captureEvent` (function) — Analytics event tracking (optional)
303
- - `transports` (string[]) — Socket.IO transports in order. Default `["polling", "websocket"]`. Pass `["websocket"]` for hosts without session affinity, such as Vercel Functions ([details](../../how-to/vercel-function-websockets.md))
304
-
305
- **Returns:**
306
- - `socket` (Socket | null) — Socket.io client instance
307
- - `isSocketConnected` (object) — Connection state with `{isConnected: boolean, lastDisconnectedAt: string | null}`
308
-
309
- **Features:**
310
- - **Automatic reconnection:** 5 attempts with exponential backoff (1-5 seconds)
311
- - **Bearer token authentication:** Sends `Bearer <token>` in `socket.auth`
312
- - **Token refresh integration:** Reconnects automatically when tokens are refreshed
313
- - **User feedback:** Toast notifications for disconnections (after 9+ seconds) and token errors
314
- - **Connection monitoring:** Periodic checks with automatic reconnection attempts
315
- - **SSR-safe:** Checks `typeof window` before initialization
316
-
317
- **Toast Behavior:**
318
- - **Disconnection:** Shows "You have been disconnected. Attempting to reconnect..." after 9 seconds
319
- - **Reconnection:** Shows "You have been reconnected" (suppressed if reconnect within 10 seconds)
320
- - **Token error:** Shows "Error refreshing token. Please log out and log back in..." with persistent error state
321
-
322
- **Token source behavior (no `getAuthToken`):**
323
- - The handshake reads `getToken()` on every connect and reconnect, including socket.io's own
324
- reconnects.
325
- - On an auth `connect_error` (the server's `UnauthorizedError`), the hook calls `refresh()`
326
- once and reconnects. A second auth failure before a successful connect stops reconnecting and
327
- calls `onConnectError`. If the source has no `refresh`, or it fails, the hook stops at once.
328
- A successful connect resets this.
329
- - While `shouldConnect` is true, an `onAuthChange` with a different token drops a live
330
- connection and reconnects with the new token. With no token it disconnects. The same token
331
- keeps the connection up.
332
-
333
- **JWT token management** (`getAuthToken` passed, or the JWT source):
334
- - Checks token expiration on disconnect and connection errors
335
- - Automatically refreshes tokens if expiring within 60 seconds
336
- - Attempts reconnection after successful token refresh
337
- - Tracks refresh events via Redux state (`lastTokenRefreshTimestamp`)
338
-
339
- ## Cache Management
340
-
341
- ### generateTags
342
-
343
- Generates RTK Query cache tags for automatic invalidation.
344
-
345
- ``````typescript
346
- import {providesIdTags, invalidatesIdTags} from "@terreno/rtk";
347
-
348
- // In your API endpoints
349
- getTodos: build.query({
350
- query: () => "/todos",
351
- providesTags: providesIdTags("todos"), // Tags individual items + collection
352
- }),
353
-
354
- createTodo: build.mutation({
355
- query: (body) => ({url: "/todos", method: "POST", body}),
356
- invalidatesTags: invalidatesIdTags("todos"), // Invalidates collection
357
- }),
358
- ``````
359
-
360
- **Functions:**
361
- - `providesIdTags(tagName)` — Returns tags for list responses (individual items + collection tag)
362
- - `invalidatesIdTags(tagName)` — Returns tags to invalidate on mutations
363
-
364
- ### populateId
365
-
366
- Helper for normalizing MongoDB ObjectIds in RTK Query responses.
367
-
368
- ``````typescript
369
- import {populateId} from "@terreno/rtk";
370
-
371
- // Ensures _id is properly handled in Redux normalization
372
- const normalizedData = populateId(responseData);
373
- ``````
374
-
375
- ### ListResponse
376
-
377
- Standard interface for paginated list responses from @terreno/api:
378
-
379
- ``````typescript
380
- interface ListResponse<T> {
381
- data: T[];
382
- page: number;
383
- limit: number;
384
- total: number;
385
- more: boolean;
386
- }
387
- ``````
388
-
389
- ## Token Management
390
-
391
- ### Token Expiration Helpers
392
-
393
- ``````typescript
394
- import {
395
- getAuthToken,
396
- getTokenExpirationTimes,
397
- getFriendlyExpirationInfo,
398
- shouldShowStillThereModal,
399
- } from "@terreno/rtk";
400
-
401
- // Get current token from secure storage
402
- const token = await getAuthToken();
403
-
404
- // Check token expiration times
405
- const {authRemainingSecs, refreshRemainingSecs} = await getTokenExpirationTimes();
406
- console.info(`Auth token expires in ${authRemainingSecs} seconds`);
407
-
408
- // Get human-readable expiration info
409
- const info = await getFriendlyExpirationInfo();
410
- console.info(info); // "Auth: 14m 23s, Refresh: 29d 23h"
411
-
412
- // Check if should show "still there?" modal (refresh token <= 65 seconds)
413
- if (shouldShowStillThereModal()) {
414
- showModal("Your session is about to expire. Continue?");
415
- }
416
- ``````
417
-
418
- **Functions:**
419
- - `getAuthToken()` — Returns JWT token from secure storage
420
- - `getRefreshToken()` — Returns refresh token from secure storage
421
- - `getTokenExpirationTimes()` — Returns `{authRemainingSecs, refreshRemainingSecs}`
422
- - `getFriendlyExpirationInfo()` — Returns formatted expiration string
423
- - `shouldShowStillThereModal()` — Returns true if refresh token expires in <= 65 seconds
424
-
425
- ## SDK Generation
426
-
427
- ### OpenAPI Code Generation
428
-
429
- Generate typed RTK Query hooks from your @terreno/api backend's OpenAPI spec.
430
-
431
- **Configuration (openapi-config.ts):**
432
- ``````typescript
433
- import type {ConfigFile} from "@rtk-query/codegen-openapi";
434
-
435
- const config: ConfigFile = {
436
- apiFile: "@terreno/rtk",
437
- apiImport: "emptySplitApi",
438
- outputFile: "./store/openApiSdk.ts",
439
- schemaFile: "http://localhost:4000/openapi.json",
440
- hooks: true,
441
- tag: true,
442
- flattenArg: true,
443
- argSuffix: "Args",
444
- responseSuffix: "Res",
445
- };
446
-
447
- export default config;
448
- ``````
449
-
450
- **Generate SDK:**
451
- ``````bash
452
- # Backend must be running on the specified port
453
- npx @rtk-query/codegen-openapi openapi-config.ts
454
- ``````
455
-
456
- **Usage:**
457
- ``````typescript
458
- import {useGetTodosQuery, usePostTodosMutation} from "@/store/openApiSdk";
459
-
460
- const {data, isLoading, error, refetch} = useGetTodosQuery({completed: false});
461
- const [createTodo, {isLoading: isCreating}] = usePostTodosMutation();
462
- ``````
463
-
464
- **Critical Rules:**
465
- - **Never modify `openApiSdk.ts` manually** — it is auto-generated
466
- - **Never use `axios` or `fetch` directly** — always use generated hooks
467
- - Regenerate SDK after any backend route changes
468
-
469
- ### emptyApi / emptySplitApi
470
-
471
- Base RTK Query API with authentication, retry logic, and automatic token refresh.
472
-
473
- **Features:**
474
- - **Axios with retry:** 3 retries with exponential backoff for queries
475
- - **Token refresh:** Automatically refreshes tokens when < 2 minutes from expiry
476
- - **Mutex locking:** Prevents simultaneous token refreshes across concurrent requests
477
- - **401 handling:** Auto-refreshes token on 401 responses and retries the request
478
- - **Mutation safety:** Mutations don't retry on non-401 errors (prevents duplicates)
479
- - **Query serialization:** Uses `qs.stringify()` for complex queries (`$in`, `$lt`, `$gte`)
480
-
481
- **Automatic Headers:**
482
- - `authorization: Bearer <token>` (from the registered [token source](#token-source); the JWT in storage by default)
483
- - `App-Version` (from Expo config)
484
- - `App-Platform` ("web" or "mobile")
485
-
486
- **Response Handling:**
487
- - 204 responses return `null`
488
- - List endpoints return full response: `{data, more, page, limit, total}`
489
- - CRUD endpoints extract and return `result.data`
490
-
491
- **Base URL Resolution (priority order):**
492
- 1. `Constants.expoConfig?.extra?.BASE_URL` (production/staging)
493
- 2. `process.env.EXPO_PUBLIC_API_URL` (dev web)
494
- 3. `Constants.expoConfig?.hostUri` + the dev API port (dev simulator/device)
495
- 4. `http://localhost:<dev API port>` (fallback)
496
-
497
- The dev API port defaults to `4000` and is overridable per app via `EXPO_PUBLIC_DEV_API_PORT`
498
- or `expoConfig.extra.DEV_API_PORT` (for example `3000` or `9000`).
499
-
500
- ## Debugging
501
-
502
- ### Debug Logging
503
-
504
- ``````typescript
505
- import {logAuth, logSocket, AUTH_DEBUG, WEBSOCKETS_DEBUG} from "@terreno/rtk";
506
-
507
- // Check if debug logging is enabled (via expoConfig.extra)
508
- console.info("Auth debug:", AUTH_DEBUG);
509
- console.info("WebSocket debug:", WEBSOCKETS_DEBUG);
510
-
511
- // Log auth events (only logs if AUTH_DEBUG is true)
512
- logAuth("Token refreshed", {userId, timestamp: Date.now()});
513
-
514
- // Log socket events (only logs if WEBSOCKETS_DEBUG is true)
515
- logSocket("Connected", {socketId: socket.id});
516
- ``````
517
-
518
- **Enable Debug Logging:**
519
-
520
- Add to your `app.json` or `app.config.ts`:
521
- ``````json
522
- {
523
- "expo": {
524
- "extra": {
525
- "AUTH_DEBUG": true,
526
- "WEBSOCKETS_DEBUG": true
527
- }
528
- }
529
- }
530
- ``````
531
-
532
- ## Platform Detection
533
-
534
- ``````typescript
535
- import {IsWeb} from "@terreno/rtk";
536
-
537
- if (IsWeb) {
538
- console.info("Running on web platform");
539
- } else {
540
- console.info("Running on native (iOS/Android)");
541
- }
542
- ``````
543
-
544
- **Platform-specific behavior:**
545
- - **Token storage:** SecureStore (native) vs AsyncStorage (web)
546
- - **WebSocket:** Uses native WebSocket API on both platforms
547
- - **SSR safety:** Web code checks `typeof window !== "undefined"` before browser APIs
548
-
549
- ## Environment Variables
550
-
551
- Configuration for frontend apps using @terreno/rtk:
552
-
553
- | Variable | Required | Default | Description |
554
- |----------|----------|---------|-------------|
555
- | `EXPO_PUBLIC_API_URL` | No | Auto-detected | Backend API base URL (production/staging deployments) |
556
- | `EXPO_PUBLIC_DEV_API_PORT` | No | 4000 | Local dev API port for host/localhost resolution (also settable via `extra.DEV_API_PORT`) |
557
- | `NODE_ENV` | No | `development` | Environment: `development`, `production`, `test` |
558
-
559
- **Base URL resolution priority:**
560
-
561
- 1. `Constants.expoConfig?.extra?.BASE_URL` (from `app.json` `extra` field)
562
- 2. `process.env.EXPO_PUBLIC_API_URL` (for web development)
563
- 3. `Constants.expoConfig?.hostUri` + the dev API port (for Expo dev server - simulator/device)
564
- 4. `http://localhost:<dev API port>` (fallback)
565
-
566
- The dev API port defaults to `4000` and is overridable per app via `EXPO_PUBLIC_DEV_API_PORT`
567
- or `expoConfig.extra.DEV_API_PORT` (for example `3000` or `9000`).
568
-
569
- **Example `app.json` configuration:**
570
-
571
- ``````json
572
- {
573
- "expo": {
574
- "extra": {
575
- "BASE_URL": "https://api.example.com",
576
- "AUTH_DEBUG": true,
577
- "WEBSOCKETS_DEBUG": false
578
- }
579
- }
580
- }
581
- ``````
582
-
583
- **Debug flags (via `app.json` `extra` field):**
584
-
585
- - `AUTH_DEBUG` — Enable verbose auth logging (token refresh, login/logout)
586
- - `WEBSOCKETS_DEBUG` — Enable WebSocket connection/disconnection logs
587
-
588
- **Example `.env` for web development:**
589
-
590
- ``````env
591
- EXPO_PUBLIC_API_URL=http://localhost:4000
592
- ``````
593
-
594
- ## Related Documentation
595
-
596
- - [Authentication Architecture](../../explanation/authentication.md) — Deep-dive into JWT + Passport system
597
- - [WebSocket Integration How-To](../../how-to/websocket-integration.md) — Real-time connection setup
598
- - [Add GitHub OAuth](../../how-to/add-github-oauth.md) — Step-by-step OAuth setup guide
599
- - [@terreno/api Reference](../api.md) — Backend API framework
600
- - [@terreno/ui Reference](../ui.md) — React Native components
1
+ # @terreno/rtk (removed)
2
+
3
+ `@terreno/rtk` was removed in **Terreno 58.0.0**. It is not published for 58 or later, and no
4
+ Terreno package depends on Redux. To upgrade, follow
5
+ [How to upgrade from @terreno/rtk to @terreno/syncdb](../../how-to/migrate-rtk-to-syncdb.md),
6
+ which maps every former export to its replacement.
7
+
8
+ | Former `@terreno/rtk` area | Terreno 58 home |
9
+ |----------------------------|-----------------|
10
+ | RTK Query base API and `@rtk-query/codegen-openapi` SDK | `createApiClient` (`@terreno/syncdb/api`) and `terreno-syncdb-codegen --api-out` → `store/apiSdk.ts` |
11
+ | Collection CRUD hooks | `useQuery` / `useMutate` (`@terreno/syncdb/react`) |
12
+ | Better Auth client and Redux slice | `createBetterAuthClient`, `SessionProvider`, `useSession` (`@terreno/syncdb/auth`) |
13
+ | JWT `generateAuthSlice` | `jwtAuthAdapter`, `useJwtAuth` (`@terreno/syncdb/auth`) |
14
+ | `baseUrl`, `baseWebsocketsUrl`, base URL resolution | `@terreno/syncdb/config` |
15
+ | `useSocketConnection` | The syncdb socket: `useSocketEvent` (`@terreno/syncdb/react`) |
16
+ | `useFeatureFlags`, `useTerrenoFeatureFlags` | `@terreno/syncdb/flags` |
17
+ | `useUpgradeCheck`, permissions helpers, dev console logger, `useMCPTools` | `@terreno/syncdb/app` |
18
+ | `@terreno/rtk/buildNumber` | `@terreno/syncdb/buildNumber` |
19
+ | Offline middleware, `useOfflineStatus`, `useServerStatus` | The syncdb outbox and `useSyncStatus` (`@terreno/syncdb/react`) |
20
+
21
+ Apps that stay on Terreno 57.x keep using `@terreno/rtk` 57.x. Its reference is in the 57.x
22
+ docs snapshot.
23
+
24
+ See also: [`@terreno/syncdb` reference](../syncdb.md) and the
25
+ [58.0.0 upgrade note](https://github.com/TerrenoLabs/terreno/blob/master/mcp-server/src/docs/upgrades/58.0.0.md#terrenortk-and-redux-are-removed).