@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,530 +1,401 @@
1
- # How to Migrate from @terreno/rtk to @terreno/syncdb
1
+ # How to upgrade from @terreno/rtk to @terreno/syncdb (Terreno 58)
2
2
 
3
- This guide is **AI-context-first**: it is written for an agent (or human) performing a screen-by-screen migration, not as a codemod. Work through one concern at a time — reads, then writes, then conflicts, then sync-status UX — and delete RTK-era code as you go.
3
+ Terreno 58 deletes `@terreno/rtk` and every Redux dependency. `@terreno/syncdb` is the only
4
+ frontend data and session layer:
4
5
 
5
- `@terreno/syncdb` supersedes `@terreno/rtk` for **data synchronization** (CRUD reads/writes, offline queue, realtime convergence). It does not replace the generated OpenAPI SDK, Better Auth Redux wiring, or feature-flag infrastructure.
6
+ | Concern | 57.x (`@terreno/rtk` + Redux) | 58 (`@terreno/syncdb`) |
7
+ |---------|-------------------------------|------------------------|
8
+ | Collection CRUD | RTK Query hooks from `store/openApiSdk.ts` | `useQuery` / `useMutate` (`@terreno/syncdb/react`) |
9
+ | Every other route (`/auth/me`, actions, AI, consents) | RTK Query hooks from `store/openApiSdk.ts` | Generated hooks in `store/apiSdk.ts` on the request client (`@terreno/syncdb/api`) |
10
+ | Session | `generateBetterAuthSlice` / `generateAuthSlice` + Redux selectors | `SessionProvider` + `useSession()` (`@terreno/syncdb/auth`) |
11
+ | Feature flags, server pushes | `useSocketConnection` (second socket) + `useTerrenoFeatureFlags(api, …)` | `useSocketEvent` on the sync socket + `useTerrenoFeatureFlags({client, socket})` (`@terreno/syncdb/flags`) |
12
+ | Base URLs, upgrade check, RBAC, dev logger, MCP tools | `@terreno/rtk` root | `@terreno/syncdb/config`, `@terreno/syncdb/app` |
13
+ | App state | Redux slice + `redux-persist` | React context / `useStoredState` |
6
14
 
7
- `modelRouter` `realtime`, `realtimeList`, `realtimeDocument`, and `setRealtimeSocket` are **deprecated** and **will be removed in Terreno 58**. `RealtimeApp` stays as the socket host for syncdb. Removal checklist: [`docs/tasks/remove-legacy-realtime.md`](https://github.com/TerrenoLabs/terreno/blob/master/docs/tasks/remove-legacy-realtime.md).
15
+ Work through the steps in order. Every removed export and its replacement is in
16
+ [the export table](#every-removed-export).
8
17
 
9
- `installTerrenoDevConsoleLogger` moved to `@terreno/syncdb`. The `@terreno/rtk` export is a **deprecated** re-export kept for the deprecation window. Change the import and pass the base URL:
18
+ ## 1. Run the backend migrations
19
+
20
+ `installDevConsoleLogger` lives on `@terreno/syncdb/app` (or `@terreno/syncdb/devConsoleLogger`). Pass the API base URL once at startup:
10
21
 
11
22
  ```typescript
12
- import {baseUrl} from "@terreno/rtk";
13
- import {installTerrenoDevConsoleLogger} from "@terreno/syncdb";
23
+ import {installDevConsoleLogger} from "@terreno/syncdb/app";
24
+ import {baseUrl} from "@terreno/syncdb/config";
14
25
 
15
- installTerrenoDevConsoleLogger({baseUrl});
26
+ installDevConsoleLogger({baseUrl});
16
27
  ```
17
28
 
18
- ## 1. Before you start
19
-
20
- | Decision | Requirement | Why |
21
- |----------|-------------|-----|
22
- | **Better Auth first?** | Recommended before or in parallel with data migration | `betterAuthAdapter` is the shipped `AuthProvider`; socket sessions work cleanly with `RealtimeApp({betterAuth})`. Legacy JWT sockets still work during transition. |
23
- | **Backend version** | `@terreno/api` with `SyncApp`, `RealtimeApp`, `syncPlugin`, `isDeletedPlugin` | Without backend sync registration, the client has nothing to talk to. |
24
- | **MongoDB replica set** | Required for `RealtimeApp` change streams | Single-node replset is fine for dev. |
25
- | **Per-collection rollout** | One synced collection at a time | REST and sync share the same write path; migrate screen-by-screen. |
26
- | **Redux** | Keep Redux for auth, feature flags, app state; drop RTK Query for migrated collections | See §2. |
27
29
 
28
- **Auth can migrate before data (M3).** A typical two-step order:
30
+ `GptHistory` and `McpServiceToken` now have String `_id`s and sync. Run both data migrations
31
+ **before** deploying 58 frontends. See the
32
+ [58.0.0 upgrade note](https://github.com/TerrenoLabs/terreno/blob/master/mcp-server/src/docs/upgrades/58.0.0.md#gpt-histories-are-a-synced-collection).
29
33
 
30
- 1. **Step A — Auth:** Move login/session to Better Auth (`generateBetterAuthSlice`, `betterAuthAdapter`, `syncDb.start()` on login). Screens can still read/write via RTK Query during this step.
31
- 2. **Step B — Data:** For each collection, replace RTK Query hooks with syncdb hooks and delete the RTK path for that screen.
34
+ Every collection the app reads or writes as a list needs the sync backend: `syncPlugin` +
35
+ `isDeletedPlugin` on the schema, a `sync` config on `modelRouter`, and `SyncApp` + `RealtimeApp`
36
+ on a MongoDB replica set. See [Backend setup](../reference/syncdb.md#backend-setup).
32
37
 
33
- The example app completed both steps. Product Todos and eligible built-in admin
34
- String-`_id` model CRUD use syncdb. Profile and other non-synced routes keep
35
- generated RTK hooks; admin RPC uses its host-bound fetch client.
38
+ ## 2. Swap the packages
36
39
 
37
- **Validation (Task 3.5):** The profile screen (`example-frontend/app/(tabs)/profile.tsx`) was reviewed against this guide. `useGetMeQuery` / `usePatchMeMutation` are **non-synced** custom SDK routes — the guide §8 explicitly says to keep them on RTK. No profile migration is required; the guide is sufficient for that case.
40
+ ```bash
41
+ bun remove @terreno/rtk @reduxjs/toolkit react-redux redux-persist @rtk-query/codegen-openapi
42
+ bun add @terreno/syncdb
43
+ bunx expo install expo-sqlite expo-secure-store @react-native-async-storage/async-storage
44
+ ```
38
45
 
39
- Reference: [`docs/reference/syncdb.md`](../reference/syncdb.md), and the package [`syncdb/README.md`](https://github.com/TerrenoLabs/terreno/blob/release-56.0.0/syncdb/README.md).
46
+ Keep `better-auth`, `@better-auth/expo`, and `@openfeature/react-sdk` / `@openfeature/web-sdk`
47
+ if the app uses them; they are optional peers of `@terreno/syncdb`. Rebuild native clients
48
+ after adding `expo-sqlite`.
40
49
 
41
- ## 2. Install and configure
50
+ Pin `better-auth`, `@better-auth/expo`, and `@better-auth/passkey` to the same `1.7.4`.
51
+ Mixed 1.6 and 1.7 versions fail to type-check, and the 1.7 Expo plugin needs the async storage
52
+ methods `createBetterAuthClient` provides. The server needs `nativeApps: true`, or native
53
+ sign-out answers `403 Missing or null Origin`. See
54
+ [Better Auth 1.7 and native sign-in](../../mcp-server/src/docs/upgrades/58.0.0.md#better-auth-17-and-native-sign-in).
42
55
 
43
- ### Install packages
56
+ Delete `store/index.ts`, `store/appState.ts`, `store/sdk.ts`, `store/openApiSdk.ts`,
57
+ `store/betterAuthApi.ts`, `openapi-config.ts`, `scripts/generate-sdk.ts`, and
58
+ `tsconfig.codegen.json`.
44
59
 
45
- ```bash
46
- bun install @terreno/syncdb
47
- bunx expo install expo-sqlite # native persistence — install in the app, rebuild native
48
- ```
60
+ ## 3. Create the clients
49
61
 
50
- ### Create the sync client
62
+ One `authProvider` feeds the sync client and the request client, so both send the same token
63
+ and refresh on a 401 the same way.
51
64
 
52
- **After** (`example-frontend/store/syncdb.ts`):
65
+ ```typescript
66
+ // lib/betterAuth.ts
67
+ import {createBetterAuthClient} from "@terreno/syncdb/auth";
68
+ import {baseUrl} from "@terreno/syncdb/config";
69
+
70
+ export const betterAuthClient = createBetterAuthClient({
71
+ baseURL: baseUrl,
72
+ scheme: "myapp",
73
+ storagePrefix: "myapp",
74
+ });
75
+ ```
53
76
 
54
77
  ```typescript
55
- import {baseUrl} from "@terreno/rtk";
56
- import {betterAuthAdapter, createSyncDb} from "@terreno/syncdb";
78
+ // store/syncdb.ts
79
+ import {betterAuthAdapter, bridgeBetterAuthReactClient, createSyncDb} from "@terreno/syncdb";
80
+ import {baseUrl} from "@terreno/syncdb/config";
57
81
  import {betterAuthClient} from "@/lib/betterAuth";
82
+ import {SYNC_COLLECTIONS} from "@/store/syncDbSdk";
58
83
 
59
- const authProvider = betterAuthAdapter(betterAuthClient, {pollIntervalMs: 60_000});
84
+ export const authProvider = betterAuthAdapter(bridgeBetterAuthReactClient(betterAuthClient));
60
85
 
61
86
  export const syncDb = createSyncDb({
62
87
  authProvider,
63
88
  baseUrl,
64
- collections: ["todos"],
65
- name: "terreno-example",
66
- debug: __DEV__ ? {capacity: 1000} : false,
89
+ collections: [...SYNC_COLLECTIONS],
90
+ name: "myapp",
91
+ wipeOnSignOut: true,
67
92
  });
68
93
  ```
69
94
 
70
- ### Start/stop on auth
71
-
72
- **After** (`example-frontend/app/_layout.tsx`):
73
-
74
95
  ```typescript
75
- useEffect(() => {
76
- if (!userId) {
77
- return;
78
- }
79
- let stopped = false;
80
- syncDb
81
- .start()
82
- .then(() => {
83
- if (!stopped) {
84
- setSyncDbReady(true);
85
- }
86
- })
87
- .catch((error) => {
88
- console.error("[syncdb] Failed to start client", error);
89
- });
90
- return () => {
91
- stopped = true;
92
- setSyncDbReady(false);
93
- void syncDb.stop();
94
- };
95
- }, [userId, syncDbStartAttempt]);
96
- ```
96
+ // store/apiClient.ts
97
+ import {createApiClient} from "@terreno/syncdb/api";
98
+ import {appHeaders, baseUrl} from "@terreno/syncdb/config";
99
+ import {authProvider} from "@/store/syncdb";
97
100
 
98
- Gate writes with a ready flag (`useSyncDbReady`) so `mutate()` is not called before `start()` resolves a user.
101
+ export const apiClient = createApiClient({
102
+ authProvider,
103
+ baseUrl,
104
+ credentials: "include",
105
+ headers: appHeaders,
106
+ onError: (error) => console.warn(error.message),
107
+ });
108
+ ```
99
109
 
100
- ### Wrap screens with `SyncDbProvider`
110
+ Apps on `@terreno/api` JWT routes (no Better Auth) use `jwtAuthAdapter` as the
111
+ `authProvider` and `SessionProvider` `source`. See
112
+ [JWT auth (legacy)](../reference/syncdb.md#jwt-auth-legacy).
101
113
 
102
- **After** (`example-frontend/app/(tabs)/index.tsx`):
114
+ ## 4. Replace the Redux store with providers
103
115
 
104
116
  ```tsx
105
- import {SyncDbProvider} from "@terreno/syncdb/react";
106
- import {syncDb} from "@/store/syncdb";
107
-
108
- const TodosScreen = () => (
109
- <SyncDbProvider client={syncDb}>
110
- <SyncTodosScreen />
111
- </SyncDbProvider>
117
+ // app/_layout.tsx
118
+ import {installDevConsoleLogger} from "@terreno/syncdb/app";
119
+ import {SessionProvider} from "@terreno/syncdb/auth";
120
+ import {ApiClientProvider, SyncDbProvider} from "@terreno/syncdb/react";
121
+
122
+ installDevConsoleLogger();
123
+
124
+ const RootLayout: React.FC = () => (
125
+ <SessionProvider authClient={betterAuthClient} syncDbs={[syncDb]} onLogout={() => apiClient.reset()}>
126
+ <ApiClientProvider client={apiClient}>
127
+ <SyncDbProvider client={syncDb}>
128
+ <App />
129
+ </SyncDbProvider>
130
+ </ApiClientProvider>
131
+ </SessionProvider>
112
132
  );
113
133
  ```
114
134
 
115
- The example also mounts `SyncDbProvider` at the root (`_layout.tsx`) for `SyncHealthToast`.
116
-
117
- ### Redux store: keep vs remove
118
-
119
- | Keep on Redux | Remove for migrated collections |
120
- |---------------|--------------------------------|
121
- | `generateBetterAuthSlice` / session | `generateAuthSlice` (JWT) once Better Auth ships |
122
- | `terrenoApi` reducer for **non-synced** routes | `useGetTodosQuery` / todo mutations |
123
- | `createOfflineMiddleware` + offline slice | `realtimeList` / `realtimeDocument` for that collection |
124
- | Feature-flag / app-state slices | Per-screen `isLoading` spinners tied to RTK mutations |
125
-
126
- **Before** (`origin/master:example-frontend/store/index.ts`): `generateAuthSlice`, `createOfflineMiddleware`, offline payload sessionStorage.
127
-
128
- **After** (`example-frontend/store/index.ts`): `generateBetterAuthSlice`, `terrenoApi` only — no offline middleware.
129
-
130
- ## 3. Reads
131
-
132
- There is no `isLoading` for reads after bootstrap — data is already in the local store.
133
-
134
- ### List reads
135
-
136
- **Before** (`origin/master:example-frontend/app/(tabs)/index.tsx`):
135
+ Start the sync client when a user signs in:
137
136
 
138
137
  ```typescript
139
- const {data: todosData, isLoading, refetch, isFetching} = useGetTodosQuery({}, {skip: !userId});
140
- const todos = todosData?.data ?? [];
141
- ```
142
-
143
- **After** (`example-frontend/components/SyncTodosScreen.tsx`):
144
-
145
- ```typescript
146
- import {useEntity, useEntityIds} from "@terreno/syncdb/react";
147
-
148
- const incompleteIds = useEntityIds<SyncTodo>("todos", {
149
- filter: (t) => !t.completed,
150
- sort: sortByCreatedDesc,
151
- });
138
+ const {userId} = useSession();
152
139
 
153
- // Per row:
154
- const {data} = useEntity<SyncTodo>("todos", id);
155
- ```
156
-
157
- For small lists, `useQuery` is fine:
158
-
159
- ```typescript
160
- import {useQuery} from "@terreno/syncdb/react";
161
- const todos = useQuery<Todo>("todos", {filter: (t) => !t.completed});
140
+ // Start syncdb for the signed-in user; stop it on sign-out or user switch.
141
+ useEffect(() => {
142
+ if (!userId) {
143
+ return;
144
+ }
145
+ void syncDb.start();
146
+ return () => {
147
+ void syncDb.stop();
148
+ };
149
+ }, [userId]);
162
150
  ```
163
151
 
164
- `where` / `filter` / `sort` / `limit` run in JS over the local store. Tombstones are excluded unless `{includeDeleted: true}`.
152
+ Mount `<SyncDbProvider>` unconditionally, not only while `userId` is set: unmounting it during
153
+ logout throws in screens that are still on screen. `logout()` waits for the server and keeps the
154
+ session if sign-out fails; show `useSession().isSigningOut` on the sign-out button.
165
155
 
166
- ### Filtered, sorted, and paged list args
156
+ Remove `<Provider store={store}>`, `<PersistGate>`, `configureStore`, and every
157
+ `useSelector` / `useDispatch`. Move app-state slices to React context or `useStoredState`
158
+ from `@terreno/ui`.
167
159
 
168
- RTK list args map to the same syntax. Pick the hook by collection size:
160
+ | Redux session code | Replacement |
161
+ |--------------------|-------------|
162
+ | `useSelector(selectBetterAuthUserId)` / `useSelectCurrentUserId()` | `useSession().userId` |
163
+ | `useSelector(selectBetterAuthIsAuthenticated)` | `useSession().isAuthenticated` |
164
+ | `useSelector(selectBetterAuthUser)` | `useSession().user` |
165
+ | `useSelector(selectBetterAuthIsLoading)` / `useSelectIsAuthenticating()` | `useSession().isLoading` (Better Auth) or `useJwtAuth().isAuthenticating` (JWT) |
166
+ | `dispatch(logout())` / `LOGOUT_ACTION_TYPE` listeners | `await useSession().logout()`; extra cleanup goes in `SessionProvider` `onLogout` |
167
+ | `dispatch(api.util.resetApiState())` on logout | `apiClient.reset()` in `onLogout` |
169
168
 
170
- | RTK | Whole collection synced (`collections`) | Too large to sync whole (`queryCollections`) |
171
- |-----|------------------------------------------|----------------------------------------------|
172
- | `useGetMessagesQuery({threadId})` | `useQuery("messages", {where: {threadId}})` | `useWindowQuery("messages", {where: {threadId}})` |
173
- | `{status: {$in: ["open", "pending"]}}` | `where: {status: {$in: [...]}}` | same |
174
- | `{sort: "-created"}` | `sort: "-created"` | same |
175
- | `{limit: 20}` | `limit: 20` | `pageSize: 20` |
176
- | `{page: 2}` / load more | grow `limit` | `fetchNextPage()` |
177
- | `data.more` / `data.total` | — | `hasMore` / `total` |
178
- | `isLoading` / `isFetching` / `refetch` | not needed | `isLoading` / `isFetching` / `refetch` |
179
- | `{skip: !threadId}` | not needed | `skip: !threadId` |
169
+ ## 5. Regenerate the SDK
180
170
 
181
- ```typescript
182
- import {useWindowQuery} from "@terreno/syncdb/react";
171
+ `terreno-syncdb-codegen --api-out` replaces `@rtk-query/codegen-openapi`. It emits typed hooks
172
+ for every operation without `x-terreno-sync`, with the same names RTK codegen used.
183
173
 
184
- const {data: messages, hasMore, fetchNextPage, isLoading} = useWindowQuery<Message>("messages", {
185
- where: {threadId},
186
- sort: "-created",
187
- pageSize: 30,
188
- skip: !threadId,
189
- });
174
+ ```json
175
+ {
176
+ "scripts": {
177
+ "sdk": "terreno-syncdb-codegen --schema ${OPENAPI_URL:-http://localhost:4000/openapi.json} --api-out ./store/apiSdk.ts",
178
+ "sync-sdk": "terreno-syncdb-codegen --schema ${OPENAPI_URL:-http://localhost:4000/openapi.json} --out ./store/syncDbSdk.ts"
179
+ }
180
+ }
190
181
  ```
191
182
 
192
- Windows with different filters keep separate result lists but share one local row per
193
- document, so an edit to a message shown in two windows updates once. See
194
- [Query windows](../reference/syncdb.md#query-windows).
183
+ With the backend running, run `bun run sdk && bun run sync-sdk`, then change imports:
195
184
 
196
- ### Delete this RTK read code
185
+ | 57.x | 58 |
186
+ |------|----|
187
+ | `import {useGetMeQuery, usePatchMeMutation} from "@/store/sdk"` | `import {useGetAuthMeQuery, usePatchAuthMeMutation} from "@/store/apiSdk"` |
188
+ | `import {usePostGptPromptMutation} from "@/store/openApiSdk"` | `import {usePostGptPromptMutation} from "@/store/apiSdk"` |
189
+ | `api.injectEndpoints({endpoints: …})` | `useApiQuery` / `useApiMutation` from `@terreno/syncdb/react`, or document the route in OpenAPI and regenerate |
190
+ | `api.util.invalidateTags([...])` | `apiClient.invalidateTags([...])` |
191
+ | `api.util.updateQueryData(...)` | Move the collection to syncdb, or `refetch()` |
197
192
 
198
- - `useGetTodosQuery` / `useGet*Query` for the migrated collection
199
- - `isLoading`, `isFetching`, `refetch`, `RefreshControl` wired to list fetch
200
- - `skip: !userId` guards on list queries (replace with `useSyncDbReady` on writes; reads are safe once bootstrapped)
201
- - `realtimeList("todos")` / `realtimeDocument("todos")` in `store/sdk.ts` for that collection
193
+ Hooks keep the `{data, error, isLoading, isFetching, refetch}` and `[trigger, state]` shapes,
194
+ and `.unwrap()` still resolves the data or throws. Errors are `ApiError` (`status`,
195
+ `data.title`, `data.detail`). See [Regenerate the API SDK](generate-the-client-sdk.md).
202
196
 
203
- ## 4. Writes
197
+ ## 6. Move flags, pushes, and app utilities
204
198
 
205
- Writes are synchronous from the UI's perspective: local apply + outbox enqueue + background replay.
206
-
207
- ### Create / update / delete
199
+ ```typescript
200
+ import {useTerrenoFeatureFlags} from "@terreno/syncdb/flags";
201
+ import {useSocketEvent} from "@terreno/syncdb/react";
202
+ import {canOpenAdminPage, useUpgradeCheck} from "@terreno/syncdb/app";
208
203
 
209
- **Before** (`origin/master:example-frontend/app/(tabs)/index.tsx`):
204
+ // Flags ride the sync socket; no second socket.
205
+ useTerrenoFeatureFlags({client: apiClient, skip: !userId, socket: syncDb, userId});
210
206
 
211
- ```typescript
212
- const [createTodo, {isLoading: isCreating}] = usePostTodosMutation();
213
- const [updateTodo] = usePatchTodosByIdMutation();
214
- const [deleteTodo] = useDeleteTodosByIdMutation();
207
+ // Server pushes that used useSocketConnection().socket.on(...)
208
+ useSocketEvent("announcementPublished", (payload) => console.info("announcement", payload));
215
209
 
216
- await createTodo({title, completed: false}).unwrap();
217
- await updateTodo({id, completed: true}).unwrap();
218
- await deleteTodo(id).unwrap();
210
+ const {isRequired, isWarning, onUpdate} = useUpgradeCheck({recheckOnForeground: true});
211
+ const isAdmin = canOpenAdminPage({admin: me?.admin, permissions: me?.permissions});
219
212
  ```
220
213
 
221
- **After** (`example-frontend/components/SyncTodosScreen.tsx`):
214
+ | 57.x | 58 |
215
+ |------|----|
216
+ | `useSocketConnection({baseUrl, getAuthToken, …})` | Delete. The syncdb client owns the one socket. |
217
+ | `socket.on(event, handler)` | `useSocketEvent(event, handler)` or `syncDb.onSocketEvent(event, handler)` |
218
+ | `useTerrenoFeatureFlags(api, {skip, socket, userId})` | `useTerrenoFeatureFlags({client, skip, socket: syncDb, userId})` |
219
+ | `useFeatureFlags(api, options)` | `useFeatureFlags({client, skip, socket, userId})` |
220
+ | `useRealtimeDebug(baseUrl)` / `setRealtimeDebug` | `createSyncDb({debug: …})` and the terreno-mcp-local `get_syncdb_state` tool |
221
+ | `useMCPTools({baseURL})` | `useMCPTools({authProvider, baseURL})` |
222
+ | `installTerrenoDevConsoleLogger()` | `installDevConsoleLogger()` |
223
+ | `import {resolveBuildNumber} from "@terreno/rtk/buildNumber"` | `import {resolveBuildNumber} from "@terreno/syncdb/buildNumber"` |
224
+
225
+ ## 7. Move collection screens to syncdb
226
+
227
+ There is no `isLoading` for reads after bootstrap; data is already local. Writes apply locally,
228
+ queue in the outbox, and replay in the background.
229
+
230
+ | RTK Query | syncdb |
231
+ |-----------|--------|
232
+ | `useGetTodosQuery({completed: false})` | `useQuery("todos", {where: {completed: false}})` |
233
+ | `{sort: "-created", limit: 20}` | `sort: "-created", limit: 20` |
234
+ | Collection too large to sync whole, `{page}` / `more` / `total` | `useWindowQuery("messages", {where, sort, pageSize})` with `fetchNextPage`, `hasMore`, `total` |
235
+ | `useGetTodosByIdQuery(id)` | `useEntity("todos", id)` |
236
+ | `usePostTodosMutation()` | `useMutate("todos").create({data: {_id: generateMutationId(), …}})` |
237
+ | `usePatchTodosByIdMutation()` | `useMutate("todos").update({id, data})` |
238
+ | `useDeleteTodosByIdMutation()` | `useMutate("todos").remove({id})` |
239
+ | Optimistic `updateQueryData`, refetch-after-mutate, write spinners | Delete; writes are local and immediate |
240
+ | 409 / `If-Unmodified-Since` handling | `useConflicts()` + a resolution UI (`ConflictSheet`) |
241
+ | `useOfflineStatus()` / `useServerStatus()` + `OfflineBanner` | `useSyncStatus()` + `SyncStatusBanner` / `SyncHealthToast` |
242
+ | `createOfflineMiddleware` / `configureOfflineMutationEndpoints` | Delete; the outbox replays every write |
222
243
 
223
244
  ```typescript
224
245
  import {generateMutationId} from "@terreno/syncdb";
225
- import {useMutate} from "@terreno/syncdb/react";
246
+ import {useMutate, useQuery} from "@terreno/syncdb/react";
226
247
 
248
+ const todos = useQuery<Todo>("todos", {where: {completed: false}, sort: "-created"});
227
249
  const {create, update, remove} = useMutate("todos");
228
250
 
229
- const id = generateMutationId();
230
- create({data: {_id: id, title, completed: false, created: DateTime.now().toISO()}});
251
+ create({data: {_id: generateMutationId(), title, completed: false}});
231
252
  update({id, data: {completed: true}});
232
253
  remove({id});
233
254
  ```
234
255
 
235
- Client-minted `_id` on creates lets the optimistic row render before the server acks.
236
-
237
- ### Delete this RTK write code
238
-
239
- - `usePost*Mutation`, `usePatch*Mutation`, `useDelete*Mutation` for the migrated collection
240
- - `.unwrap()` / try-catch around mutation promises for happy-path UX
241
- - **`isLoading` / `isCreating` / `isUpdating` spinners on write buttons** — syncdb writes do not await the server
242
- - Manual optimistic cache updates (`updateQueryData`, `upsertQueryData`, temporary local rows in component state)
243
- - `refetch()` or tag invalidation after mutate (`invalidatesTags` for that collection)
244
- - `createOfflineMiddleware` endpoint list entries for that collection (`configureOfflineMutationEndpoints`)
245
- - `If-Unmodified-Since` / 409 handling in custom mutation code (conflicts move to `useConflicts()`)
246
-
247
- Server rejection surfaces later as outbox nacks — conflicts in `useConflicts()`, terminal failures in `getSyncStatus().failedCount`.
248
-
249
- ## 5. Conflicts
250
-
251
- A **conflict** means the client's `baseVersion` (last seen `_syncSeq`) disagrees with the server. The server returns a `conflict` nack:
252
-
253
- - HTTP: `POST /sync/mutate` → **409** with `{nack: {code: "conflict", serverDoc, serverSeq, ...}}`
254
- - Socket: `sync:nack` with `code: "conflict"`
255
-
256
- The client records the conflict locally; the UI must let the user pick a side.
257
-
258
- ### Strategies
259
-
260
- | Strategy | When to use |
261
- |----------|-------------|
262
- | `useServer` | Accept canonical server data (or tombstone if server deleted the doc) |
263
- | `keepMine` | Re-enqueue local changes with a fresh `baseVersion` |
264
-
265
- ```typescript
266
- import {useConflicts} from "@terreno/syncdb/react";
267
-
268
- const {conflicts, resolve} = useConflicts();
269
- resolve({mutationId, strategy: "useServer"});
270
- resolve({mutationId, strategy: "keepMine"});
271
- ```
272
-
273
- ### Minimum UI
274
-
275
- The example app uses:
276
-
277
- - `SyncConflictsController` — single app-wide sheet state (`example-frontend/components/SyncConflictsController.tsx`)
278
- - `ConflictSheet` rendered from `SyncHealthToast` (`example-frontend/app/_layout.tsx`)
279
- - `SyncStatusBanner` conflict badge → `openConflicts("todos")` (`example-frontend/components/SyncTodosScreen.tsx`)
280
-
281
- Copy that pattern or build equivalent: show local vs server fields, two resolution actions, block further edits on conflicted entities until resolved.
282
-
283
- ## 6. Sync status
284
-
285
- Replace **request-loading UX** with **sync-state UX**.
286
-
287
- | RTK era | Syncdb era |
288
- |---------|------------|
289
- | `isLoading` on queries | No list loading spinner (data is local) |
290
- | `useOfflineStatus` / `useServerStatus` | `useSyncStatus()` |
291
- | `OfflineBanner` on fetch failure | `SyncStatusBanner` + `SyncHealthToast` |
292
- | Pull-to-refresh `refetch()` | Optional `client.forceResync()` for support tooling |
293
-
294
- **After** (`example-frontend/components/SyncTodosScreen.tsx`):
295
-
296
- ```typescript
297
- import {useSyncStatus} from "@terreno/syncdb/react";
298
- import {SyncStatusBanner} from "@terreno/ui";
299
-
300
- const syncStatus = useSyncStatus();
301
-
302
- <SyncStatusBanner
303
- isOnline={syncStatus.isOnline}
304
- queuedCount={syncStatus.queuedCount}
305
- conflictCount={syncStatus.conflictCount}
306
- failedCount={syncStatus.failedCount}
307
- paused={syncStatus.paused}
308
- draining={syncStatus.draining}
309
- sentThisDrain={syncStatus.sentThisDrain}
310
- totalThisDrain={syncStatus.totalThisDrain}
311
- onOpenConflicts={openConflictSheet}
312
- onAuthRequired={handleAuthRequired}
313
- />
314
- ```
315
-
316
- `SyncHealthToast` (`example-frontend/components/SyncHealthToast.tsx`) adds persistent toasts for per-collection conflicts/failures and an initial "Syncing…" toast with optional force reload.
317
-
318
- `paused: "auth"` means replay is halted until the same user re-authenticates — wire `onAuthRequired` to your login flow.
319
-
320
- ## 7. Auth
321
-
322
- **Auth can ship before data migration.** Complete Better Auth + `betterAuthAdapter` first; keep RTK Query for reads/writes until each screen migrates.
323
-
324
- ### Better Auth adapter
325
-
326
- **After** (`example-frontend/store/syncdb.ts`):
327
-
328
- ```typescript
329
- import {betterAuthAdapter, createSyncDb} from "@terreno/syncdb";
330
- import {betterAuthClient} from "@/lib/betterAuth";
331
-
332
- const authProvider = betterAuthAdapter(syncAuthClient, {pollIntervalMs: 60_000});
333
-
334
- export const syncDb = createSyncDb({authProvider, baseUrl, collections: ["todos"], name: "terreno-example"});
335
- ```
336
-
337
- `betterAuthAdapter` implements `AuthProvider`:
338
-
339
- - `getToken()` / `getUserId()` — read session per request (not cached)
340
- - `onAuthChange` — session atom subscription (example bridges `$store.atoms.session`)
341
- - `refresh()` — one silent refresh per auth-pause episode
256
+ Client-minted `_id`s let the optimistic row render before the server acknowledges it.
257
+ Rejected writes surface as conflicts in `useConflicts()` (resolve with `useServer` or
258
+ `keepMine`) or as `failedCount` in `useSyncStatus()`. `paused: "auth"` means replay waits for
259
+ the same user to sign back in; wire `SyncStatusBanner` `onAuthRequired` to the login flow.
260
+ Example: `example-frontend/components/SyncTodosScreen.tsx`.
342
261
 
343
- ### Token storage
262
+ | Event | Local store and outbox |
263
+ |-------|------------------------|
264
+ | 401 or expired session | Kept; replay resumes after the same user signs in |
265
+ | `useSession().logout()` with `wipeOnSignOut: true` | Wiped |
266
+ | A different user signs in | Wiped |
267
+ | Schema version mismatch or web decrypt failure | Wiped and re-bootstrapped |
344
268
 
345
- Better Auth session tokens live in the Better Auth client (`@terreno/rtk` `createBetterAuthClient`):
269
+ ## 8. Update ui and admin call sites
346
270
 
347
- - **Native:** the Expo plugin's cookie jar in `expo-secure-store`
348
- - **Web (default):** Better Auth's HttpOnly session cookie, managed by the browser. No token is in app storage
349
- - **Web with `webAuth: "bearer"`:** the `set-auth-token` bearer in `localStorage` (or the `bearerStorage` you pass), readable with `getBetterAuthBearerToken()`. See [Frontend and API on different sites](configure-better-auth.md#frontend-and-api-on-different-sites)
271
+ | 57.x | 58 |
272
+ |------|----|
273
+ | `<ConsentNavigator api={terrenoApi}>` | `<ConsentNavigator client={apiClient}>` |
274
+ | `<AnnouncementNavigator api={terrenoApi} …>` | `<AnnouncementNavigator client={apiClient} …>` |
275
+ | `useConsentForms(api, baseUrl)`, `useSubmitConsent(api, baseUrl)`, `useAnnouncements(api, …)`, `useAcknowledgeAnnouncement(api, …)` | Same hooks with `client` as the first argument |
276
+ | `useServerStatus()` → `OfflineBanner` | `useSyncStatus()` → `OfflineBanner queueLength={queuedCount}` |
277
+ | Admin `api` prop, `useAdminApi(api, …)` | Removed; see [Admin no longer takes an RTK `api`](https://github.com/TerrenoLabs/terreno/blob/master/mcp-server/src/docs/upgrades/58.0.0.md#admin-no-longer-takes-an-rtk-api) |
278
+ | `getAdminAuthHeaders` from `@/store/betterAuthApi` | `() => authProvider.getToken()` as a Bearer header, passed to `AdminProvider` `getAuthHeaders` |
350
279
 
351
- Syncdb does not store tokens itself — it calls `authProvider` on every transport/HTTP request.
280
+ ## Copy the example app's changes
352
281
 
353
- ### Socket auth and `sync:auth-expired`
282
+ Native build config, auth wiring, and phone layout fixes the example app needed are listed in
283
+ [Example app changes to copy](../../mcp-server/src/docs/upgrades/58.0.0.md#example-app-changes-to-copy).
354
284
 
355
- On connect, the socket transport sends the bearer token from `getToken()`. When the server's session re-validation sweep finds an expired/disabled session, it emits `sync:auth-expired` then disconnects (`api/src/realtime/sessionRevalidation.ts`). The client maps this to `paused: "auth"` — **no local wipe**, outbox preserved (INV-2).
356
-
357
- ### Backend
358
-
359
- Register `BetterAuthApp` and `RealtimeApp` on the same `TerrenoApp`. `RealtimeApp` picks up the
360
- Better Auth instance by itself, so no extra option is needed. Legacy JWT sockets keep working
361
- alongside it. Only a `RealtimeApp` mounted without `TerrenoApp` needs the instance passed in:
362
-
363
- ```typescript
364
- new RealtimeApp({betterAuth: {auth, userModel: User}})
365
- ```
366
-
367
- ## 8. Codegen
368
-
369
- Synced collections use `bun run sync-sdk`, which runs `terreno-syncdb-codegen` from
370
- `@terreno/syncdb`. It reads `/openapi.json`, finds list operations tagged
371
- `x-terreno-sync`, and writes `store/syncDbSdk.ts` (`SYNC_COLLECTIONS` plus friendly
372
- hooks such as `useTodos` / `useTodo` / `useCreateTodo`). Do not edit that file.
373
- Custom collections call `createCollectionHooks` in a sibling file. Names are chosen
374
- so they do not collide with RTK `openApiSdk.ts` during dual-stack migration.
285
+ ## 9. Verify
375
286
 
376
287
  ```bash
377
- # Backend running on port 4000
378
- cd example-frontend && bun run sync-sdk
288
+ rg -l "@terreno/rtk|@reduxjs/toolkit|react-redux|redux-persist|rtk-query" --glob '!**/node_modules/**' .
289
+ bun run compile && bun run lint && bun test
379
290
  ```
380
291
 
381
- ### Does `bun run sdk` still work?
382
-
383
- **Yes.** `example-frontend/package.json` still defines:
384
-
385
- ```json
386
- "sdk": "bun scripts/generate-sdk.ts && bun biome check --write scripts/generate-sdk.ts"
387
- ```
388
-
389
- It runs `@rtk-query/codegen-openapi` against `openapi-config.ts` and writes `store/openApiSdk.ts`.
390
-
391
- ### What changes post-migration
392
-
393
- | Still generated | Stop using for migrated collections |
394
- |-----------------|-------------------------------------|
395
- | Auth routes, `GET /auth/me`, AI routes, feature flags, ObjectId admin compatibility CRUD | Collection CRUD — import from `store/syncDbSdk.ts` instead |
396
- | Custom REST / RPC endpoints | Todo `realtimeList` / `realtimeDocument` wiring in `store/sdk.ts` |
397
-
398
- Keep running `bun run sdk` after backend route changes — non-synced screens still import from `store/sdk.ts`.
399
-
400
- Workflow:
401
-
402
- ```bash
403
- # Backend running on port 4000
404
- cd example-frontend && bun run sdk
405
- cd example-frontend && bun run sync-sdk
406
- ```
407
-
408
- ### Syncdb hooks codegen
409
-
410
- For collections with `sync` enabled on the backend, generate local-first hooks from the same OpenAPI spec:
411
-
412
- ```bash
413
- cd example-frontend && bun run sync-sdk
414
- ```
415
-
416
- This writes `store/syncDbSdk.ts` with friendly hooks (`useTodos`, `useCreateTodo`, …) that intentionally differ from RTK names so both SDKs can coexist during migration. Import synced screens from `@/store/syncDbSdk`, not `store/sdk.ts`.
417
-
418
- ### Built-in admin
419
-
420
- For a synced String-`_id` model, set `sync.adminBroadcast: true` on its existing
421
- `modelRouter` sync config and inject a dedicated `createSyncDb` client with
422
- `windowCollections` into `AdminProvider`. REST remains the list membership,
423
- search, sort, pagination, and bulk-patch surface; TinyBase overlays rows and
424
- `useMutate` handles create/update/delete. Pass the host's `useConflicts()` result
425
- as `syncConflicts`.
426
-
427
- Admin RPC (config, scripts, roles, comms, consent, documents, version config,
428
- background tasks) uses admin-frontend's host-bound native fetch client. Do not
429
- add new admin `injectEndpoints`. Terreno 57 keeps `useAdminApi` and the required
430
- `api` prop only for ObjectId/API-only compatibility; Terreno 58 removes them.
431
-
432
- ## 9. Feature flags
433
-
434
- Post-migration, feature flags stay on `@terreno/rtk` + OpenFeature — **not** on syncdb.
435
-
436
- **After** (`example-frontend/app/_layout.tsx`):
437
-
438
- ```typescript
439
- import {OpenFeatureProvider} from "@openfeature/react-sdk";
440
- import {useTerrenoFeatureFlags} from "@terreno/rtk";
441
-
442
- const OpenFeatureBridge = ({children, socket}) => {
443
- const bridgeUserId = useSelector(selectBetterAuthUserId) ?? undefined;
444
- useTerrenoFeatureFlags(terrenoApi, {skip: !bridgeUserId, socket, userId: bridgeUserId});
445
- return <OpenFeatureProvider domain="feature-flags">{children}</OpenFeatureProvider>;
446
- };
447
- ```
448
-
449
- See [`docs/reference/feature-flags.md`](../reference/feature-flags.md) and [`docs/how-to/add-feature-flags.md`](add-feature-flags.md).
450
-
451
- **Example app gap:** todos do **not** gate on a feature flag — `SyncTodosScreen` is syncdb-only. For a gradual rollout, add a boolean flag and render the RTK screen when off, syncdb screen when on (see §10). The reference migration intentionally skipped flag wiring to keep one data path for e2e tests.
452
-
453
- ## 10. Rollback
454
-
455
- ### Per-screen feature flag
456
-
457
- Ship both implementations behind a flag:
458
-
459
- - Flag **off** → legacy RTK Query screen
460
- - Flag **on** → syncdb screen
461
-
462
- Both bundles ship until you flip the default and delete the RTK path.
463
-
464
- The example app does **not** use this pattern for todos.
465
-
466
- ### What local data survives
467
-
468
- | Event | Local IndexedDB / SQLite | Outbox |
469
- |-------|------------------------|--------|
470
- | Logout / 401 | **Kept** (INV-2) | **Kept** — same user can sign back in and replay |
471
- | Different-user login | **Wiped** | **Wiped** |
472
- | `signOut()` default | **Kept** | **Kept** |
473
- | `signOut()` with `wipeOnSignOut: true` | **Wiped** | **Wiped** |
474
- | Schema version mismatch | **Wiped** + re-bootstrap | Re-built |
475
- | Decrypt failure (web, default) | **Wiped** + re-bootstrap | Re-built |
476
-
477
- `wipeLocalData` clears store tables, persisted databases, and cached encryption keys (`syncdb/src/storage/wipe.ts`).
478
-
479
- Rolling back a **screen** to RTK does not automatically clear syncdb data — stale local todos may still exist until user switch or explicit wipe. Plan for that if you flip the flag off in production.
480
-
481
- ## 11. Checklist
482
-
483
- Copy and track per collection:
484
-
485
- ```
486
- [ ] 1. Backend: isDeletedPlugin + syncPlugin on schema
487
- [ ] 2. Backend: modelRouter sync scope configured
488
- [ ] 3. Backend: SyncApp + RealtimeApp registered; Mongo replica set running
489
- [ ] 4. Backend: ensureSyncIndexes succeeds at startup
490
- [ ] 5. Frontend: @terreno/syncdb installed; expo-sqlite in app (native)
491
- [ ] 6. Frontend: createSyncDb client + betterAuthAdapter
492
- [ ] 7. Frontend: syncDb.start()/stop() wired to auth in root layout
493
- [ ] 8. Frontend: SyncDbProvider wraps migrated screens
494
- [ ] 9. Reads: useGet*Query → useQuery / useEntityIds + useEntity
495
- [ ] 10. Writes: usePost/Patch/Delete*Mutation → useMutate().create/update/remove
496
- [ ] 11. Deleted RTK: mutation isLoading spinners, refetch-after-mutate, offline middleware entries, realtime cache patches
497
- [ ] 12. Conflicts: useConflicts + resolution UI (ConflictSheet or equivalent)
498
- [ ] 13. Sync status: useSyncStatus + SyncStatusBanner / health toasts (not query isLoading)
499
- [ ] 14. Auth: Better Auth session + onAuthRequired → re-login when paused
500
- [ ] 15. Codegen: bun run sdk still run for non-synced routes; remove todo hooks from screens
501
- [ ] 16. Feature flags: OpenFeature bridge still fed via useTerrenoFeatureFlags (if used)
502
- [ ] 17. Rollback plan: feature flag or accept stale local data on revert
503
- [ ] 18. Verify: CRUD, offline create/edit/delete, reconnect, conflict both strategies, user switch
504
- ```
505
-
506
- ## Backend prerequisites (detail)
507
-
508
- Before a collection can sync:
509
-
510
- - [ ] `isDeletedPlugin` on schema (soft delete required)
511
- - [ ] `syncPlugin` on schema (`_syncSeq` stamping)
512
- - [ ] `modelRouter("/path", Model, {sync: {scope: ...}})` three-argument form
513
- - [ ] Custom scopes include `snapshotFilter`
514
- - [ ] `new SyncApp({getUserScopes?, accessControl?})` for tenant/custom scopes and admin-window `admin:access` (mount `AdminApp` too so admin `queryFilter` / resource actions apply to hydrate and `|admin` deltas)
515
- - [ ] `new RealtimeApp({betterAuth?})` + MongoDB replica set
516
- - [ ] No `updateMany`, `deleteMany`, `deleteOne`, `findOneAndDelete`, or `bulkWrite` on synced models
517
-
518
- ## What stays on @terreno/rtk
519
-
520
- - Generated OpenAPI SDK for non-synced routes
521
- - Better Auth Redux slice (`generateBetterAuthSlice`)
522
- - Feature flags (`useTerrenoFeatureFlags` / `useFeatureFlags`)
523
- - Socket connection for flags and legacy realtime (until removed)
524
- - `@terreno/rtk` is not being removed
292
+ The search returns nothing. Run the app on an iOS and an Android dev client, not only web. Then sign in, open each screen that read or wrote data, sign out,
293
+ and sign in as a second user.
294
+
295
+ ## Every removed export
296
+
297
+ Every export of `@terreno/rtk` 57.x and its 58 replacement. Paths: `api` =
298
+ `@terreno/syncdb/api`, `app` = `@terreno/syncdb/app`, `auth` = `@terreno/syncdb/auth`,
299
+ `config` = `@terreno/syncdb/config`, `flags` = `@terreno/syncdb/flags`, `react` =
300
+ `@terreno/syncdb/react`, root = `@terreno/syncdb`.
301
+
302
+ ### Request client and generated SDK (`emptyApi`, `tagGenerator`, `mongooseSlice`, `sessionApi`)
303
+
304
+ | Removed | Replacement |
305
+ |---------|-------------|
306
+ | `emptySplitApi` | `createApiClient` (api) + `ApiClientProvider` (react) |
307
+ | `getBaseQuery`, `staggeredBaseQuery` | `createApiClient` (api): auth header, 401 → refresh → retry, timeout |
308
+ | `useGetVersionCheckQuery`, `useLazyGetVersionCheckQuery` | `useUpgradeCheck` (app), or the generated `useGetVersionCheckQuery` in `store/apiSdk.ts` |
309
+ | `VersionCheckResponse` | `VersionCheckResponse` (app) |
310
+ | `refreshAuthToken` | `authProvider.refresh()` |
311
+ | `getTokenExpirationTimes`, `shouldShowStillThereModal` | Same names (auth), JWT apps only |
312
+ | `getFriendlyExpirationInfo` | Removed (debug string). Use `getTokenExpirationTimes` (auth). |
313
+ | `generateTags` | Removed. Codegen derives `providesTags` / `invalidatesTags` from OpenAPI tags. |
314
+ | `ListResponse`, `populateId` | Removed. List envelopes keep `{data, more, page, total}`; generated `<Name>Res` types describe them. |
315
+ | `createSessionApi` | Removed (no consumers). Use `createApiClient` (api). |
316
+
317
+ ### Session and auth (`authSlice`, `betterAuthSlice`, `betterAuthClient`, `betterAuthTypes`)
318
+
319
+ | Removed | Replacement |
320
+ |---------|-------------|
321
+ | `createBetterAuthClient`, `BetterAuthClient` | Same names (auth) |
322
+ | `createStorageAdapter`, `StorageAdapter`, `repairCookieJar`, `withCookieJarRepair` | Same names (auth) |
323
+ | `BetterAuthClientConfig`, `BetterAuthUser`, `BetterAuthSession`, `BetterAuthSessionData`, `BetterAuthOAuthProvider` | Same names (auth) |
324
+ | `BetterAuthClientInterface` | `BetterAuthSessionClientLike` (auth) |
325
+ | `generateBetterAuthSlice`, `BetterAuthSlice`, `GenerateBetterAuthSliceConfig`, `BetterAuthState` | `SessionProvider` + `useSession()` (auth); `createSessionStore` for non-React code |
326
+ | `selectBetterAuthState`, `selectBetterAuthIsAuthenticated`, `selectBetterAuthUserId`, `selectBetterAuthUser`, `selectBetterAuthIsLoading`, `selectBetterAuthError` | `useSession()` → `{isAuthenticated, userId, user, isLoading, error}` |
327
+ | `generateAuthSlice` (JWT) | `jwtAuthAdapter` + `SessionProvider source={adapter}` + `useJwtAuth({adapter})` (auth) |
328
+ | `generateProfileEndpoints` | Generated `useGetAuthMeQuery` / `usePatchAuthMeMutation` in `store/apiSdk.ts` |
329
+ | `EmailLoginRequest`, `EmailSignupRequest`, `GoogleLoginRequest`, `ResetPasswordRequest` | Same names (auth) |
330
+ | `UserResponse` | `JwtAuthResponse` (auth) |
331
+ | `selectCurrentUserId`, `useSelectCurrentUserId` | `useSession().userId` |
332
+ | `selectIsAuthenticating`, `useSelectIsAuthenticating` | `useJwtAuth().isAuthenticating` / `useSession().isLoading` |
333
+ | `selectLastTokenRefreshTimestamp` | Removed. Subscribe with `authProvider.onAuthChange`. |
334
+ | `getAuthToken` | `authProvider.getToken()` |
335
+ | `LOGOUT_ACTION_TYPE`, `TOKEN_REFRESHED_SUCCESS` | Removed (Redux action types). Use `SessionProvider` `onLogout` and `authProvider.onAuthChange`. |
336
+ | `RootState` | Removed (no Redux store) |
337
+ | `AUTH_DEBUG`, `logAuth` | Removed. Set `createSyncDb({debug})` for sync and auth-pause events. |
338
+
339
+ ### Config and platform (`constants`, `platform`)
340
+
341
+ | Removed | Replacement |
342
+ |---------|-------------|
343
+ | `baseUrl`, `baseWebsocketsUrl`, `baseTasksUrl` | Same names (config) |
344
+ | `resolveBaseUrls`, `resolveDevApiPort`, `BaseUrls`, `ExpoConstantsShape`, `SAME_ORIGIN_SENTINEL`, `DEFAULT_DEV_API_PORT` | Same names (config) |
345
+ | `IsWeb` | `isWeb` (config) or `Platform.OS === "web"` |
346
+ | — | New: `appHeaders()` (config) sends `App-Version` and `App-Platform` |
347
+
348
+ ### Sockets and realtime (`socket`, `useRealtimeDebug`, `constants`)
349
+
350
+ | Removed | Replacement |
351
+ |---------|-------------|
352
+ | `useSocketConnection`, `SocketConnection`, `UseSocketConnectionOptions` | The `createSyncDb` socket; `useSocketEvent` (react) and `syncDb.onSocketEvent` (root) |
353
+ | `useRealtimeDebug`, `setRealtimeDebug`, `isWebsocketsDebugEnabled`, `logSocket` | `createSyncDb({debug})`, `useSyncDebugLog` (react), terreno-mcp-local `get_syncdb_state` |
354
+
355
+ ### Feature flags (`useFeatureFlags`, `useTerrenoFeatureFlags`)
356
+
357
+ | Removed | Replacement |
358
+ |---------|-------------|
359
+ | `useFeatureFlags(api, options)`, `UseFeatureFlagsOptions` | `useFeatureFlags({client, skip, socket, userId})`, `UseFeatureFlagsOptions` (flags) |
360
+ | `resolveFeatureFlagsOptions` | Removed. Options are one object. |
361
+ | `useTerrenoFeatureFlags(api, options)`, `UseTerrenoFeatureFlagsOptions`, `UseTerrenoFeatureFlagsResult` | `useTerrenoFeatureFlags({client, skip, socket, userId})` and the same types (flags) |
362
+ | `TerrenoFlagDefinition`, `TerrenoFlagConfiguration` | Same names (flags) |
363
+ | `FlagsApi` | Removed. Pass an `ApiClient` as `client`. |
364
+
365
+ ### Offline queue (`offlineSlice`, `offlineMiddleware`, `offlineGate`, `useOfflineStatus`, `useServerStatus`)
366
+
367
+ | Removed | Replacement |
368
+ |---------|-------------|
369
+ | `offlineSlice`, `offlineReducer`, `setOnlineStatus`, `enqueue`, `dequeue`, `clearQueue`, `addConflict`, `dismissConflict`, `clearConflicts`, `setSyncing` | The syncdb outbox. Write with `useMutate`; conflicts in `useConflicts()` (react). |
370
+ | `OfflineState`, `QueuedMutation`, `ConflictRecord` | `SyncStatus` from `useSyncStatus()`; conflicts from `useConflicts()` / `listConflicts` (root) |
371
+ | `selectIsOnline`, `selectIsOnlineSafe`, `selectOfflineQueue`, `selectQueueLength`, `selectConflicts`, `selectUndismissedConflicts`, `selectIsSyncing` | `useSyncStatus()` → `{isOnline, queuedCount, conflictCount, failedCount, isSyncing}` and `useConflicts()` |
372
+ | `createOfflineMiddleware`, `OfflineMiddlewareConfig`, `isNetworkFetchError`, `shouldReplayQueuedMutation`, `buildOptimisticCreateItem` | Removed. syncdb applies writes locally and replays the outbox. |
373
+ | `configureOfflineMutationEndpoints`, `shouldDeferOfflineMutation` | Removed. Every syncdb write is queued. |
374
+ | `useOfflineStatus`, `OfflineStatus`, `useServerStatus`, `ServerStatus`, `ServerStatusOptions` | `useSyncStatus()` (react) |
375
+
376
+ ### App utilities (`useUpgradeCheck`, `usePermissions`, `devConsoleLogger`, `devStore`, `useMCPTools`, `useTerrenoChat`, `buildNumber`)
377
+
378
+ | Removed | Replacement |
379
+ |---------|-------------|
380
+ | `useUpgradeCheck` | `useUpgradeCheck` (app); runs on the request client from `ApiClientProvider` |
381
+ | `hasPermission`, `canOpenAdminPage`, `PermissionSet`, `PermissionRequest`, `ADMIN_PAGE_RESOURCE`, `ADMIN_PAGE_ACTION`, `ADMIN_PAGE_PERMISSION` | Same names (app) |
382
+ | `createPermissionSelectors`, `selectPermissions`, `useSelectPermissions`, `useCan`, `DEFAULT_PERMISSION_API_REDUCER_PATH` | Removed. Read `permissions` from `useGetAuthMeQuery()` and call `hasPermission` (app). |
383
+ | `installTerrenoDevConsoleLogger` | `installDevConsoleLogger` (app) |
384
+ | `resetTerrenoDevConsoleLoggerForTests` | Removed from the public API |
385
+ | `registerTerrenoDevStore` | Removed. `createSyncDb({debug})` registers the client for terreno-mcp-local (`get_syncdb_state`, `syncdb_snapshot`, `syncdb_action`). |
386
+ | `useMCPTools`, `MCPToolInfo`, `UseMCPToolsOptions`, `UseMCPToolsResult` | Same names (app); pass `authProvider` |
387
+ | `useTerrenoChat`, `UseTerrenoChatOptions` | Removed (no consumers). Use `useChat` from `@ai-sdk/react` with a bearer header from `authProvider.getToken()`, or `GPTChat` from `@terreno/ui`. |
388
+ | `@terreno/rtk/buildNumber`: `coerceBuildNumber`, `resolveBuildNumber` | `@terreno/syncdb/buildNumber` (node-only) |
389
+
390
+ ### Removed before 58
391
+
392
+ `realtimeList`, `realtimeDocument`, `setRealtimeSocket`, `getRealtimeSocket`,
393
+ `useSyncConnection`, and `RealtimeEvent` went with the legacy RTK realtime path. See
394
+ [Legacy RTK realtime is removed](https://github.com/TerrenoLabs/terreno/blob/master/mcp-server/src/docs/upgrades/58.0.0.md#legacy-rtk-realtime-is-removed).
525
395
 
526
396
  ## Further reading
527
397
 
528
- - [`docs/reference/syncdb.md`](../reference/syncdb.md) — full API reference
529
- - [`syncdb/README.md`](https://github.com/TerrenoLabs/terreno/blob/release-56.0.0/syncdb/README.md) — architecture and protocol detail
530
- - [`docs/how-to/configure-better-auth.md`](configure-better-auth.md) — Better Auth setup
398
+ - [`@terreno/syncdb` reference](../reference/syncdb.md)
399
+ - [Local-first data](../explanation/local-first-data.md)
400
+ - [Configure Better Auth](configure-better-auth.md)
401
+ - [Use syncdb correctly](use-syncdb-correctly.md)