@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,28 +1,33 @@
|
|
|
1
|
-
# RTK
|
|
1
|
+
# RTK removal announcement (draft)
|
|
2
2
|
|
|
3
3
|
**Do not post until maintainers review.** Target: GitHub Discussions → Announcements.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
## `@terreno/rtk`
|
|
7
|
+
## `@terreno/rtk` and Redux are removed in Terreno 58
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Terreno **58.0.0** deletes `@terreno/rtk` and every Redux dependency. [`@terreno/syncdb`](https://github.com/TerrenoLabs/terreno/blob/master/docs/reference/syncdb.md) is now the only frontend data and session layer. `@terreno/rtk` is not published for 58.
|
|
10
10
|
|
|
11
11
|
### What changed
|
|
12
12
|
|
|
13
|
-
- **
|
|
14
|
-
- **
|
|
15
|
-
- **
|
|
13
|
+
- **Collections:** `useQuery` / `useMutate` read and write a local-first store (unchanged since 56).
|
|
14
|
+
- **Every other route** (`/auth/me`, modelRouter actions, AI, consents, announcements): a request client in `@terreno/syncdb/api` with typed hooks generated by `terreno-syncdb-codegen --api-out`. Hook names match the old RTK codegen.
|
|
15
|
+
- **Session:** `SessionProvider` + `useSession()` from `@terreno/syncdb/auth` (Better Auth, or `jwtAuthAdapter` for JWT backends).
|
|
16
|
+
- **Flags and pushes:** ride the syncdb socket (`@terreno/syncdb/flags`, `useSocketEvent`). One socket per app.
|
|
17
|
+
- **App utilities:** base URLs in `@terreno/syncdb/config`; upgrade check, permissions, dev logger, and MCP tools in `@terreno/syncdb/app`.
|
|
18
|
+
- **Backend:** `GptHistory` and `McpServiceToken` are synced collections with String ids. Two data migrations ship with 58.
|
|
16
19
|
|
|
17
20
|
### What you need to do
|
|
18
21
|
|
|
19
|
-
1.
|
|
20
|
-
2.
|
|
21
|
-
3.
|
|
22
|
+
1. Run the `GptHistory` and `McpServiceToken` migrations before deploying 58 frontends.
|
|
23
|
+
2. Follow the [upgrade guide](https://github.com/TerrenoLabs/terreno/blob/master/docs/how-to/migrate-rtk-to-syncdb.md). It maps every removed export to its replacement.
|
|
24
|
+
3. Remove `@terreno/rtk`, `@reduxjs/toolkit`, `react-redux`, `redux-persist`, and `@rtk-query/codegen-openapi` from the app.
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
Apps that stay on 57.x keep `@terreno/rtk` 57.x.
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
### Why
|
|
29
|
+
|
|
30
|
+
One data layer means one socket, one auth provider, and one cache for every request. See [local-first data](https://github.com/TerrenoLabs/terreno/blob/master/docs/explanation/local-first-data.md).
|
|
26
31
|
|
|
27
32
|
### Questions
|
|
28
33
|
|
|
@@ -6,9 +6,9 @@ Understanding how authentication works in @terreno/api — Better Auth sessions
|
|
|
6
6
|
|
|
7
7
|
**Better Auth is the default path** for new Terreno apps. It provides session-based authentication with MongoDB storage, built-in social OAuth (Google, GitHub, Apple), and clean integration with `@terreno/syncdb` (`betterAuthAdapter`, `RealtimeApp` socket sessions).
|
|
8
8
|
|
|
9
|
-
Set `AUTH_PROVIDER=better-auth` and register `BetterAuthApp` on the server. On the client, use `createBetterAuthClient`
|
|
9
|
+
Set `AUTH_PROVIDER=better-auth` and register `BetterAuthApp` on the server. On the client, use `createBetterAuthClient` and `SessionProvider` / `useSession()` from `@terreno/syncdb/auth` for session state (React context, no Redux), then wire `betterAuthAdapter` into `createSyncDb` and the API client.
|
|
10
10
|
|
|
11
|
-
**JWT/Passport (legacy)** remains supported through the current major release line for existing deployments. It uses stateless tokens, Passport strategies (email/password, GitHub OAuth, anonymous), and
|
|
11
|
+
**JWT/Passport (legacy)** remains supported through the current major release line for existing deployments. It uses stateless tokens, Passport strategies (email/password, GitHub OAuth, anonymous), and client-side token refresh via `jwtAuthAdapter` from `@terreno/syncdb/auth`. New projects should not start on JWT unless they have a specific requirement (custom token contracts, non-cookie clients, or a phased migration).
|
|
12
12
|
|
|
13
13
|
Both systems can run in parallel during migration (`AUTH_PROVIDER` selects the primary path; legacy JWT routes stay available).
|
|
14
14
|
|
|
@@ -23,7 +23,7 @@ Both systems can run in parallel during migration (`AUTH_PROVIDER` selects the p
|
|
|
23
23
|
| `@terreno/syncdb` local-first data | Non-cookie API clients only |
|
|
24
24
|
| Socket sessions via `RealtimeApp` | Gradual migration in progress |
|
|
25
25
|
|
|
26
|
-
**Setup:** [Configure Better Auth](../how-to/configure-better-auth.md). **Data layer:** migrate auth before or with syncdb — see [
|
|
26
|
+
**Setup:** [Configure Better Auth](../how-to/configure-better-auth.md). **Data layer:** migrate auth before or with syncdb — see [Upgrade from @terreno/rtk to @terreno/syncdb](../how-to/migrate-rtk-to-syncdb.md) §3–4.
|
|
27
27
|
|
|
28
28
|
## Authentication Strategies
|
|
29
29
|
|
|
@@ -68,7 +68,7 @@ the `X-Terreno-Auth-Challenge` header instead:
|
|
|
68
68
|
never set a session cookie, and a tampered value fails Better Auth's signature check (the 2FA
|
|
69
69
|
verify answers 401, a passkey verify 400 `CHALLENGE_NOT_FOUND`, and a tampered trust value
|
|
70
70
|
just asks for the second factor).
|
|
71
|
-
4.
|
|
71
|
+
4. `@terreno/syncdb/auth`'s bearer mode stores the header under `${storagePrefix}_auth_challenge` in the same
|
|
72
72
|
`bearerStorage` as the bearer token. It never stores a token from a response that asks for a
|
|
73
73
|
second factor. It drops the 2FA challenge and `dont_remember` after a successful verify, and
|
|
74
74
|
everything but the trust-device value on sign-out. The trust-device value stays until the server
|
|
@@ -147,7 +147,7 @@ short-lived cookie. The callback rejects a response without matching state.
|
|
|
147
147
|
|
|
148
148
|
### MCP service tokens (opt-in, `/mcp` only)
|
|
149
149
|
|
|
150
|
-
Personal `mcp_` keys for remote MCP clients (Perplexity, Cursor JSON config) that cannot hold a session cookie. `TerrenoApp` must set `mcpServiceTokens`. Users mint keys from `POST /mcp/service-tokens` or **Profile → MCP connections**. The plaintext secret is returned **once**; the database stores SHA-256 `tokenHash`.
|
|
150
|
+
Personal `mcp_` keys for remote MCP clients (Perplexity, Cursor JSON config) that cannot hold a session cookie. `TerrenoApp` must set `mcpServiceTokens`. Users mint keys from `POST /mcp/service-tokens/create` or **Profile → MCP connections**. The plaintext secret is returned **once**; the database stores SHA-256 `tokenHash`.
|
|
151
151
|
|
|
152
152
|
`extractUserFromHeaders` on `/mcp` tries a `mcp_` Bearer first, then Better Auth, then JWT. A match loads the owning `User` and updates `lastUsedAt`. Revoked, expired, or disabled-user keys resolve to no user, and so does a key minted before the user's last password reset or session revocation (`tokenEpoch` changed since it was issued). MCP OAuth grants end the same way. The same Bearer is ignored on REST, sync, admin, and on the mint/list/revoke routes (a key cannot mint another key).
|
|
153
153
|
|
|
@@ -210,19 +210,21 @@ modelRouter(Model, {
|
|
|
210
210
|
|
|
211
211
|
### Token Payload
|
|
212
212
|
|
|
213
|
-
Access tokens contain:
|
|
213
|
+
Access and refresh tokens contain:
|
|
214
214
|
|
|
215
215
|
``````json
|
|
216
216
|
{
|
|
217
|
-
"
|
|
218
|
-
"
|
|
219
|
-
"
|
|
220
|
-
"
|
|
221
|
-
"
|
|
217
|
+
"id": "507f1f77bcf86cd799439011", // User ID (the frontend reads the signed-in user from it)
|
|
218
|
+
"sid": "4f9c…", // Session ID, kept across refreshes
|
|
219
|
+
"te": 0, // User token epoch; bumping it revokes refresh tokens
|
|
220
|
+
"iat": 1709000000, // Issued at (timestamp)
|
|
221
|
+
"exp": 1709000900, // Expires at (timestamp)
|
|
222
|
+
"iss": "your-app-name" // Issuer (from TOKEN_ISSUER env var)
|
|
222
223
|
}
|
|
223
224
|
``````
|
|
224
225
|
|
|
225
|
-
|
|
226
|
+
Add claims with `authOptions.generateJWTPayload`. `id`, `sid`, and `te` always win over
|
|
227
|
+
custom claims:
|
|
226
228
|
|
|
227
229
|
``````typescript
|
|
228
230
|
setupServer({
|
|
@@ -238,42 +240,58 @@ setupServer({
|
|
|
238
240
|
|
|
239
241
|
### Token Refresh Flow
|
|
240
242
|
|
|
241
|
-
1.
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
243
|
+
1. Before each request, the frontend checks the stored access token. With under two minutes
|
|
244
|
+
left (or already expired), it calls `POST /auth/refresh_token` with the refresh token first.
|
|
245
|
+
2. If a request still returns `401 Unauthorized`, the frontend refreshes and retries the
|
|
246
|
+
request once.
|
|
247
|
+
3. The backend verifies the refresh token and the user's token epoch, then returns a new
|
|
248
|
+
access and refresh token pair (`{data: {token, refreshToken}}`).
|
|
249
|
+
4. Concurrent requests share one refresh call.
|
|
250
|
+
5. If the backend rejects the refresh, the frontend clears both tokens and signs out. A
|
|
251
|
+
network failure or timeout keeps the session and the current token.
|
|
248
252
|
|
|
249
|
-
|
|
253
|
+
`jwtAuthAdapter` from `@terreno/syncdb/auth` does this for the syncdb request client and
|
|
254
|
+
`createSyncDb` (it replaces `@terreno/rtk`'s `emptyApi` base query).
|
|
250
255
|
|
|
251
256
|
## Frontend Integration
|
|
252
257
|
|
|
253
|
-
###
|
|
258
|
+
### Session State (Better Auth)
|
|
254
259
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
import {configureStore} from "@reduxjs/toolkit";
|
|
258
|
-
import {openapi} from "./openApiSdk";
|
|
260
|
+
`@terreno/syncdb/auth` owns the frontend session. One Better Auth client feeds three
|
|
261
|
+
consumers:
|
|
259
262
|
|
|
260
|
-
|
|
263
|
+
- `SessionProvider` / `useSession()` — `{userId, user, isLoading, isAuthenticated, error,
|
|
264
|
+
logout, refresh}` for screens and routing.
|
|
265
|
+
- `betterAuthAdapter(bridgeBetterAuthReactClient(client))` — the `AuthProvider` that
|
|
266
|
+
`createSyncDb` and the API client use for bearer tokens and 401 refresh.
|
|
267
|
+
- The Better Auth client itself — `signIn.email`, `signIn.social`, password reset.
|
|
261
268
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
269
|
+
The session store reads `getSession()` on start and then follows Better Auth's session atom,
|
|
270
|
+
which fires after sign-in, sign-out, and user switches. A failed background read keeps the
|
|
271
|
+
current session: a network blip is not a logout. `logout()` signs out of Better Auth, signs
|
|
272
|
+
out syncdb clients (which wipe local data when created with `wipeOnSignOut`), runs the app's
|
|
273
|
+
`onLogout`, and clears the session. This replaces the `@terreno/rtk` Better Auth Redux slice.
|
|
274
|
+
See [syncdb reference: Auth](../reference/syncdb.md#auth).
|
|
275
|
+
|
|
276
|
+
### Session State (JWT, legacy)
|
|
277
|
+
|
|
278
|
+
JWT apps use the same `SessionProvider` / `useSession()` with a different session source.
|
|
279
|
+
`jwtAuthAdapter({baseUrl})` returns one object that is both:
|
|
280
|
+
|
|
281
|
+
- the `AuthProvider` for `createSyncDb` and `createApiClient` (bearer token, refresh before
|
|
282
|
+
expiry, refresh on 401), and
|
|
283
|
+
- the `source` for `SessionProvider` (it pushes every sign-in and sign-out to the session).
|
|
284
|
+
|
|
285
|
+
`useJwtAuth({adapter})` gives screens `login`, `signup`, `googleLogin`, `resetPassword`, and
|
|
286
|
+
`logout`, plus `isAuthenticating` and `error`. `logout` goes through the `SessionProvider`, so
|
|
287
|
+
syncdb clients sign out and `onLogout` runs. The signed-in user is the `id` claim of the stored
|
|
288
|
+
token (`user` is `{id}`); load the profile from `/auth/me`. A refresh the server rejects signs
|
|
289
|
+
the session out on its own. This replaces `@terreno/rtk`'s `generateAuthSlice` and Redux store.
|
|
290
|
+
See [syncdb reference: JWT auth](../reference/syncdb.md#jwt-auth-legacy).
|
|
271
291
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
- Login/logout state management
|
|
276
|
-
- Auth header injection for all API requests
|
|
292
|
+
`getTokenExpirationTimes()` and `shouldShowStillThereModal()` read the stored tokens for
|
|
293
|
+
"are you still there?" prompts (the modal shows with 65 seconds or less left on the refresh
|
|
294
|
+
token).
|
|
277
295
|
|
|
278
296
|
### Token Storage
|
|
279
297
|
|
|
@@ -286,7 +304,8 @@ export const store = configureStore({
|
|
|
286
304
|
- Falls back to localStorage
|
|
287
305
|
- SSR-safe (checks `typeof window`)
|
|
288
306
|
|
|
289
|
-
**Storage keys
|
|
307
|
+
**Storage keys** (the same keys `@terreno/rtk` used, so signed-in users stay signed in after
|
|
308
|
+
the move):
|
|
290
309
|
- `AUTH_TOKEN` — Access token
|
|
291
310
|
- `REFRESH_TOKEN` — Refresh token
|
|
292
311
|
|
|
@@ -368,7 +387,7 @@ address is the signal that lets the owner react. Status codes and messages are i
|
|
|
368
387
|
- Use HTTPS in production
|
|
369
388
|
- Validate token issuer (`TOKEN_ISSUER`)
|
|
370
389
|
- Set appropriate token expiration times
|
|
371
|
-
-
|
|
390
|
+
- `TerrenoApp` rate limiting (on by default) puts login/signup/refresh in the stricter `auth` bucket (20 / 15 min). Keep it on and pass `trustProxy: 1` on Cloud Run. See [Rate limiting](../how-to/rate-limiting.md).
|
|
372
391
|
- Keep login/signup/refresh reachable when the client still sends an expired access JWT. Those three routes skip JWT verification; `/auth/me` and other APIs still 401.
|
|
373
392
|
- Log authentication failures
|
|
374
393
|
|
|
@@ -538,4 +557,4 @@ See [Receive inbound webhooks](../how-to/inbound-webhooks.md).
|
|
|
538
557
|
- [Add GitHub OAuth](../how-to/add-github-oauth.md)
|
|
539
558
|
- [Create a model](../how-to/create-a-model.md)
|
|
540
559
|
- [API reference](../reference/api.md)
|
|
541
|
-
- [@terreno/rtk
|
|
560
|
+
- [Upgrade from @terreno/rtk to @terreno/syncdb](../how-to/migrate-rtk-to-syncdb.md)
|
|
@@ -76,7 +76,7 @@ JavaScript packages, GitHub Actions, and a few Expo JS-only names may update. Me
|
|
|
76
76
|
| `github-actions` | `github-actions` at `/` |
|
|
77
77
|
| `root` | `bun` at `/` (root catalog and root tools) |
|
|
78
78
|
| `backend` | `bun` under api, ai, backends, … |
|
|
79
|
-
| `frontend` | `bun` under ui,
|
|
79
|
+
| `frontend` | `bun` under ui, syncdb, admin-frontend, demo, apps, … |
|
|
80
80
|
|
|
81
81
|
Frontend ignores catalog-managed names so those fixes land once, in the root catalog. Root ignores fingerprint-skip names, so a security fix for a native/Expo package never opens a PR; handle it by hand in a release. `dependabot-auto-merge.yml` approves patch and minor Dependabot PRs and enables squash auto-merge, so those security PRs merge when required checks pass. A semver-major security PR (or a group containing one) gets no approval or auto-merge and waits for a human. When one fails because the fix needs code changes, fold the bump into the rolling PR ([Update dependencies](../how-to/update-dependencies.md)); never push to the `dependabot/*` branch.
|
|
82
82
|
|
|
@@ -32,7 +32,7 @@ Use [MongoDB Atlas](https://www.mongodb.com/atlas) or a single-node replica set
|
|
|
32
32
|
|
|
33
33
|
**3 — Short-lived serverless timeouts**
|
|
34
34
|
|
|
35
|
-
The
|
|
35
|
+
The syncdb socket client enables reconnection (`reconnection: true` in `syncdb/src/sync/socketTransport.ts`). When the backend drops idle connections (for example Cloud Run's default 300s request timeout), clients reconnect and catch up from their sync cursors, but deltas may lag until the new socket attaches.
|
|
36
36
|
|
|
37
37
|
**5 — Build-time API URL**
|
|
38
38
|
|
|
@@ -19,8 +19,8 @@ that file (and its imports) to a working example. Gaps stay explicit.
|
|
|
19
19
|
| UI blocks | yes — `uiBlocks` with `repair: true`, `exportDataset`, and the opt-in `scaleStepper` and `toggleChecklist` callbacks in `example-backend/src/ai/hostActions.ts`, wired from `example-backend/src/api/ai.ts`; `todoStats` in `example-backend/src/ai/tools.ts` stores a `ref` dataset; `findPhotos` in the same file cites the generated photo library, and `example-backend/src/api/photoLibrary.ts` signs its URLs | yes — `example-frontend/app/(tabs)/ai.tsx` (`uiBlocks`, dataset and action hooks, `hostActions` includes `exportDataset`, `scaleStepper`, and `toggleChecklist`, `resolveImage` resolves `file:` photo ids through `useLazyPhotoLibraryUrlQuery`) | e2e mocks a document with one inline and one `ref` chart. A second e2e test stores the Sunday roast reply (`blocks/src/fixtures/golden/sunday-roast.yaml` with `file:` photo ids) in a real `GptHistory`, streams it from the mocked model, and runs the stepper and checklist through the real `POST /gpt/actions`; photo URLs come from the real `GET /photoLibrary/:id/url`, and only the image download is stubbed (`example-frontend/e2e/helpers/seedGptHistory.ts`). Steps: [agent-ui-blocks.md](../how-to/agent-ui-blocks.md). |
|
|
20
20
|
| Agent asks | yes — `asks: {approvals}` in `example-backend/src/api/ai.ts`, with the per-request `deleteCompletedTodos` approval tool from `example-backend/src/api/todoTools.ts`; it also passes `chat` to `addGptHistoryRoutes` for the headless `pendingAsks` and `turn` endpoints; keyless scripted agent with short compact-surface replies in `example-backend/src/api/demoAgent.ts` | yes — `example-frontend/app/(tabs)/ai.tsx` (`onAskSubmit`, `askErrors`), helpers in `example-frontend/lib/gptAsks.ts` | Without `GEMINI_API_KEY`, "Help me pick a plan" asks a `choice` in chat, "Pick toppings" asks a `select: "many"` `choice` with an Other field, "Send the weekly report" and "Archive old chats" ask a `confirm` (the second `destructive`), "Draft an announcement" asks a `markdown` edit of a draft, "Fill in the invoice details" asks a `form` with eight typed fields, "Upload a receipt" asks for `files`, sent as uploads when `GCS_BUCKET` is set and as data URLs otherwise, and "Delete my completed todos" calls `deleteCompletedTodos`, which runs only after the user approves the server's destructive approval card. e2e: `example-frontend/e2e/ai-chat.spec.ts`. Small clients: the curl walkthrough in the how-to and the demo's watch-sized `SimpleAskCard` story. Steps: [agent-ui-asks.md](../how-to/agent-ui-asks.md). |
|
|
21
21
|
| AI structured output | no | no | `@terreno/ai` supports structured generation; the example only streams chat. Recorded gap — not claimed by a current tutorial. |
|
|
22
|
-
| Feature flags + live updates | yes — `FeatureFlagsApp` in `example-backend/src/server.ts`, seed `example-backend/src/scripts/seed-feature-flags.ts` | yes — `useTerrenoFeatureFlags` in `example-frontend/app/_layout.tsx` |
|
|
23
|
-
| Websockets / realtime | yes — `RealtimeApp` in `example-backend/src/server.ts`, `example-backend/src/websockets.ts` | yes — `
|
|
22
|
+
| Feature flags + live updates | yes — `FeatureFlagsApp` in `example-backend/src/server.ts`, seed `example-backend/src/scripts/seed-feature-flags.ts` | yes — `useTerrenoFeatureFlags` (`@terreno/syncdb/flags`) in `example-frontend/app/_layout.tsx` | `featureFlagsChanged` pushes ride the syncdb socket; the app opens one socket. |
|
|
23
|
+
| Websockets / realtime | yes — `RealtimeApp` in `example-backend/src/server.ts`, `example-backend/src/websockets.ts` | yes — the `syncDb` client socket (`example-frontend/store/syncdb.ts`) | Sync deltas and app events (`useSocketEvent`) share the one syncdb socket (`SyncApp` + `@terreno/syncdb`). |
|
|
24
24
|
| Consent forms | yes — `example-backend/src/consentDefinitions.ts`, seed scripts | yes — `example-frontend/app/(tabs)/documents.tsx` (Consents section), admin `example-frontend/app/admin/consent-forms/` | First-login flow is covered by e2e (`example-frontend/e2e/consents.spec.ts`). |
|
|
25
25
|
| Product announcements | yes — `AnnouncementsApp` in `example-backend/src/server.ts`, release upload token via `ANNOUNCEMENTS_UPLOAD_TOKEN`, seed `example-backend/src/scripts/seed-announcements.ts`, sample release pack `example-backend/announcements/releases/1.14.0/` with `bun run announcements:pack` | yes — `AnnouncementNavigator` in `example-frontend/app/_layout.tsx` and admin announcement routes | Seeded examples are drafts, so fresh databases do not interrupt sign-in. Release packs support multiple Markdown surfaces and idempotent draft or explicit live API import. |
|
|
26
26
|
| What's new banner | n/a — generated from `CHANGELOG.md` by `bun run whats-new:generate` | yes — `WhatsNewBanner` in `example-frontend/components/SyncTodosScreen.tsx`, data in `example-frontend/constants/whatsNew.generated.json` | `bun run check:whats-new` keeps the data in sync with each release. |
|
|
@@ -15,8 +15,7 @@ Prefer `unknown` (and narrow) or a precise type. Use `as unknown as T` over `as
|
|
|
15
15
|
Explicit `any` is acceptable only at **framework or library boundaries** where types are not expressible in TypeScript today, for example:
|
|
16
16
|
|
|
17
17
|
- Mongoose model boundaries that inspect consumer-defined schemas (`api/src/populate.ts`)
|
|
18
|
-
-
|
|
19
|
-
- Dynamic RTK hook lookup where endpoint names are not statically known (`admin-frontend`)
|
|
18
|
+
- Dynamic lookups where member names are not statically known
|
|
20
19
|
- Test mocks with type-erased doubles (`*.test.ts`, `*.isolated.tsx`)
|
|
21
20
|
|
|
22
21
|
Every allowed use must be **lint-clean** and **documented** (see below).
|
|
@@ -38,15 +37,15 @@ The audit script (`bun run check:explicit-any`) classifies each explicit `any`:
|
|
|
38
37
|
When `any` cannot be removed, add **both** comments on the line above (or a file header for tests):
|
|
39
38
|
|
|
40
39
|
```typescript
|
|
41
|
-
// noExplicitAny:
|
|
42
|
-
// biome-ignore lint/suspicious/noExplicitAny: dynamic
|
|
43
|
-
const
|
|
40
|
+
// noExplicitAny: plugin methods are registered at runtime; names are not statically known
|
|
41
|
+
// biome-ignore lint/suspicious/noExplicitAny: dynamic method lookup on a runtime-registered plugin
|
|
42
|
+
const handler = (plugin as any)[`handle${name}`];
|
|
44
43
|
```
|
|
45
44
|
|
|
46
45
|
For test files with many mocks, a file-level header is fine:
|
|
47
46
|
|
|
48
47
|
```typescript
|
|
49
|
-
// noExplicitAny: test mocks use type-erased
|
|
48
|
+
// noExplicitAny: test mocks use type-erased request client doubles
|
|
50
49
|
// biome-ignore-all lint/suspicious/noExplicitAny: test mock typing
|
|
51
50
|
```
|
|
52
51
|
|
|
@@ -191,7 +191,7 @@ gs://bucket/
|
|
|
191
191
|
|
|
192
192
|
### Production Deploy
|
|
193
193
|
|
|
194
|
-
1. **Trigger**: Push to `master` branch with changes to `demo/**`, `ui/**`, `example-frontend/**`, `
|
|
194
|
+
1. **Trigger**: Push to `master` branch with changes to `demo/**`, `ui/**`, `example-frontend/**`, `syncdb/**`
|
|
195
195
|
2. **Build**: GitHub Actions runs `bun run export` to generate static files
|
|
196
196
|
3. **Ensure negative caching disabled**: Update backend bucket to prevent cached 404s
|
|
197
197
|
4. **Upload**:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Local-first data in Terreno
|
|
2
2
|
|
|
3
|
-
Terreno's default frontend data path is **local-first**: the on-device store is what your UI reads and writes, and the server reconciles changes asynchronously. This is a different mental model from request/response fetching
|
|
3
|
+
Terreno's default frontend data path is **local-first**: the on-device store is what your UI reads and writes, and the server reconciles changes asynchronously. This is a different mental model from request/response fetching.
|
|
4
4
|
|
|
5
5
|
## What local-first means here
|
|
6
6
|
|
|
@@ -10,8 +10,8 @@ There is no server-first mode and no opt-in flag: if you use syncdb, you adopt t
|
|
|
10
10
|
|
|
11
11
|
## Mental model shift
|
|
12
12
|
|
|
13
|
-
| Server-first (
|
|
14
|
-
|
|
13
|
+
| Server-first (request/response) | Local-first (syncdb) |
|
|
14
|
+
|---------------------------------|----------------------|
|
|
15
15
|
| UI reads from cache filled by HTTP | UI reads from the local store |
|
|
16
16
|
| Writes call the API, then invalidate/refetch | Writes apply locally, then reconcile |
|
|
17
17
|
| Loading spinners track request lifecycle | Sync status tracks outbox + connectivity |
|
|
@@ -27,10 +27,12 @@ Think **apply locally, then reconcile** instead of **request, wait, update cache
|
|
|
27
27
|
3. **Refetch orchestration** — `invalidatesTags`, `refetch()`, and list/detail cache coordination after mutations.
|
|
28
28
|
4. **Treating the network as the read path** — list screens blocked on `isLoading` for every navigation.
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
Routes that are not collections — `/auth/me`, modelRouter actions, AI, consents —
|
|
31
|
+
stay request/response. They go through the syncdb request client
|
|
32
|
+
(`@terreno/syncdb/api`) and its generated hooks in `store/apiSdk.ts`, which share the
|
|
33
|
+
sync client's `AuthProvider`. Built-in admin RPC and ObjectId/API-only admin CRUD use
|
|
34
|
+
admin-frontend's host-bound fetch client. Terreno 58 removed `@terreno/rtk` and Redux;
|
|
35
|
+
syncdb is the only frontend data layer.
|
|
34
36
|
|
|
35
37
|
## What you start doing
|
|
36
38
|
|
|
@@ -54,12 +56,12 @@ belong in `queryCollections` instead.
|
|
|
54
56
|
|
|
55
57
|
**Gains:** instant UI, offline writes with a durable outbox, simpler screen code, and one write path on the server (sync and REST share `modelRouter`).
|
|
56
58
|
|
|
57
|
-
**Costs:** larger conceptual surface (conflicts, sync status, local storage), mandatory replica-set MongoDB for realtime, and migration work for
|
|
59
|
+
**Costs:** larger conceptual surface (conflicts, sync status, local storage), mandatory replica-set MongoDB for realtime, and migration work for screens that fetched collections over request/response.
|
|
58
60
|
|
|
59
61
|
Server-first fetching is simpler when you only need online CRUD with no offline requirement — but Terreno's blessed path for collection data is local-first.
|
|
60
62
|
|
|
61
63
|
## Related docs
|
|
62
64
|
|
|
63
65
|
- [syncdb reference](../reference/syncdb.md) — API ground truth
|
|
64
|
-
- [
|
|
66
|
+
- [Upgrade from @terreno/rtk to @terreno/syncdb](../how-to/migrate-rtk-to-syncdb.md) — the Terreno 58 upgrade path
|
|
65
67
|
- [Authentication architecture](authentication.md) — Better Auth + socket sessions
|
|
@@ -128,7 +128,7 @@ Instance actions load the document by `id` before the handler runs. Missing docu
|
|
|
128
128
|
When `openApi` is configured on the router (automatic with `TerrenoApp` and `setupServer`), each action is documented on first route registration. Operations use:
|
|
129
129
|
|
|
130
130
|
- **Tag** — model collection name by default; override with `action.tag`.
|
|
131
|
-
- **operationId** — `{tag}_{actionName}` (e.g. `todos_markComplete`) for stable
|
|
131
|
+
- **operationId** — `{tag}_{actionName}` (e.g. `todos_markComplete`) for stable generated hook names (`store/apiSdk.ts`) after SDK regeneration.
|
|
132
132
|
|
|
133
133
|
Request/response schemas are derived from Zod when `body`, `query`, or `response` are set. Successful responses are documented as `{data: ...}` to match the wire format.
|
|
134
134
|
|
|
@@ -160,9 +160,9 @@ With TerrenoApp, an "app" is just a function that receives the `TerrenoApp` inst
|
|
|
160
160
|
|
|
161
161
|
#### 6. One collection catalog for surfaces
|
|
162
162
|
|
|
163
|
-
`modelRouter` registers each collection once in an in-memory **collection catalog** keyed by route path (for example `/todos`). The record holds the Mongoose model, `ModelRouterOptions`, and which surfaces are active (`mcp`, `
|
|
163
|
+
`modelRouter` registers each collection once in an in-memory **collection catalog** keyed by route path (for example `/todos`). The record holds the Mongoose model, `ModelRouterOptions`, and which surfaces are active (`mcp`, `sync`). TerrenoApp option enrichment calls `replaceCollectionOptions` once per path after RBAC injects `accessControl`.
|
|
164
164
|
|
|
165
|
-
MCP
|
|
165
|
+
MCP and sync read **views** of that catalog — they do not keep separate option arrays. `registerMCPModel`, `registerSync`, and the `update*RegistryOptions` helpers remain as wrappers for tests and legacy callers. `clearMCPRegistry` and `clearSyncRegistry` clear the **whole** catalog so tests cannot leave a stale MCP entry after a sync-only clear. The legacy realtime surface (`modelRouter` `realtime`, `registerRealtime`, `clearRealtimeRegistry`) was removed in Terreno 58.
|
|
166
166
|
|
|
167
167
|
Custom MCP tools stay on a separate list; they are not collections. Sync schema validation and index enqueue still run when a collection first opts into `sync`.
|
|
168
168
|
|
|
@@ -10,7 +10,7 @@ Terreno avoids **barrel imports** — imports that resolve through an `index.ts`
|
|
|
10
10
|
|
|
11
11
|
## Allowed
|
|
12
12
|
|
|
13
|
-
- **Published package APIs** — cross-package imports like `@terreno/api`, `@terreno/ui`, and `@terreno/
|
|
13
|
+
- **Published package APIs** — cross-package imports like `@terreno/api`, `@terreno/ui`, and `@terreno/syncdb/react` use each package's public entry (`src/index` or a declared subpath export). That is intentional.
|
|
14
14
|
- **Package public entry files** — `api/src/index.ts`, `ui/src/index.tsx`, etc. may re-export for npm consumers. Do not import those barrels from *inside* the same package.
|
|
15
15
|
- **Expo Router routes** — `app/**/index.tsx` files are route entries, not barrels.
|
|
16
16
|
|
|
@@ -29,12 +29,12 @@ import {User} from "../models/user";
|
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
```typescript
|
|
32
|
-
// Bad —
|
|
33
|
-
import {
|
|
32
|
+
// Bad — a directory barrel
|
|
33
|
+
import {useGetAuthMeQuery} from "@/store";
|
|
34
34
|
|
|
35
35
|
// Good
|
|
36
|
-
import {
|
|
37
|
-
import
|
|
36
|
+
import {useGetAuthMeQuery} from "@/store/apiSdk";
|
|
37
|
+
import {syncDb} from "@/store/syncdb";
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Enforcement
|
|
@@ -554,7 +554,7 @@ org-admins manage only the current org. Invites and billing are design-only plac
|
|
|
554
554
|
- **Implementation plan:** [org-management-ui.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/implementationPlans/org-management-ui.md)
|
|
555
555
|
- **Tasks:** [org-management-ui.md](https://github.com/TerrenoLabs/terreno/blob/master/docs/tasks/org-management-ui.md)
|
|
556
556
|
- **RTK flag:** Partial — custom non-synced routes use the generated SDK;
|
|
557
|
-
ObjectId admin
|
|
557
|
+
ObjectId admin CRUD uses the admin fetch client (`useAdminModelApi`), while
|
|
558
558
|
eligible String-`_id` CRUD uses windowed syncdb
|
|
559
559
|
- **Depends on:** rbac-permissions
|
|
560
560
|
|
|
@@ -871,9 +871,10 @@ Shipping this work **closes** https://github.com/TerrenoLabs/terreno/issues/1186
|
|
|
871
871
|
**Labels:** `area:api`, `type:feature`
|
|
872
872
|
**Project fields:** Area=`api`, Target=`Released`, Impact=`Feature`, IP=`rate-limiting`, Status=`Shipped`
|
|
873
873
|
|
|
874
|
-
Adds
|
|
874
|
+
Adds HTTP rate limiting to `@terreno/api`: per-user or per-IP keys, stricter
|
|
875
875
|
auth/OTP buckets (including login), memory Redis or Mongo stores, and 429 rate-limit
|
|
876
|
-
headers.
|
|
876
|
+
headers. Opt-in in 57.x; on by default from Terreno 58 (`rateLimit: false` turns it off).
|
|
877
|
+
Sync mutation nacks stay a separate limiter.
|
|
877
878
|
|
|
878
879
|
Shipping this work **closes** https://github.com/TerrenoLabs/terreno/issues/1187.
|
|
879
880
|
|
|
@@ -6,13 +6,13 @@ file (or a new hit in an existing file) that would trip them.
|
|
|
6
6
|
|
|
7
7
|
## Scope
|
|
8
8
|
|
|
9
|
-
`{api,ui,
|
|
9
|
+
`{api,ui,ai,admin-backend,admin-frontend,example-backend}/src/**/*.{ts,tsx}`
|
|
10
10
|
|
|
11
11
|
`example-frontend` and `demo` are listed in the original align glob but have no `src/`
|
|
12
12
|
tree (`app/`, `components/`, `stories/` instead), so this scanner does not cover them.
|
|
13
13
|
|
|
14
14
|
Skipped: tests (`*.test.*`, `*.spec.*`, `*.isolated.*`, `__tests__`), generated SDKs
|
|
15
|
-
(`openApiSdk.ts`, `*OpenApiSdk.ts`), declaration files (`*.d.ts`), `node_modules`, `dist`,
|
|
15
|
+
(`openApiSdk.ts`, `*OpenApiSdk.ts`, `*.gen.ts`), declaration files (`*.d.ts`), `node_modules`, `dist`,
|
|
16
16
|
and test harness files such as `bunSetup.ts`.
|
|
17
17
|
|
|
18
18
|
## Rules
|
|
@@ -10,7 +10,7 @@ TypeScript 7 removes.
|
|
|
10
10
|
| --- | --- | --- | --- | --- |
|
|
11
11
|
| Node library | `api`, `ai`, `admin-backend`, `announcements`, `api-health`, `blocks`, `comms`, `feature-flags`, `jobs`, `test`, `admin-spa` server (`tsconfig.server.json`) | `nodenext` | `es2023` | CommonJS `dist/` |
|
|
12
12
|
| Node ESM tool | `mcp-server`, `create-terreno-app` | `nodenext` | `es2023` | ESM `dist/` (`"type": "module"`) |
|
|
13
|
-
| Frontend library | `ui`, `admin-frontend`, `
|
|
13
|
+
| Frontend library | `ui`, `admin-frontend`, `syncdb` | `esnext` + `moduleResolution: bundler` | `es2022` | ESM `dist/` for Metro |
|
|
14
14
|
| Bun CLI | `cli` | `esnext` + `moduleResolution: bundler` + `customConditions: ["bun"]` | `es2022` | ESM `dist/` for Bun |
|
|
15
15
|
| Expo app | `example-frontend`, `demo`, `admin-spa` | extends `expo/tsconfig.base` | from Expo | nothing (`noEmit`) |
|
|
16
16
|
| Bun app | `example-backend` | `preserve` | `esnext` | nothing (`noEmit`) |
|
|
@@ -41,6 +41,4 @@ their `tsconfig.json`, so an `extends` into a repo-root base would ship a broken
|
|
|
41
41
|
| `target: es5`, `downlevelIteration` | Deprecated. No supported runtime needs ES5 output. |
|
|
42
42
|
| `esModuleInterop`, `allowSyntheticDefaultImports`, `forceConsistentCasingInFileNames` | Already `true` by default in TypeScript 6. Setting them to `false` is deprecated. |
|
|
43
43
|
|
|
44
|
-
`create-terreno-app` scaffolds follow the Expo app and Bun app profiles.
|
|
45
|
-
`tsconfig.codegen.json` pairs `module: commonjs` with `moduleResolution: bundler`, because
|
|
46
|
-
the RTK codegen CLI forces CommonJS when it registers `ts-node`.
|
|
44
|
+
`create-terreno-app` scaffolds follow the Expo app and Bun app profiles.
|
|
@@ -4,9 +4,9 @@ All published `@terreno/*` packages share one version. Upgrade them together. Mi
|
|
|
4
4
|
|
|
5
5
|
## Lockstep
|
|
6
6
|
|
|
7
|
-
A git tag `X.Y.Z` (no `v` prefix) is the version for every package that `.github/workflows/publish-on-tag.yml` publishes: `api`, `test`, `ui`, `
|
|
7
|
+
A git tag `X.Y.Z` (no `v` prefix) is the version for every package that `.github/workflows/publish-on-tag.yml` publishes: `api`, `test`, `ui`, `admin-backend`, `admin-frontend`, `admin-spa`, `blocks`, `ai`, `api-health`, `comms`, `feature-flags`, `mcp`, `syncdb`. Publish jobs read that tag; they do not pick per-package versions.
|
|
8
8
|
|
|
9
|
-
Inside the monorepo, packages depend on each other with `workspace:*` (for example `@terreno/
|
|
9
|
+
Inside the monorepo, packages depend on each other with `workspace:*` (for example `@terreno/admin-frontend` → `@terreno/ui`). On npm they are released at the same number. `@terreno/ui` also pins Expo-line peers (`react-native` `0.88.0` on the 58 line). Installing `@terreno/ui@58` next to Expo 57 is outside support, same as mixing `@terreno/api@58` with `@terreno/ui@57`.
|
|
10
10
|
|
|
11
11
|
## Pre-1.0 (and Expo-aligned majors)
|
|
12
12
|
|
|
@@ -18,7 +18,7 @@ From 56 onward the **major** tracks the Expo SDK major (`56.x`, `57.x`). That is
|
|
|
18
18
|
|
|
19
19
|
A public API marked deprecated stays available for **at least three minor releases** on the current major (example: deprecated in `57.1.0` → still present in `57.2.0` and `57.3.0`). Removal happens in a later minor or the next major, always with an upgrade note.
|
|
20
20
|
|
|
21
|
-
Worked example: `@terreno/rtk` collection CRUD was deprecated in `56.0.0` (restated in `57.1.0`). The package
|
|
21
|
+
Worked example: `@terreno/rtk` collection CRUD was deprecated in `56.0.0` (restated in `57.1.0`). The package published through 57.x and was removed in `58.0.0`, with `@terreno/syncdb` taking over its request client, session, flags, and app utilities. Upgrading across 58 follows [migrate-rtk-to-syncdb.md](../how-to/migrate-rtk-to-syncdb.md).
|
|
22
22
|
|
|
23
23
|
## Where to read changes
|
|
24
24
|
|
|
@@ -22,7 +22,7 @@ Problem-oriented, practical steps. Use these when you know what you want to do.
|
|
|
22
22
|
- [Run tests locally](run-tests-locally.md) — Map every CircleCI test job to a Cursor Cloud / laptop command
|
|
23
23
|
- [GitHub Actions CI](github-actions-ci.md) — Cut PR runtime: docs previews, compile-once e2e, pinned Bun
|
|
24
24
|
- [Add feature flags](add-feature-flags.md) — Add feature flags, A/B testing, and OpenFeature migration
|
|
25
|
-
- [Rate limiting](rate-limiting.md) —
|
|
25
|
+
- [Rate limiting](rate-limiting.md) — Default-on HTTP limiter on `TerrenoApp` (memory, Redis, or Mongo; `rateLimit: false` turns it off)
|
|
26
26
|
- [Receive inbound webhooks](inbound-webhooks.md) — HMAC, Stripe, Twilio, and SendGrid signatures on `WebhooksApp`
|
|
27
27
|
- [Durable background jobs](background-jobs.md) — `@terreno/jobs`: enqueue, workers, schedules, Cloud Tasks / Vercel Queues
|
|
28
28
|
- [Create a Mongoose model](create-a-model.md) — Define models with proper conventions
|
|
@@ -59,9 +59,10 @@ Problem-oriented, practical steps. Use these when you know what you want to do.
|
|
|
59
59
|
- [Show loading skeletons](show-loading-skeletons.md) — `loading`, `loadingComponent`, and `loadingAnimation` on Box, Card, charts, and Page; `withLoading` for custom components
|
|
60
60
|
- [Compose charts in a dashboard grid](charts-and-dashboards.md) — `LineChart` / `BarChart` / `AreaChart` / `DonutChart` inside `Card`s in `DashboardGrid` (ops-dashboard scorecards and spans: [dashboard-chart-parity](../implementationPlans/dashboard-chart-parity.md))
|
|
61
61
|
- [Compare rendered chart snapshots](compare-chart-rendered-snapshots.md) — PNG goldens for the chart visual gallery, not JSON snapshots
|
|
62
|
-
- [
|
|
62
|
+
- [Upgrade from @terreno/rtk to @terreno/syncdb (Terreno 58)](migrate-rtk-to-syncdb.md) — Remove `@terreno/rtk` and Redux; every removed export mapped to its replacement
|
|
63
63
|
- [Use syncdb correctly](use-syncdb-correctly.md) — Serial snapshot startup, outbox-halting 403s, phantom creates, conflicts, and other library behavior apps must handle
|
|
64
64
|
- [Generate a REST CLI from OpenAPI](generate-a-rest-cli.md) — Scaffold `terreno generate rest-cli` for an app API
|
|
65
|
+
- [Generate the client SDK](generate-the-client-sdk.md) — `terreno-syncdb-codegen` writes collection hooks and typed request hooks from OpenAPI
|
|
65
66
|
- [In-app notifications](in-app-notifications.md) — Register `NotificationsApp`, syncdb inbox, and UI bell
|
|
66
67
|
- [Add organizations](add-organizations.md) — Opt into Membership-backed tenancy, RBAC, admin, and seeded examples
|
|
67
68
|
- [Add a GPT chat mascot](add-gpt-chat-mascot.md) — Optional consumer-owned character on empty `GPTChat`
|