@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,21 +1,18 @@
1
+ import {useSession} from "@terreno/syncdb/auth";
1
2
  import {Box, Button, Card, Heading, Page, Spinner, TapToEdit, Text, TextField} from "@terreno/ui";
2
3
  import type React from "react";
3
4
  import {useCallback, useEffect, useMemo, useState} from "react";
4
- import {signOut} from "@/lib/betterAuth";
5
- import {logout, syncBetterAuthSession, useAppDispatch} from "@/store/index";
6
- import {useGetMeQuery, usePatchMeMutation} from "@/store/sdk";
5
+ import {useGetAuthMeQuery, usePatchAuthMeMutation} from "@/store/apiSdk";
7
6
 
8
7
  const ProfileScreen: React.FC = () => {
9
- const dispatch = useAppDispatch();
10
- const {data: profile, isLoading} = useGetMeQuery();
11
- const [updateProfile] = usePatchMeMutation();
8
+ const {logout} = useSession();
9
+ const {data: user, isLoading} = useGetAuthMeQuery();
10
+ const [updateProfile] = usePatchAuthMeMutation();
12
11
  const [name, setName] = useState<string>("");
13
12
  const [email, setEmail] = useState<string>("");
14
13
  const [currentPassword, setCurrentPassword] = useState<string>("");
15
14
  const [saveError, setSaveError] = useState<string | null>(null);
16
15
 
17
- const user = profile?.data;
18
-
19
16
  // Copy the server name into local state without resetting an in-progress email edit.
20
17
  useEffect(() => {
21
18
  if (!user) {
@@ -32,11 +29,10 @@ const ProfileScreen: React.FC = () => {
32
29
  setEmail(user.email || "");
33
30
  }, [user?.email]);
34
31
 
32
+ // logout signs syncDb out, then Better Auth, and clears cached API responses.
35
33
  const handleLogout = useCallback(async (): Promise<void> => {
36
- await signOut();
37
- dispatch(logout());
38
- await syncBetterAuthSession(dispatch);
39
- }, [dispatch]);
34
+ await logout();
35
+ }, [logout]);
40
36
 
41
37
  const handleSaveName = useCallback(
42
38
  async (value: string): Promise<void> => {
@@ -1,19 +1,18 @@
1
- import FontAwesome from "@expo/vector-icons/FontAwesome";
2
- import {DarkTheme, DefaultTheme, ThemeProvider} from "expo-router/react-navigation";
3
- import {useFonts} from "expo-font";
1
+ import {SessionProvider, useSession} from "@terreno/syncdb/auth";
2
+ import {baseUrl} from "@terreno/syncdb/config";
3
+ import {ApiClientProvider, SyncDbProvider} from "@terreno/syncdb/react";
4
+ import {Spinner, TerrenoProvider} from "@terreno/ui";
4
5
  import {Stack} from "expo-router";
5
- import * as SplashScreen from "expo-splash-screen";
6
+ import {DefaultTheme, ThemeProvider} from "expo-router/react-navigation";
7
+ import type React from "react";
6
8
  import {useEffect} from "react";
7
9
  import "react-native-reanimated";
8
- import {baseUrl, selectBetterAuthUserId} from "@terreno/rtk";
9
- import {installTerrenoDevConsoleLogger} from "@terreno/syncdb";
10
- import {TerrenoProvider} from "@terreno/ui";
11
- import {Provider, useSelector} from "react-redux";
12
- import {PersistGate} from "redux-persist/integration/react";
13
- import {useColorScheme} from "@/components/useColorScheme";
14
- import store, {persistor} from "@/store/index";
10
+ import {installDevConsoleLogger} from "@terreno/syncdb/app";
11
+ import {betterAuthClient} from "@/lib/betterAuth";
12
+ import {apiClient} from "@/store/apiClient";
13
+ import {setSyncDbReady, syncDb} from "@/store/syncdb";
15
14
 
16
- installTerrenoDevConsoleLogger({baseUrl});
15
+ installDevConsoleLogger({baseUrl});
17
16
 
18
17
  export {ErrorBoundary} from "expo-router";
19
18
 
@@ -21,54 +20,78 @@ export const unstable_settings = {
21
20
  initialRouteName: "(tabs)",
22
21
  };
23
22
 
24
- SplashScreen.preventAutoHideAsync();
25
-
26
- export default function RootLayout(): React.ReactElement | null {
27
- const [loaded, error] = useFonts({
28
- SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"),
29
- ...FontAwesome.font,
30
- });
31
-
32
- useEffect(() => {
33
- if (error) {
34
- throw error;
35
- }
36
- }, [error]);
37
-
38
- useEffect(() => {
39
- if (loaded) {
40
- SplashScreen.hideAsync();
41
- }
42
- }, [loaded]);
43
-
44
- if (!loaded) {
45
- return null;
46
- }
23
+ // Logging out signs syncDb out (stop + wipe) and drops cached API responses so the next
24
+ // user never sees the previous user's data.
25
+ const SYNC_DBS = [syncDb];
47
26
 
27
+ // TerrenoProvider loads the Nunito/Titillium Web families @terreno/ui renders with, so this
28
+ // app ships no font assets of its own. Add `useFonts` here only for extra custom faces.
29
+ // SessionProvider owns the Better Auth session (`useSession()`); ApiClientProvider gives the
30
+ // generated hooks in store/apiSdk.ts their request client. No Redux store.
31
+ export default function RootLayout(): React.ReactElement {
48
32
  return (
49
- <Provider store={store}>
50
- <PersistGate loading={null} persistor={persistor}>
51
- <TerrenoProvider openAPISpecUrl={`${baseUrl}/openapi.json`}>
33
+ <TerrenoProvider openAPISpecUrl={`${baseUrl}/openapi.json`}>
34
+ <ApiClientProvider client={apiClient}>
35
+ <SessionProvider
36
+ authClient={betterAuthClient}
37
+ onLogout={apiClient.reset}
38
+ syncDbs={SYNC_DBS}
39
+ >
52
40
  <RootLayoutNav />
53
- </TerrenoProvider>
54
- </PersistGate>
55
- </Provider>
41
+ </SessionProvider>
42
+ </ApiClientProvider>
43
+ </TerrenoProvider>
56
44
  );
57
45
  }
58
46
 
59
47
  function RootLayoutNav(): React.ReactElement {
60
- const colorScheme = useColorScheme();
61
- const userId = useSelector(selectBetterAuthUserId);
48
+ const {userId, isLoading: isAuthLoading} = useSession();
49
+
50
+ // Start syncdb after login; stop on logout or user switch.
51
+ useEffect(() => {
52
+ if (!userId) {
53
+ setSyncDbReady(false);
54
+ return;
55
+ }
56
+ let stopped = false;
57
+ void syncDb
58
+ .start()
59
+ .then(() => {
60
+ if (!stopped) {
61
+ setSyncDbReady(true);
62
+ }
63
+ })
64
+ .catch((startError: unknown) => {
65
+ console.error("[syncdb] start failed", startError);
66
+ setSyncDbReady(false);
67
+ });
68
+ return () => {
69
+ stopped = true;
70
+ setSyncDbReady(false);
71
+ void syncDb.stop();
72
+ };
73
+ }, [userId]);
74
+
75
+ if (isAuthLoading) {
76
+ return <Spinner />;
77
+ }
62
78
 
63
79
  return (
64
- <ThemeProvider value={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
65
- <Stack>
66
- {!userId ? (
67
- <Stack.Screen name="login" options={{headerShown: false}} />
68
- ) : (
69
- <Stack.Screen name="(tabs)" options={{headerShown: false}} />
70
- )}
71
- </Stack>
72
- </ThemeProvider>
80
+ <SyncDbProvider client={syncDb}>
81
+ <ThemeProvider value={DefaultTheme}>
82
+ {/* Stack.Protected is the supported way to gate routes on auth state. Rendering
83
+ Stack.Screen children behind a raw conditional (or a fragment) crashes the
84
+ navigator, because Stack reads its screen config off the child elements. */}
85
+ <Stack>
86
+ <Stack.Protected guard={!userId}>
87
+ <Stack.Screen name="login" options={{headerShown: false}} />
88
+ <Stack.Screen name="signup" options={{headerShown: false}} />
89
+ </Stack.Protected>
90
+ <Stack.Protected guard={Boolean(userId)}>
91
+ <Stack.Screen name="(tabs)" options={{headerShown: false}} />
92
+ </Stack.Protected>
93
+ </Stack>
94
+ </ThemeProvider>
95
+ </SyncDbProvider>
73
96
  );
74
97
  }
@@ -1,116 +1,54 @@
1
- import {Box, Button, Heading, Page, Text, TextField, useToast} from "@terreno/ui";
1
+ import {useSession} from "@terreno/syncdb/auth";
2
+ import {baseUrl} from "@terreno/syncdb/config";
3
+ import {LoginScreen} from "@terreno/ui";
4
+ import {useRouter} from "expo-router";
2
5
  import type React from "react";
3
6
  import {useCallback, useState} from "react";
4
- import {useEmailLoginMutation, useEmailSignUpMutation} from "@/store/sdk";
5
-
6
- const LoginScreen: React.FC = () => {
7
- const [name, setName] = useState<string>("");
8
- const [email, setEmail] = useState<string>("");
9
- const [password, setPassword] = useState<string>("");
10
- const [isSignUp, setIsSignUp] = useState<boolean>(false);
11
- const toast = useToast();
12
-
13
- const [emailLogin, {isLoading: isLoginLoading, error: loginError}] = useEmailLoginMutation();
14
- const [emailSignUp, {isLoading: isSignUpLoading, error: signUpError}] = useEmailSignUpMutation();
15
-
16
- const handleSubmit = useCallback(async (): Promise<void> => {
17
- if (!email || !password) {
18
- toast.warn("Email and password are required");
19
- return;
20
- }
21
-
22
- if (isSignUp && !name) {
23
- toast.warn("Signup requires name");
24
- return;
25
- }
26
-
27
- try {
28
- if (isSignUp) {
29
- await emailSignUp({email, name, password}).unwrap();
30
- } else {
31
- await emailLogin({email, password}).unwrap();
7
+ import {betterAuthClient} from "@/lib/betterAuth";
8
+
9
+ const Login: React.FC = () => {
10
+ const router = useRouter();
11
+ const {refresh} = useSession();
12
+ const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
13
+ const [isSubmitting, setIsSubmitting] = useState(false);
14
+
15
+ const handleSubmit = useCallback(
16
+ async (values: Record<string, string>): Promise<void> => {
17
+ const {email, password} = values;
18
+ setErrorMessage(undefined);
19
+ setIsSubmitting(true);
20
+ try {
21
+ const result = await betterAuthClient.signIn.email({email, password});
22
+ if (result.error) {
23
+ setErrorMessage(result.error.message ?? "Sign in failed.");
24
+ return;
25
+ }
26
+ // Resolve the new session now instead of waiting for the next session push.
27
+ await refresh();
28
+ router.replace("/(tabs)");
29
+ } catch (error: unknown) {
30
+ console.error("[login] Sign in threw", {baseUrl, error});
31
+ setErrorMessage("Sign in failed. Please try again.");
32
+ } finally {
33
+ setIsSubmitting(false);
32
34
  }
33
- } catch (err) {
34
- console.error("Authentication error:", err);
35
- }
36
- }, [email, password, name, isSignUp, emailLogin, emailSignUp, toast]);
37
-
38
- const toggleMode = useCallback((): void => {
39
- setIsSignUp(!isSignUp);
40
- }, [isSignUp]);
41
-
42
- const isLoading = isLoginLoading || isSignUpLoading;
43
- const error = loginError || signUpError;
44
- const isSubmitDisabled = !email || !password || (isSignUp && !name) || isLoading;
35
+ },
36
+ [refresh, router]
37
+ );
45
38
 
46
39
  return (
47
- <Page navigation={undefined}>
48
- <Box
49
- alignItems="center"
50
- alignSelf="center"
51
- flex="grow"
52
- justifyContent="center"
53
- maxWidth={400}
54
- padding={4}
55
- width="100%"
56
- >
57
- <Box marginBottom={8}>
58
- <Heading>{isSignUp ? "Create Account" : "Welcome Back"}</Heading>
59
- </Box>
60
- <Box gap={4} width="100%">
61
- {isSignUp && (
62
- <TextField
63
- disabled={isLoading}
64
- onChange={setName}
65
- placeholder="Name"
66
- title="Name"
67
- value={name}
68
- />
69
- )}
70
- <TextField
71
- autoComplete="username"
72
- disabled={isLoading}
73
- onChange={setEmail}
74
- placeholder="Email"
75
- title="Email"
76
- type="email"
77
- value={email}
78
- />
79
- <TextField
80
- disabled={isLoading}
81
- onChange={setPassword}
82
- placeholder="Password"
83
- title="Password"
84
- type="password"
85
- value={password}
86
- />
87
- {Boolean(error) && (
88
- <Text color="error">
89
- {(error as {data?: {message?: string}})?.data?.message || "An error occurred"}
90
- </Text>
91
- )}
92
- <Box marginTop={4}>
93
- <Button
94
- disabled={isSubmitDisabled}
95
- fullWidth
96
- loading={isLoading}
97
- onClick={handleSubmit}
98
- text={isSignUp ? "Sign Up" : "Login"}
99
- />
100
- </Box>
101
- <Box marginTop={2}>
102
- <Button
103
- disabled={isLoading}
104
- fullWidth
105
- onClick={toggleMode}
106
- text={isSignUp ? "Already have an account? Login" : "Need an account? Sign Up"}
107
- variant="outline"
108
- />
109
- </Box>
110
- </Box>
111
- </Box>
112
- </Page>
40
+ <LoginScreen
41
+ error={errorMessage}
42
+ fields={[
43
+ {autoComplete: "username", label: "Email", name: "email", required: true, type: "email"},
44
+ {label: "Password", name: "password", required: true, type: "password"},
45
+ ]}
46
+ loading={isSubmitting}
47
+ onSignUpPress={() => router.push("/signup")}
48
+ onSubmit={handleSubmit}
49
+ title="Welcome"
50
+ />
113
51
  );
114
52
  };
115
53
 
116
- export default LoginScreen;
54
+ export default Login;
@@ -0,0 +1,53 @@
1
+ import {useSession} from "@terreno/syncdb/auth";
2
+ import {SignUpScreen, simplePasswordRequirements} from "@terreno/ui";
3
+ import {useRouter} from "expo-router";
4
+ import type React from "react";
5
+ import {useCallback, useState} from "react";
6
+ import {betterAuthClient} from "@/lib/betterAuth";
7
+
8
+ const SignUp: React.FC = () => {
9
+ const router = useRouter();
10
+ const {refresh} = useSession();
11
+ const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined);
12
+ const [isSubmitting, setIsSubmitting] = useState(false);
13
+
14
+ const handleSubmit = useCallback(
15
+ async (values: Record<string, string>): Promise<void> => {
16
+ const {email, password, name} = values;
17
+ setErrorMessage(undefined);
18
+ setIsSubmitting(true);
19
+ try {
20
+ const result = await betterAuthClient.signUp.email({email, name, password});
21
+ if (result.error) {
22
+ setErrorMessage(result.error.message ?? "Sign up failed.");
23
+ return;
24
+ }
25
+ // Resolve the new session now instead of waiting for the next session push.
26
+ await refresh();
27
+ router.replace("/(tabs)");
28
+ } catch {
29
+ setErrorMessage("Sign up failed. Please try again.");
30
+ } finally {
31
+ setIsSubmitting(false);
32
+ }
33
+ },
34
+ [refresh, router]
35
+ );
36
+
37
+ return (
38
+ <SignUpScreen
39
+ error={errorMessage}
40
+ fields={[
41
+ {label: "Name", name: "name", required: true, type: "text"},
42
+ {autoComplete: "off", label: "Email", name: "email", required: true, type: "email"},
43
+ {label: "Password", name: "password", required: true, type: "password"},
44
+ ]}
45
+ loading={isSubmitting}
46
+ onLoginPress={() => router.back()}
47
+ onSubmit={handleSubmit}
48
+ passwordRequirements={simplePasswordRequirements}
49
+ />
50
+ );
51
+ };
52
+
53
+ export default SignUp;
@@ -0,0 +1,18 @@
1
+ import {createBetterAuthClient} from "@terreno/syncdb/auth";
2
+ import {baseUrl} from "@terreno/syncdb/config";
3
+ import Constants from "expo-constants";
4
+
5
+ const getAppScheme = (): string => {
6
+ const expoExtra = Constants.expoConfig?.extra;
7
+ return expoExtra?.scheme ?? "{{appName}}";
8
+ };
9
+
10
+ /**
11
+ * Expo-ready Better Auth client (SecureStore on native, AsyncStorage on web). Feeds
12
+ * `SessionProvider` (app/_layout.tsx) and the shared auth provider in store/syncdb.ts.
13
+ */
14
+ export const betterAuthClient = createBetterAuthClient({
15
+ baseURL: baseUrl,
16
+ scheme: getAppScheme(),
17
+ storagePrefix: "{{appName}}",
18
+ });
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Request client for every non-synced backend route (`/auth/me`, modelRouter actions, admin).
3
+ * Synced collections go through `syncDb` instead. Generated hooks in `store/apiSdk.ts` use the
4
+ * client that `ApiClientProvider` (in `app/_layout.tsx`) puts in context.
5
+ */
6
+ import {type ApiClient, createApiClient} from "@terreno/syncdb/api";
7
+ import {appHeaders, baseUrl} from "@terreno/syncdb/config";
8
+ import {authProvider} from "@/store/syncdb";
9
+
10
+ /** Failures callers already handle (signed out, missing rows) are not worth a warning. */
11
+ const QUIET_STATUSES = new Set([401, 404]);
12
+
13
+ export const apiClient: ApiClient = createApiClient({
14
+ authProvider,
15
+ baseUrl,
16
+ credentials: "include",
17
+ headers: appHeaders,
18
+ onError: (error, args): void => {
19
+ if (QUIET_STATUSES.has(error.status)) {
20
+ return;
21
+ }
22
+ console.warn(`${args.method ?? "GET"} ${args.url} failed: ${error.message}`);
23
+ },
24
+ });
25
+
26
+ /** Bearer header for `@terreno/admin-frontend`'s `AdminProvider` fetch client. */
27
+ export const getAdminAuthHeaders = async (): Promise<Record<string, string>> => {
28
+ const token = await authProvider.getToken();
29
+ if (!token) {
30
+ return {};
31
+ }
32
+ return {Authorization: `Bearer ${token}`};
33
+ };
@@ -0,0 +1,76 @@
1
+ // GENERATED by @terreno/syncdb (terreno-syncdb-codegen --api-out). Do not edit.
2
+ // Typed request hooks for every non-synced @terreno/api route. Synced collections use the
3
+ // collection hooks from the sync SDK instead. Run `bun run sdk` with the backend running to
4
+ // regenerate this file from its OpenAPI spec.
5
+ // biome-ignore-all lint/suspicious/noConfusingVoidType: no-argument operations type their argument as void so callers can omit it
6
+
7
+ import type {ApiRequestArgs} from "@terreno/syncdb/api";
8
+ import {
9
+ type UseApiMutationOptions,
10
+ type UseApiMutationResult,
11
+ type UseApiQueryOptions,
12
+ type UseApiQueryResult,
13
+ type UseLazyApiQueryOptions,
14
+ type UseLazyApiQueryResult,
15
+ useApiMutation,
16
+ useApiQuery,
17
+ useLazyApiQuery,
18
+ } from "@terreno/syncdb/react";
19
+
20
+ type ApiSdkQueryOptions<T> = Omit<UseApiQueryOptions<T, T>, "providesTags" | "transformResponse">;
21
+ type ApiSdkLazyOptions<T> = Omit<UseLazyApiQueryOptions<T, never>, "providesTags" | "toRequest">;
22
+
23
+ export type GetAuthMeArgs = void;
24
+
25
+ export type GetAuthMeRes = {
26
+ admin?: boolean;
27
+ email: string;
28
+ name?: string;
29
+ _id: string;
30
+ id: string;
31
+ created?: string;
32
+ updated?: string;
33
+ };
34
+
35
+ export const getAuthMeRequest = (): ApiRequestArgs => ({
36
+ method: "GET",
37
+ url: `/auth/me`,
38
+ });
39
+
40
+ /** Get the current user */
41
+ export const useGetAuthMeQuery = (
42
+ _arg?: GetAuthMeArgs,
43
+ options: ApiSdkQueryOptions<GetAuthMeRes> = {}
44
+ ): UseApiQueryResult<GetAuthMeRes> =>
45
+ useApiQuery<GetAuthMeRes>(getAuthMeRequest(), {...options, providesTags: ["auth"]});
46
+
47
+ export const useLazyGetAuthMeQuery = (
48
+ options: ApiSdkLazyOptions<GetAuthMeRes> = {}
49
+ ): UseLazyApiQueryResult<GetAuthMeRes, GetAuthMeArgs> =>
50
+ useLazyApiQuery<GetAuthMeRes, GetAuthMeArgs>({
51
+ ...options,
52
+ providesTags: ["auth"],
53
+ toRequest: (): ApiRequestArgs => getAuthMeRequest(),
54
+ });
55
+
56
+ export type PatchAuthMeArgs = {
57
+ email?: string;
58
+ name?: string;
59
+ };
60
+
61
+ export type PatchAuthMeRes = GetAuthMeRes;
62
+
63
+ export const patchAuthMeRequest = (arg: PatchAuthMeArgs): ApiRequestArgs => ({
64
+ body: arg,
65
+ method: "PATCH",
66
+ url: `/auth/me`,
67
+ });
68
+
69
+ /** Update the current user */
70
+ export const usePatchAuthMeMutation = (
71
+ options: UseApiMutationOptions<PatchAuthMeArgs, PatchAuthMeRes> = {}
72
+ ): UseApiMutationResult<PatchAuthMeArgs, PatchAuthMeRes> =>
73
+ useApiMutation<PatchAuthMeArgs, PatchAuthMeRes>(patchAuthMeRequest, {
74
+ invalidatesTags: ["auth"],
75
+ ...options,
76
+ });
@@ -0,0 +1,56 @@
1
+ import {
2
+ betterAuthAdapter,
3
+ bridgeBetterAuthReactClient,
4
+ createSyncDb,
5
+ type SyncDb,
6
+ } from "@terreno/syncdb";
7
+ import {baseUrl} from "@terreno/syncdb/config";
8
+ import {betterAuthClient} from "@/lib/betterAuth";
9
+
10
+ export const SYNC_DB_NAME = "{{appName}}";
11
+
12
+ /** Add synced collection names here as you register modelRouter sync scopes on the backend. */
13
+ export const SYNC_COLLECTIONS: string[] = [];
14
+
15
+ /**
16
+ * Shared by the sync client and the request client (store/apiClient.ts) so both send the same
17
+ * Better Auth session token and refresh the same way. bridgeBetterAuthReactClient turns the
18
+ * Better Auth session atom into push updates; pollIntervalMs is only a fallback if that atom
19
+ * is unavailable, so keep it slow.
20
+ */
21
+ export const authProvider = betterAuthAdapter(bridgeBetterAuthReactClient(betterAuthClient), {
22
+ pollIntervalMs: 60_000,
23
+ });
24
+
25
+ export const syncDb: SyncDb = createSyncDb({
26
+ authProvider,
27
+ baseUrl,
28
+ collections: SYNC_COLLECTIONS,
29
+ name: SYNC_DB_NAME,
30
+ });
31
+
32
+ /**
33
+ * Tracks whether `syncDb.start()` has resolved for the current login. Screens gate
34
+ * `mutate()` on it: `start()` must resolve an authenticated user before writes are safe.
35
+ */
36
+ let syncDbReady = false;
37
+ const syncDbReadyListeners = new Set<() => void>();
38
+
39
+ export const setSyncDbReady = (ready: boolean): void => {
40
+ if (syncDbReady === ready) {
41
+ return;
42
+ }
43
+ syncDbReady = ready;
44
+ for (const listener of syncDbReadyListeners) {
45
+ listener();
46
+ }
47
+ };
48
+
49
+ export const subscribeSyncDbReady = (listener: () => void): (() => void) => {
50
+ syncDbReadyListeners.add(listener);
51
+ return () => {
52
+ syncDbReadyListeners.delete(listener);
53
+ };
54
+ };
55
+
56
+ export const getSyncDbReadySnapshot = (): boolean => syncDbReady;
@@ -5,7 +5,3 @@ export const captureException = (error: unknown): void => {
5
5
  export const captureMessage = (message: string): void => {
6
6
  console.warn("Captured message:", message);
7
7
  };
8
-
9
- export const createSentryReduxEnhancer = (): unknown => {
10
- return undefined;
11
- };