@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.
- package/CHANGELOG.md +43 -0
- package/README.md +57 -3
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +17 -16
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
- package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
- package/dist/docs/prompts/debug.md +6 -4
- package/dist/docs/prompts/screen.md +11 -17
- package/dist/docs/resources/overview.md +5 -3
- package/dist/docs/resources/patterns.md +157 -238
- package/dist/docs/resources/syncdb.md +44 -16
- package/dist/docs/templates/bootstrap/CLAUDE.template.md +2 -2
- package/dist/docs/templates/bootstrap/cursorrules.template.md +7 -5
- package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
- package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
- package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
- package/dist/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
- package/dist/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
- package/dist/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
- package/dist/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
- package/dist/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
- package/dist/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
- package/dist/docs/upgrades/58.0.0-beta.1.md +504 -0
- package/dist/docs/upgrades/58.0.0.md +495 -4
- package/dist/docs/versioned/next/README.md +2 -2
- package/dist/docs/versioned/next/approval-rubric.md +2 -2
- package/dist/docs/versioned/next/explanation/admin-interface.md +6 -5
- package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
- package/dist/docs/versioned/next/explanation/ai-workflows.md +5 -3
- package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
- package/dist/docs/versioned/next/explanation/authentication.md +62 -43
- package/dist/docs/versioned/next/explanation/dependency-management.md +1 -1
- package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/dist/docs/versioned/next/explanation/example-coverage.md +2 -2
- package/dist/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
- package/dist/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
- package/dist/docs/versioned/next/explanation/local-first-data.md +11 -9
- package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/dist/docs/versioned/next/explanation/modular-api-design.md +2 -2
- package/dist/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
- package/dist/docs/versioned/next/explanation/source-rules.md +2 -2
- package/dist/docs/versioned/next/explanation/typescript-configuration.md +2 -4
- package/dist/docs/versioned/next/explanation/versioning-policy.md +3 -3
- package/dist/docs/versioned/next/how-to/README.md +3 -2
- package/dist/docs/versioned/next/how-to/add-feature-flags.md +50 -25
- package/dist/docs/versioned/next/how-to/add-organizations.md +2 -1
- package/dist/docs/versioned/next/how-to/admin-custom-home.md +8 -2
- package/dist/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
- package/dist/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
- package/dist/docs/versioned/next/how-to/build-admin-screens.md +10 -8
- package/dist/docs/versioned/next/how-to/build-for-web.md +1 -1
- package/dist/docs/versioned/next/how-to/call-external-apis.md +2 -2
- package/dist/docs/versioned/next/how-to/circleci.md +12 -6
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +114 -60
- package/dist/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
- package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
- package/dist/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
- package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
- package/dist/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
- package/dist/docs/versioned/next/how-to/github-actions-ci.md +2 -2
- package/dist/docs/versioned/next/how-to/in-app-notifications.md +1 -1
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
- package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
- package/dist/docs/versioned/next/how-to/product-announcements.md +6 -3
- package/dist/docs/versioned/next/how-to/rate-limiting.md +38 -16
- package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +2 -3
- package/dist/docs/versioned/next/how-to/set-up-social-login.md +1 -1
- package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
- package/dist/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
- package/dist/docs/versioned/next/how-to/upgrade-banner.md +5 -3
- package/dist/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
- package/dist/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
- package/dist/docs/versioned/next/how-to/websocket-integration.md +113 -402
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
- package/dist/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
- package/dist/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
- package/dist/docs/versioned/next/reference/README.md +1 -1
- package/dist/docs/versioned/next/reference/admin-frontend.md +85 -70
- package/dist/docs/versioned/next/reference/admin-spa.md +10 -4
- package/dist/docs/versioned/next/reference/ai.md +36 -4
- package/dist/docs/versioned/next/reference/announcements.md +3 -3
- package/dist/docs/versioned/next/reference/api.md +39 -24
- package/dist/docs/versioned/next/reference/cli.md +3 -4
- package/dist/docs/versioned/next/reference/environment-variables.md +16 -16
- package/dist/docs/versioned/next/reference/feature-flags.md +48 -7
- package/dist/docs/versioned/next/reference/jobs.md +2 -2
- package/dist/docs/versioned/next/reference/legacy/rtk.md +25 -600
- package/dist/docs/versioned/next/reference/mcp-server.md +441 -1158
- package/dist/docs/versioned/next/reference/syncdb.md +426 -16
- package/dist/docs/versioned/next/reference/test.md +28 -3
- package/dist/docs/versioned/next/reference/ui.md +132 -19
- package/dist/docs/versioned/next/tasks/native-module-baseline.md +8 -4
- package/dist/docs/versioned/next/tasks/rate-limiting.md +9 -2
- package/dist/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
- package/dist/docs/versioned/next/tasks/remove-rtk.md +155 -0
- package/dist/local/localTools.d.ts.map +1 -1
- package/dist/local/localTools.js +3 -54
- package/dist/local/localTools.js.map +1 -1
- package/dist/local/tools/runtime.d.ts +0 -21
- package/dist/local/tools/runtime.d.ts.map +1 -1
- package/dist/local/tools/runtime.js +0 -176
- package/dist/local/tools/runtime.js.map +1 -1
- package/dist/local/tools/syncDb.d.ts +0 -11
- package/dist/local/tools/syncDb.d.ts.map +1 -1
- package/dist/local/tools/syncDb.js +1 -5
- package/dist/local/tools/syncDb.js.map +1 -1
- package/dist/packageGuidelines.d.ts +3 -2
- package/dist/packageGuidelines.d.ts.map +1 -1
- package/dist/packageGuidelines.js +4 -11
- package/dist/packageGuidelines.js.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +17 -18
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +1 -8
- package/dist/resources.js.map +1 -1
- package/dist/search/chunker.d.ts +1 -1
- package/dist/search/chunker.d.ts.map +1 -1
- package/dist/search/inferPackages.d.ts +1 -1
- package/dist/search/inferPackages.js +5 -5
- package/dist/search/inferPackages.js.map +1 -1
- package/dist/tools.d.ts +6 -0
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +126 -119
- package/dist/tools.js.map +1 -1
- package/package.json +6 -6
- package/src/docs/guidelines/admin-frontend/guidelines/core.md +6 -5
- package/src/docs/guidelines/ui/guidelines/core.md +1 -1
- package/src/docs/prompts/debug.md +6 -4
- package/src/docs/prompts/screen.md +11 -17
- package/src/docs/resources/overview.md +5 -3
- package/src/docs/resources/patterns.md +157 -238
- package/src/docs/resources/syncdb.md +44 -16
- package/src/docs/templates/bootstrap/CLAUDE.template.md +2 -2
- package/src/docs/templates/bootstrap/cursorrules.template.md +7 -5
- package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +8 -12
- package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +75 -52
- package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +46 -108
- package/src/docs/templates/bootstrap/frontend/app/signup.template.tsx +53 -0
- package/src/docs/templates/bootstrap/frontend/lib/betterAuth.template.md +18 -0
- package/src/docs/templates/bootstrap/frontend/store/apiClient.template.md +33 -0
- package/src/docs/templates/bootstrap/frontend/store/apiSdk.template.md +76 -0
- package/src/docs/templates/bootstrap/frontend/store/syncdb.template.md +56 -0
- package/src/docs/templates/bootstrap/frontend/utils/index.template.md +0 -4
- package/src/docs/upgrades/58.0.0-beta.1.md +504 -0
- package/src/docs/upgrades/58.0.0.md +495 -4
- package/src/docs/versioned/next/README.md +2 -2
- package/src/docs/versioned/next/approval-rubric.md +2 -2
- package/src/docs/versioned/next/explanation/admin-interface.md +6 -5
- package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +1 -1
- package/src/docs/versioned/next/explanation/ai-workflows.md +5 -3
- package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +16 -11
- package/src/docs/versioned/next/explanation/authentication.md +62 -43
- package/src/docs/versioned/next/explanation/dependency-management.md +1 -1
- package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/src/docs/versioned/next/explanation/example-coverage.md +2 -2
- package/src/docs/versioned/next/explanation/explicit-any-policy.md +5 -6
- package/src/docs/versioned/next/explanation/gcp-hosting-architecture.md +1 -1
- package/src/docs/versioned/next/explanation/local-first-data.md +11 -9
- package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/src/docs/versioned/next/explanation/modular-api-design.md +2 -2
- package/src/docs/versioned/next/explanation/no-barrel-imports.md +5 -5
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +4 -3
- package/src/docs/versioned/next/explanation/source-rules.md +2 -2
- package/src/docs/versioned/next/explanation/typescript-configuration.md +2 -4
- package/src/docs/versioned/next/explanation/versioning-policy.md +3 -3
- package/src/docs/versioned/next/how-to/README.md +3 -2
- package/src/docs/versioned/next/how-to/add-feature-flags.md +50 -25
- package/src/docs/versioned/next/how-to/add-organizations.md +2 -1
- package/src/docs/versioned/next/how-to/admin-custom-home.md +8 -2
- package/src/docs/versioned/next/how-to/admin-custom-widget.md +0 -1
- package/src/docs/versioned/next/how-to/agent-ui-asks.md +6 -6
- package/src/docs/versioned/next/how-to/build-admin-screens.md +10 -8
- package/src/docs/versioned/next/how-to/build-for-web.md +1 -1
- package/src/docs/versioned/next/how-to/call-external-apis.md +2 -2
- package/src/docs/versioned/next/how-to/circleci.md +12 -6
- package/src/docs/versioned/next/how-to/configure-better-auth.md +114 -60
- package/src/docs/versioned/next/how-to/connect-mcp-service-token.md +10 -4
- package/src/docs/versioned/next/how-to/create-a-terreno-app.md +14 -2
- package/src/docs/versioned/next/how-to/debug-with-mcp.md +8 -13
- package/src/docs/versioned/next/how-to/expose-mcp-tools.md +15 -4
- package/src/docs/versioned/next/how-to/generate-the-client-sdk.md +33 -0
- package/src/docs/versioned/next/how-to/github-actions-ci.md +2 -2
- package/src/docs/versioned/next/how-to/in-app-notifications.md +1 -1
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +318 -447
- package/src/docs/versioned/next/how-to/password-reset.md +1 -1
- package/src/docs/versioned/next/how-to/product-announcements.md +6 -3
- package/src/docs/versioned/next/how-to/rate-limiting.md +38 -16
- package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +19 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +2 -3
- package/src/docs/versioned/next/how-to/set-up-social-login.md +1 -1
- package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +2 -2
- package/src/docs/versioned/next/how-to/track-upstream-expo.md +1 -1
- package/src/docs/versioned/next/how-to/upgrade-banner.md +5 -3
- package/src/docs/versioned/next/how-to/upgrade-terreno.md +5 -5
- package/src/docs/versioned/next/how-to/use-syncdb-correctly.md +1 -1
- package/src/docs/versioned/next/how-to/websocket-integration.md +113 -402
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
- package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +5 -0
- package/src/docs/versioned/next/implementationPlans/native-module-baseline.md +29 -5
- package/src/docs/versioned/next/implementationPlans/remove-legacy-realtime.md +1 -1
- package/src/docs/versioned/next/implementationPlans/remove-rtk.md +185 -0
- package/src/docs/versioned/next/reference/README.md +1 -1
- package/src/docs/versioned/next/reference/admin-frontend.md +85 -70
- package/src/docs/versioned/next/reference/admin-spa.md +10 -4
- package/src/docs/versioned/next/reference/ai.md +36 -4
- package/src/docs/versioned/next/reference/announcements.md +3 -3
- package/src/docs/versioned/next/reference/api.md +39 -24
- package/src/docs/versioned/next/reference/cli.md +3 -4
- package/src/docs/versioned/next/reference/environment-variables.md +16 -16
- package/src/docs/versioned/next/reference/feature-flags.md +48 -7
- package/src/docs/versioned/next/reference/jobs.md +2 -2
- package/src/docs/versioned/next/reference/legacy/rtk.md +25 -600
- package/src/docs/versioned/next/reference/mcp-server.md +441 -1158
- package/src/docs/versioned/next/reference/syncdb.md +426 -16
- package/src/docs/versioned/next/reference/test.md +28 -3
- package/src/docs/versioned/next/reference/ui.md +132 -19
- package/src/docs/versioned/next/tasks/native-module-baseline.md +8 -4
- package/src/docs/versioned/next/tasks/rate-limiting.md +9 -2
- package/src/docs/versioned/next/tasks/remove-legacy-realtime.md +5 -5
- package/src/docs/versioned/next/tasks/remove-rtk.md +155 -0
- package/dist/docs/guidelines/rtk/guidelines/core.md +0 -26
- package/dist/docs/resources/rtk.md +0 -181
- package/dist/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
- package/dist/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
- package/dist/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
- package/dist/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
- package/dist/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
- package/dist/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
- package/dist/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
- package/src/docs/guidelines/rtk/guidelines/core.md +0 -26
- package/src/docs/resources/rtk.md +0 -181
- package/src/docs/templates/bootstrap/frontend/openapi-config.template.md +0 -16
- package/src/docs/templates/bootstrap/frontend/scripts/generate-sdk.template.md +0 -49
- package/src/docs/templates/bootstrap/frontend/store/appState.template.md +0 -45
- package/src/docs/templates/bootstrap/frontend/store/errors.template.md +0 -51
- package/src/docs/templates/bootstrap/frontend/store/index.template.md +0 -86
- package/src/docs/templates/bootstrap/frontend/store/openApiSdk.template.md +0 -49
- package/src/docs/templates/bootstrap/frontend/store/sdk.template.md +0 -80
|
@@ -1,530 +1,401 @@
|
|
|
1
|
-
# How to
|
|
1
|
+
# How to upgrade from @terreno/rtk to @terreno/syncdb (Terreno 58)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Terreno 58 deletes `@terreno/rtk` and every Redux dependency. `@terreno/syncdb` is the only
|
|
4
|
+
frontend data and session layer:
|
|
4
5
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 {
|
|
13
|
-
import {
|
|
23
|
+
import {installDevConsoleLogger} from "@terreno/syncdb/app";
|
|
24
|
+
import {baseUrl} from "@terreno/syncdb/config";
|
|
14
25
|
|
|
15
|
-
|
|
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
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
84
|
+
export const authProvider = betterAuthAdapter(bridgeBetterAuthReactClient(betterAuthClient));
|
|
60
85
|
|
|
61
86
|
export const syncDb = createSyncDb({
|
|
62
87
|
authProvider,
|
|
63
88
|
baseUrl,
|
|
64
|
-
collections: [
|
|
65
|
-
name: "
|
|
66
|
-
|
|
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
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
114
|
+
## 4. Replace the Redux store with providers
|
|
103
115
|
|
|
104
116
|
```tsx
|
|
105
|
-
|
|
106
|
-
import {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
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 {
|
|
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
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
|
|
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
|
-
##
|
|
197
|
+
## 6. Move flags, pushes, and app utilities
|
|
204
198
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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
|
-
|
|
204
|
+
// Flags ride the sync socket; no second socket.
|
|
205
|
+
useTerrenoFeatureFlags({client: apiClient, skip: !userId, socket: syncDb, userId});
|
|
210
206
|
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
|
|
217
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
-
|
|
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
|
-
|
|
269
|
+
## 8. Update ui and admin call sites
|
|
346
270
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
|
|
280
|
+
## Copy the example app's changes
|
|
352
281
|
|
|
353
|
-
|
|
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
|
-
|
|
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
|
-
|
|
378
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
###
|
|
392
|
-
|
|
393
|
-
|
|
|
394
|
-
|
|
395
|
-
|
|
|
396
|
-
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
`
|
|
422
|
-
`
|
|
423
|
-
|
|
424
|
-
`
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
|
471
|
-
|
|
|
472
|
-
| `
|
|
473
|
-
| `
|
|
474
|
-
|
|
|
475
|
-
|
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
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
|
-
- [
|
|
529
|
-
- [
|
|
530
|
-
- [
|
|
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)
|