@terreno/mcp 57.0.0 → 57.1.0
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/dist/bootstrap.d.ts +1 -1
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +381 -191
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +8 -0
- package/dist/docs/resources/overview.md +6 -1
- package/dist/docs/resources/rtk.md +3 -1
- package/dist/docs/resources/syncdb.md +43 -0
- package/dist/docs/upgrades/56.0.0-beta.2.md +34 -0
- package/dist/docs/upgrades/57.1.0.md +126 -0
- package/dist/docs/versioned/next/README.md +15 -3
- package/dist/docs/versioned/next/explanation/README.md +3 -0
- package/dist/docs/versioned/next/explanation/admin-consent-migration.md +15 -0
- package/dist/docs/versioned/next/explanation/admin-plugin-frontend.md +12 -0
- package/dist/docs/versioned/next/explanation/announcements/rtk-deprecation.md +29 -0
- package/dist/docs/versioned/next/explanation/authentication.md +51 -58
- package/dist/docs/versioned/next/explanation/local-first-data.md +52 -0
- package/dist/docs/versioned/next/explanation/loop-engineering.md +117 -0
- package/dist/docs/versioned/next/explanation/repository-settings.md +8 -0
- package/dist/docs/versioned/next/explanation/roadmap-process.md +22 -9
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +39 -0
- package/dist/docs/versioned/next/how-to/README.md +3 -0
- package/dist/docs/versioned/next/how-to/admin-add-model.md +41 -0
- package/dist/docs/versioned/next/how-to/admin-custom-home.md +31 -0
- package/dist/docs/versioned/next/how-to/admin-custom-widget.md +42 -0
- package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +25 -0
- package/dist/docs/versioned/next/how-to/circleci.md +173 -0
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +13 -10
- package/dist/docs/versioned/next/how-to/expose-mcp-tools.md +194 -0
- package/dist/docs/versioned/next/how-to/install-agent-skills.md +46 -0
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +26 -1
- package/dist/docs/versioned/next/how-to/websocket-integration.md +2 -1
- package/dist/docs/versioned/next/implementationPlans/ModularAPI.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/README.md +31 -17
- package/dist/docs/versioned/next/implementationPlans/admin-improvements-research.md +136 -0
- package/dist/docs/versioned/next/implementationPlans/admin-improvements.md +157 -527
- package/dist/docs/versioned/next/implementationPlans/admin-only-research.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/admin-only.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/admin-script-runner.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/admin-ui-v2-django-parity.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +160 -130
- package/dist/docs/versioned/next/implementationPlans/apierror-standard-error-redesign.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +5 -4
- package/dist/docs/versioned/next/implementationPlans/comms-abstraction-research.md +48 -0
- package/dist/docs/versioned/next/implementationPlans/comms-abstraction.md +152 -62
- package/dist/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +9 -9
- package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/consent-forms.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/design-blend-skill.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/feature-flags-openfeature.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/feature-flags.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/infra-mcp-research.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/infra-mcp.md +3 -3
- package/dist/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +2 -2
- package/dist/docs/versioned/next/implementationPlans/model-router-actions.md +4 -0
- package/dist/docs/versioned/next/implementationPlans/model-router-mcp.md +5 -4
- package/dist/docs/versioned/next/implementationPlans/offline-mode.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/oss-launch-program.md +11 -2
- package/dist/docs/versioned/next/implementationPlans/pluggable-database-sqlite-research.md +150 -0
- package/dist/docs/versioned/next/implementationPlans/pluggable-database-sqlite.md +247 -0
- package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/public-roadmap-github.md +2 -2
- package/dist/docs/versioned/next/implementationPlans/rbac-permissions.md +52 -15
- package/dist/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +13 -12
- package/dist/docs/versioned/next/implementationPlans/syncdb-codegen-research.md +137 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-codegen.md +63 -59
- package/dist/docs/versioned/next/implementationPlans/syncdb-local-first.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +1 -0
- package/dist/docs/versioned/next/implementationPlans/upgrade-banner.md +4 -0
- package/dist/docs/versioned/next/reference/README.md +6 -1
- package/dist/docs/versioned/next/reference/admin-config.md +51 -0
- package/dist/docs/versioned/next/reference/ai.md +15 -1
- package/dist/docs/versioned/next/reference/api.md +18 -0
- package/dist/docs/versioned/next/reference/comms.md +88 -7
- package/dist/docs/versioned/next/reference/environment-variables.md +9 -1
- package/dist/docs/versioned/next/reference/{rtk.md → legacy/rtk.md} +11 -7
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +54 -0
- package/dist/docs/versioned/next/reference/mcp-server.md +7 -0
- package/dist/docs/versioned/next/reference/syncdb.md +30 -3
- package/dist/docs/versioned/next/tasks/README.md +21 -10
- package/dist/docs/versioned/next/tasks/admin-improvements.md +189 -353
- package/dist/docs/versioned/next/tasks/admin-only.md +2 -0
- package/dist/docs/versioned/next/tasks/agentic-sdlc-plugin.md +54 -160
- package/dist/docs/versioned/next/tasks/comms-abstraction.md +30 -24
- package/dist/docs/versioned/next/tasks/comms-adapter-sendgrid.md +4 -4
- package/dist/docs/versioned/next/tasks/consent-forms.md +2 -0
- package/dist/docs/versioned/next/tasks/feature-flags.md +2 -0
- package/dist/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +14 -10
- package/dist/docs/versioned/next/tasks/model-router-actions.md +2 -0
- package/dist/docs/versioned/next/tasks/model-router-mcp.md +27 -27
- package/dist/docs/versioned/next/tasks/offline-mode.md +2 -0
- package/dist/docs/versioned/next/tasks/pluggable-database-sqlite.md +44 -0
- package/dist/docs/versioned/next/tasks/public-roadmap-github.md +1 -1
- package/dist/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +15 -15
- package/dist/docs/versioned/next/tasks/syncdb-codegen.md +37 -31
- package/dist/docs/versioned/next/tasks/upgrade-banner.md +2 -0
- package/dist/index.js +24 -55
- package/dist/index.js.map +1 -1
- package/dist/local/localServer.d.ts.map +1 -1
- package/dist/local/localServer.js +9 -8
- package/dist/local/localServer.js.map +1 -1
- package/dist/local/localTools.d.ts +1 -1
- package/dist/local/localTools.d.ts.map +1 -1
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +44 -45
- package/dist/prompts.js.map +1 -1
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +9 -2
- package/dist/resources.js.map +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +4 -2
- package/dist/tools.js.map +1 -1
- package/package.json +7 -3
- package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +8 -0
- package/src/docs/resources/overview.md +6 -1
- package/src/docs/resources/rtk.md +3 -1
- package/src/docs/resources/syncdb.md +43 -0
- package/src/docs/upgrades/56.0.0-beta.2.md +34 -0
- package/src/docs/upgrades/57.1.0.md +126 -0
- package/src/docs/versioned/next/README.md +15 -3
- package/src/docs/versioned/next/explanation/README.md +3 -0
- package/src/docs/versioned/next/explanation/admin-consent-migration.md +15 -0
- package/src/docs/versioned/next/explanation/admin-plugin-frontend.md +12 -0
- package/src/docs/versioned/next/explanation/announcements/rtk-deprecation.md +29 -0
- package/src/docs/versioned/next/explanation/authentication.md +51 -58
- package/src/docs/versioned/next/explanation/local-first-data.md +52 -0
- package/src/docs/versioned/next/explanation/loop-engineering.md +117 -0
- package/src/docs/versioned/next/explanation/repository-settings.md +8 -0
- package/src/docs/versioned/next/explanation/roadmap-process.md +22 -9
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +39 -0
- package/src/docs/versioned/next/how-to/README.md +3 -0
- package/src/docs/versioned/next/how-to/admin-add-model.md +41 -0
- package/src/docs/versioned/next/how-to/admin-custom-home.md +31 -0
- package/src/docs/versioned/next/how-to/admin-custom-widget.md +42 -0
- package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +25 -0
- package/src/docs/versioned/next/how-to/circleci.md +173 -0
- package/src/docs/versioned/next/how-to/configure-better-auth.md +13 -10
- package/src/docs/versioned/next/how-to/expose-mcp-tools.md +194 -0
- package/src/docs/versioned/next/how-to/install-agent-skills.md +46 -0
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +26 -1
- package/src/docs/versioned/next/how-to/websocket-integration.md +2 -1
- package/src/docs/versioned/next/implementationPlans/ModularAPI.md +1 -0
- package/src/docs/versioned/next/implementationPlans/README.md +31 -17
- package/src/docs/versioned/next/implementationPlans/admin-improvements-research.md +136 -0
- package/src/docs/versioned/next/implementationPlans/admin-improvements.md +157 -527
- package/src/docs/versioned/next/implementationPlans/admin-only-research.md +4 -0
- package/src/docs/versioned/next/implementationPlans/admin-only.md +3 -1
- package/src/docs/versioned/next/implementationPlans/admin-script-runner.md +1 -0
- package/src/docs/versioned/next/implementationPlans/admin-ui-v2-django-parity.md +1 -0
- package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +160 -130
- package/src/docs/versioned/next/implementationPlans/apierror-standard-error-redesign.md +1 -0
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +5 -4
- package/src/docs/versioned/next/implementationPlans/comms-abstraction-research.md +48 -0
- package/src/docs/versioned/next/implementationPlans/comms-abstraction.md +152 -62
- package/src/docs/versioned/next/implementationPlans/comms-adapter-sendgrid.md +9 -9
- package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +1 -1
- package/src/docs/versioned/next/implementationPlans/consent-forms.md +4 -0
- package/src/docs/versioned/next/implementationPlans/design-blend-skill.md +1 -0
- package/src/docs/versioned/next/implementationPlans/docs-site-and-versioning.md +1 -0
- package/src/docs/versioned/next/implementationPlans/feature-flags-openfeature.md +1 -0
- package/src/docs/versioned/next/implementationPlans/feature-flags.md +4 -0
- package/src/docs/versioned/next/implementationPlans/infra-mcp-research.md +1 -0
- package/src/docs/versioned/next/implementationPlans/infra-mcp.md +3 -3
- package/src/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -0
- package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci-research.md +1 -0
- package/src/docs/versioned/next/implementationPlans/migrate-cicd-to-circleci.md +2 -2
- package/src/docs/versioned/next/implementationPlans/model-router-actions.md +4 -0
- package/src/docs/versioned/next/implementationPlans/model-router-mcp.md +5 -4
- package/src/docs/versioned/next/implementationPlans/offline-mode.md +3 -1
- package/src/docs/versioned/next/implementationPlans/oss-launch-program.md +11 -2
- package/src/docs/versioned/next/implementationPlans/pluggable-database-sqlite-research.md +150 -0
- package/src/docs/versioned/next/implementationPlans/pluggable-database-sqlite.md +247 -0
- package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/src/docs/versioned/next/implementationPlans/public-roadmap-github.md +2 -2
- package/src/docs/versioned/next/implementationPlans/rbac-permissions.md +52 -15
- package/src/docs/versioned/next/implementationPlans/rtk-to-syncdb-migration-docs.md +13 -12
- package/src/docs/versioned/next/implementationPlans/syncdb-codegen-research.md +137 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-codegen.md +63 -59
- package/src/docs/versioned/next/implementationPlans/syncdb-local-first.md +1 -0
- package/src/docs/versioned/next/implementationPlans/syncdb-phase-c-design.md +1 -0
- package/src/docs/versioned/next/implementationPlans/terreno-langfuse-integration.md +1 -0
- package/src/docs/versioned/next/implementationPlans/terreno-syncdb-2.md +1 -0
- package/src/docs/versioned/next/implementationPlans/upgrade-banner.md +4 -0
- package/src/docs/versioned/next/reference/README.md +6 -1
- package/src/docs/versioned/next/reference/admin-config.md +51 -0
- package/src/docs/versioned/next/reference/ai.md +15 -1
- package/src/docs/versioned/next/reference/api.md +18 -0
- package/src/docs/versioned/next/reference/comms.md +88 -7
- package/src/docs/versioned/next/reference/environment-variables.md +9 -1
- package/src/docs/versioned/next/reference/{rtk.md → legacy/rtk.md} +11 -7
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +54 -0
- package/src/docs/versioned/next/reference/mcp-server.md +7 -0
- package/src/docs/versioned/next/reference/syncdb.md +30 -3
- package/src/docs/versioned/next/tasks/README.md +21 -10
- package/src/docs/versioned/next/tasks/admin-improvements.md +189 -353
- package/src/docs/versioned/next/tasks/admin-only.md +2 -0
- package/src/docs/versioned/next/tasks/agentic-sdlc-plugin.md +54 -160
- package/src/docs/versioned/next/tasks/comms-abstraction.md +30 -24
- package/src/docs/versioned/next/tasks/comms-adapter-sendgrid.md +4 -4
- package/src/docs/versioned/next/tasks/consent-forms.md +2 -0
- package/src/docs/versioned/next/tasks/feature-flags.md +2 -0
- package/src/docs/versioned/next/tasks/migrate-cicd-to-circleci.md +14 -10
- package/src/docs/versioned/next/tasks/model-router-actions.md +2 -0
- package/src/docs/versioned/next/tasks/model-router-mcp.md +27 -27
- package/src/docs/versioned/next/tasks/offline-mode.md +2 -0
- package/src/docs/versioned/next/tasks/pluggable-database-sqlite.md +44 -0
- package/src/docs/versioned/next/tasks/public-roadmap-github.md +1 -1
- package/src/docs/versioned/next/tasks/rtk-to-syncdb-migration-docs.md +15 -15
- package/src/docs/versioned/next/tasks/syncdb-codegen.md +37 -31
- package/src/docs/versioned/next/tasks/upgrade-banner.md +2 -0
- package/dist/docs/versioned/next/implementationPlans/PLAN_INDEX.md +0 -73
- package/dist/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +0 -303
- package/src/docs/versioned/next/implementationPlans/PLAN_INDEX.md +0 -73
- package/src/docs/versioned/next/implementationPlans/syncdb-api-inventory.md +0 -303
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# Research: @terreno/syncdb-codegen
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-08-20
|
|
4
|
+
**Scope:** Validate the draft IP (`docs/implementationPlans/syncdb-codegen.md`) against the current `master` codebase and parent syncdb work.
|
|
5
|
+
**Status:** Complete — clarification pass answered 2026-08-20. IP **Approved**.
|
|
6
|
+
|
|
7
|
+
## Clarification outcomes (2026-08-20)
|
|
8
|
+
|
|
9
|
+
| # | Topic | Choice |
|
|
10
|
+
|---|-------|--------|
|
|
11
|
+
| 1 | Collection discovery | A — `x-terreno-sync` + `--collections` fallback |
|
|
12
|
+
| 2 | Per-mutation retries | A — `maxAttempts` plumbing |
|
|
13
|
+
| 3 | Type emission | A — hand-rolled emitter |
|
|
14
|
+
| 4 | Hook factory | A — `createCollectionHooks` in `@terreno/syncdb/react` |
|
|
15
|
+
| 5 | Hook naming | **C — friendly names** (`useTodos`, `useTodo`, `useCreateTodo`, …) |
|
|
16
|
+
| 6 | Mutation return | A — `[trigger]` one-tuple |
|
|
17
|
+
| 7 | example-frontend scope | A — todos only |
|
|
18
|
+
| 8 | Delivery | **B — single PR** |
|
|
19
|
+
| 9 | npm publish | **Include `@terreno/syncdb-codegen` in `publish-on-tag.yml`** |
|
|
20
|
+
|
|
21
|
+
## Open questions (blocking — see blend Step 3)
|
|
22
|
+
|
|
23
|
+
Resolved — see table above.
|
|
24
|
+
|
|
25
|
+
## Problem summary
|
|
26
|
+
|
|
27
|
+
`@terreno/syncdb` is implemented (Phases 1–8 of `syncdb-local-first.md` landed). Consumers still hand-write collection strings, entity interfaces, and hook wiring — see `example-frontend/components/SyncTodosScreen.tsx`. RTK consumers get typed hooks from `bun run sdk` → `openApiSdk.ts`. The gap is a **codegen CLI** that reads the same OpenAPI spec and emits typed syncdb hooks (friendly names per approved IP).
|
|
28
|
+
|
|
29
|
+
## What was investigated
|
|
30
|
+
|
|
31
|
+
| Area | Locations | Finding |
|
|
32
|
+
|------|-----------|---------|
|
|
33
|
+
| React hooks | `syncdb/src/react/hooks.ts`, `index.ts` | `useQuery`, `useEntity`, `useMutate` exist; no per-operation hooks, no `createCollectionHooks` |
|
|
34
|
+
| Mutate / outbox | `syncdb/src/client.ts`, `mutations/outbox.ts`, `storage/schema.ts` | `MutateArgs` has no `maxAttempts`; outbox rows have `errorNackCount` only; replay uses global `MAX_ERROR_NACK_ATTEMPTS = 5` |
|
|
35
|
+
| OpenAPI list | `api/src/openApi.ts` `listOpenApiMiddleware` | No vendor extensions; no `operationId`; tags = model collection name |
|
|
36
|
+
| Sync registry | `api/src/sync/registry.ts`, `api/src/api.ts` | In-memory only; `collectionTag = routePath.replace(/^\//, "")`; not reflected in OpenAPI |
|
|
37
|
+
| Example backend | `example-backend/src/api/todos.ts`, `projects.ts` | `sync: {scope: {type: "owner"}}` on todos; tenant scope on projects |
|
|
38
|
+
| RTK hook names | `example-frontend/store/openApiSdk.ts` | `useGetTodosQuery`, `useGetTodosByIdQuery`, `usePostTodosMutation`, `usePatchTodosByIdMutation`, `useDeleteTodosByIdMutation` |
|
|
39
|
+
| Codegen package | `syncdb-codegen/` | **Does not exist** — tasks/IP only |
|
|
40
|
+
| Parent IP | `docs/implementationPlans/syncdb-local-first.md` | Shaped; codegen listed as Phase 6 / future work |
|
|
41
|
+
| Roadmap | `.github/roadmap-fields.yml` present | Roadmap-enabled repo; no GitHub issue found for syncdb-codegen |
|
|
42
|
+
|
|
43
|
+
## Current consumer pattern (manual)
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
// example-frontend/components/SyncTodosScreen.tsx
|
|
47
|
+
interface SyncTodo { _id: string; title?: string; completed?: boolean; created?: string; }
|
|
48
|
+
const todos = useQuery<SyncTodo>("todos", {sort: sortByCreatedDesc});
|
|
49
|
+
const {update, remove} = useMutate("todos");
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`example-frontend/store/syncdb.ts` maintains `SYNC_COLLECTIONS` by hand — can drift from backend `sync` registration.
|
|
53
|
+
|
|
54
|
+
## Candidate options (not chosen here — see blocking questions)
|
|
55
|
+
|
|
56
|
+
### How codegen discovers synced collections
|
|
57
|
+
|
|
58
|
+
| Option | Pros | Cons |
|
|
59
|
+
|--------|------|------|
|
|
60
|
+
| **A. `x-terreno-sync` on list OpenAPI ops** | Single input (same as RTK); spec is versioned with backend; ~10-line api change | Must thread `routePath` into `listOpenApiMiddleware` (not available today) |
|
|
61
|
+
| **B. `--collections` CLI flag only** | No api change | Drifts from backend; duplicates config |
|
|
62
|
+
| **C. `GET /sync/config` manifest** | Runtime truth | Extra endpoint; second source of truth vs OpenAPI |
|
|
63
|
+
|
|
64
|
+
Draft IP recommends **A + B fallback**.
|
|
65
|
+
|
|
66
|
+
### Per-mutation retry budget (`retries` / `maxAttempts`)
|
|
67
|
+
|
|
68
|
+
| Option | Pros | Cons |
|
|
69
|
+
|--------|------|------|
|
|
70
|
+
| **A. Plumb `maxAttempts` through mutate → outbox → replay** | Config is honest; matches RTK `maxRetries: 0` intent | Touches syncdb storage + replay coordinator |
|
|
71
|
+
| **B. Codegen emits `retries` but engine ignores** | Smaller diff | Lying generated config |
|
|
72
|
+
|
|
73
|
+
Draft IP recommends **A**.
|
|
74
|
+
|
|
75
|
+
### Type emission from OpenAPI schemas
|
|
76
|
+
|
|
77
|
+
| Option | Pros | Cons |
|
|
78
|
+
|--------|------|------|
|
|
79
|
+
| **A. Hand-rolled emitter for mongoose-to-swagger dialect** | Zero runtime deps; small compiled binary | Must grow if spec dialect widens |
|
|
80
|
+
| **B. `openapi-typescript` or oazapfts** | Battle-tested | Heavier dep; may emit more than needed |
|
|
81
|
+
|
|
82
|
+
Draft IP recommends **A** (~150 lines + snapshot tests).
|
|
83
|
+
|
|
84
|
+
### Hook factory location
|
|
85
|
+
|
|
86
|
+
| Option | Pros | Cons |
|
|
87
|
+
|--------|------|------|
|
|
88
|
+
| **A. `createCollectionHooks` in `@terreno/syncdb/react`** | Versioned with hooks; custom hooks use same factory | Expands syncdb public API |
|
|
89
|
+
| **B. Inline factory in every generated file** | No syncdb API change | Duplicated generated code |
|
|
90
|
+
| **C. Runtime export from `@terreno/syncdb-codegen`** | — | Codegen becomes app dependency (rejected) |
|
|
91
|
+
|
|
92
|
+
Draft IP recommends **A**.
|
|
93
|
+
|
|
94
|
+
### Hook naming and RTK collision
|
|
95
|
+
|
|
96
|
+
| Option | Pros | Cons |
|
|
97
|
+
|--------|------|------|
|
|
98
|
+
| **A. Grouped `useTodosMutate()`** | No name collision with RTK | Different call sites; harder migration |
|
|
99
|
+
| **B. RTK-exact per-op names (`useGetTodosQuery`, …)** | Import-path migration | Intentional name collision — must not barrel-export both SDKs |
|
|
100
|
+
| **C. Friendly names (`useCreateTodo`)** | Readable | Diverges from RTK; migration friction |
|
|
101
|
+
|
|
102
|
+
Draft IP recommends **B**.
|
|
103
|
+
|
|
104
|
+
### Mutation hook return shape
|
|
105
|
+
|
|
106
|
+
| Option | Pros | Cons |
|
|
107
|
+
|--------|------|------|
|
|
108
|
+
| **A. `[trigger]` one-tuple** | RTK destructuring compiles; syncdb writes are sync | `.unwrap()` / `isLoading` must be deleted at migration |
|
|
109
|
+
| **B. Full RTK async tuple** | Drop-in compat | Fakes async state syncdb does not have |
|
|
110
|
+
|
|
111
|
+
Draft IP recommends **A**.
|
|
112
|
+
|
|
113
|
+
## Technical notes for implementation
|
|
114
|
+
|
|
115
|
+
1. **`listOpenApiMiddleware` needs `routePath`**: Today signature is `(model, options)` only. `collection` for `x-terreno-sync` must match `sync/registry.ts` `collectionTag` (path without leading `/`). Pass path from `_buildModelRouter` or read from options if added there.
|
|
116
|
+
|
|
117
|
+
2. **`scope` in extension**: Emit scope **type** string (`owner` | `tenant` | `broadcast` | `custom`). Function resolvers → `"custom"`.
|
|
118
|
+
|
|
119
|
+
3. **Name derivation**: Match `@rtk-query/codegen-openapi` — pascal-case from HTTP method + path segments (verified against `openApiSdk.ts` for `/todos`).
|
|
120
|
+
|
|
121
|
+
4. **example-backend has two synced models** (todos owner, projects tenant). Draft IP example-frontend integration only wires todos — confirm whether projects should appear in generated SDK for the example app.
|
|
122
|
+
|
|
123
|
+
5. **No data migration** for optional `_outbox.maxAttempts` cell — missing cell keeps engine default (5).
|
|
124
|
+
|
|
125
|
+
6. **Roadmap handoff**: When IP status → Approved, run `roadmap-item` to create/update tracking issue (no existing issue found).
|
|
126
|
+
|
|
127
|
+
## Open questions (blocking — see blend Step 3)
|
|
128
|
+
|
|
129
|
+
Transferred to the clarification pass in the blend thread; do not treat draft IP decisions as approved until answered.
|
|
130
|
+
|
|
131
|
+
## References
|
|
132
|
+
|
|
133
|
+
- `docs/implementationPlans/syncdb-codegen.md` (draft IP)
|
|
134
|
+
- `docs/tasks/syncdb-codegen.md` (task breakdown)
|
|
135
|
+
- `docs/implementationPlans/syncdb-local-first.md` (parent — Phase 6 codegen)
|
|
136
|
+
- `docs/how-to/migrate-rtk-to-syncdb.md` (migration guide — codegen section TBD)
|
|
137
|
+
- `.rulesync/skills/generate-sdk/SKILL.md` (RTK codegen parallel)
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# Implementation Plan: @terreno/syncdb-codegen
|
|
2
2
|
|
|
3
|
-
**Status:**
|
|
4
|
-
**
|
|
3
|
+
**Status:** In progress
|
|
4
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1087
|
|
5
|
+
**Branch:** cursor/syncdb-codegen-in-package-746d
|
|
5
6
|
**Owner:**
|
|
6
7
|
**Created:** 2026-07-08
|
|
8
|
+
**Approved:** 2026-08-20
|
|
9
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1110
|
|
10
|
+
**Research:** [syncdb-codegen-research.md](./syncdb-codegen-research.md)
|
|
7
11
|
|
|
8
12
|
## Goal
|
|
9
13
|
|
|
10
|
-
Ship
|
|
14
|
+
Ship **`terreno-syncdb-codegen` as a bin of `@terreno/syncdb`** (not a separate workspace package): a CLI that reads a backend OpenAPI spec and generates a static, typed set of syncdb hooks — the local-first analogue of `@rtk-query/codegen-openapi` (`bun run sync-sdk` → `store/syncDbSdk.ts`).
|
|
11
15
|
|
|
12
16
|
Today syncdb consumers hand-write both the entity types and the collection strings:
|
|
13
17
|
|
|
@@ -18,14 +22,14 @@ const todos = useQuery<SyncTodo>("todos", {sort: sortByCreatedDesc});
|
|
|
18
22
|
const {update, remove} = useMutate("todos"); // update/remove payloads are Record<string, unknown>
|
|
19
23
|
```
|
|
20
24
|
|
|
21
|
-
After this IP, a generated `store/syncDbSdk.ts` provides one hook per operation with
|
|
25
|
+
After this IP, a generated `store/syncDbSdk.ts` provides one hook per operation with **friendly, entity-centric names** — `useTodos` (list), `useTodo` (read by id), `useCreateTodo`, `useUpdateTodo`, `useDeleteTodo` — with payload types derived from the same OpenAPI schemas RTK codegen uses (`Todo`, `CreateTodoBody`, `UpdateTodoBody`), plus a `SYNC_COLLECTIONS` constant that feeds `createSyncDb`. Names deliberately differ from RTK codegen so `openApiSdk.ts` and `syncDbSdk.ts` can coexist without collision during migration. Mutation hooks still use one-element `[trigger]` tuples for familiar destructuring; triggers are syncdb-native (no `.unwrap()`). Custom hooks are supported the same way RTK supports custom endpoints: the generated file is never edited, and consumers extend it in a sibling file using `createCollectionHooks`.
|
|
22
26
|
|
|
23
27
|
## Non-Goals
|
|
24
28
|
|
|
25
29
|
- Covering the whole syncdb surface. Only the five CRUD operations (list query, read, create, update/patch, delete) get generated per-collection hooks. `useSyncStatus`, `useConflicts`, `useSyncDbClient`, and `SyncDbProvider` are global (not per-collection) and stay as direct imports from `@terreno/syncdb/react`.
|
|
26
|
-
- Full RTK return-shape emulation. Hook **names
|
|
30
|
+
- Full RTK return-shape emulation. Hook **names** are syncdb-friendly (not RTK-derived); mutation hooks use `[trigger]` tuples only. Return shapes are syncdb-native (no `isLoading`/`error` async state on mutations — writes are synchronous local applies plus a durable outbox; sync state lives in `useSyncStatus`). The RTK ↔ syncdb mapping is documented in the generated file header and the migration guide.
|
|
27
31
|
- Runtime schema validation of mutation payloads (types are compile-time only, matching RTK codegen).
|
|
28
|
-
- Publishing per-platform compiled binaries
|
|
32
|
+
- Publishing per-platform compiled binaries as GitHub release artifacts (the compiled binary remains a local/CI `build:binary` target).
|
|
29
33
|
- Watch mode, incremental generation, or multi-output-file support.
|
|
30
34
|
- Generating Redux integration or migrating non-synced endpoints — per `docs/how-to/migrate-rtk-to-syncdb.md`, custom routes stay on RTK.
|
|
31
35
|
|
|
@@ -40,9 +44,11 @@ Options were considered where the design was genuinely open; each row records th
|
|
|
40
44
|
| **Config surface: flags only, or a config file?** The binary should "take a few args", but per-collection overrides (retries, exclusions) don't map well to flags. | **Flags for the common path, optional JSON config for overrides.** `--schema`, `--out`, `--collections` cover the RTK-equivalent happy path. `--config ./syncdb-codegen.json` adds `{overrides: {<collection>: {retries?: boolean \| number}}, exportName?, sdkImportPath?}`. JSON (not TS) because a compiled bun binary cannot reliably import arbitrary consumer TypeScript at runtime; RTK's TS `ConfigFile` needs the `tsx`/`TS_NODE_PROJECT` dance we currently work around in `example-frontend/scripts/generate-sdk.ts`. |
|
|
41
45
|
| **How are TS types emitted from OpenAPI schemas?** Options: (a) a small hand-rolled emitter for the subset `mongoose-to-swagger` actually produces (objects, primitives, enums as string unions, arrays, `$ref`); (b) depend on `openapi-typescript` or oazapfts. | **(a).** The @terreno/api spec is a narrow, known dialect; a ~150-line emitter with snapshot tests keeps the binary dependency-free and small. If the emitter grows past that dialect, revisit (b). |
|
|
42
46
|
| **Where does the shared hook factory live?** Generated code and custom hooks must be built the same way (the RTK parallel: generated `injectEndpoints` and app `sdk.ts` both build on `emptySplitApi`). Options: (a) new `createCollectionHooks` export in `@terreno/syncdb/react`; (b) inline the factory into every generated file; (c) a runtime export of `@terreno/syncdb-codegen`. | **(a).** One implementation, versioned with the hooks it wraps, and consumers already depend on `@terreno/syncdb`. (b) duplicates logic into generated output; (c) would make the codegen package a runtime dependency of apps, which it should never be. |
|
|
43
|
-
| **Hook granularity and naming.** Options: (a) one grouped `useTodosMutate()` returning `{create, update, remove}`; (b) one hook per operation with RTK codegen's exact naming scheme (`use{Method}{Path}Query/Mutation`); (c) per-operation hooks with friendlier names (`useCreateTodo`). | **(
|
|
47
|
+
| **Hook granularity and naming.** Options: (a) one grouped `useTodosMutate()` returning `{create, update, remove}`; (b) one hook per operation with RTK codegen's exact naming scheme (`use{Method}{Path}Query/Mutation`); (c) per-operation hooks with friendlier names (`useCreateTodo`). | **(c) — per-operation, friendly names.** Generated per collection (entity `Todo`, collection `todos`): `useTodos`, `useTodo`, `useCreateTodo`, `useUpdateTodo`, `useDeleteTodo`. Derivation: `entityName` from the list-item schema `$ref` basename (`Todo`); `pluralName` = PascalCase of the collection path segment (`todos` → `Todos`). List hook `use{Plural}`; read hook `use{Entity}`; mutations `useCreate{Entity}`, `useUpdate{Entity}`, `useDelete{Entity}`. No collision with RTK hooks during dual-stack migration. "Update" and "patch" are the same hook — modelRouter only exposes PATCH (merge semantics). |
|
|
44
48
|
| **Mutation hook return shape.** RTK mutation hooks return `[trigger, {isLoading, error, ...}]` and triggers return promises with `.unwrap()`. syncdb writes are synchronous local applies; there is no per-call async state. | **One-element tuple `[trigger]`.** Keeps the RTK call site (`const [createTodo] = usePostTodosMutation()`) working unchanged; the trigger returns `{mutationId, id}` synchronously instead of a promise, so `.unwrap()`/`isLoading` usages are deleted during migration (the compiler flags them). Pending/conflict state is per-entity (`isPending` on reads) and global (`useSyncStatus`), not per-call. |
|
|
45
|
-
| **Distribution.** "Compiled bun binary" vs npm reality. | **Both, npm-bin first.** The package ships `"bin": {"terreno-syncdb-codegen": "./dist/cli.js"}` with a `#!/usr/bin/env bun` shebang (the `@terreno/mcp` pattern), plus a `build:binary` script running `bun build --compile src/cli.ts --outfile dist/terreno-syncdb-codegen`
|
|
49
|
+
| **Distribution.** "Compiled bun binary" vs npm reality. | **Both, npm-bin first, published on tag.** The package ships `"bin": {"terreno-syncdb-codegen": "./dist/cli.js"}` with a `#!/usr/bin/env bun` shebang (the `@terreno/mcp` pattern), plus a `build:binary` script running `bun build --compile src/cli.ts --outfile dist/terreno-syncdb-codegen` for standalone use. Add `publish-syncdb-codegen` to `publish-on-tag.yml` (modeled on `publish-syncdb` / `publish-mcp`). |
|
|
50
|
+
| **Delivery shape.** Four phased PRs vs one. | **Single PR** — all runtime, api, codegen, integration, docs, and publish wiring land together. Task phases remain for implementation order inside the PR. |
|
|
51
|
+
| **example-frontend generated collections.** Backend syncs todos and projects; example app scope. | **todos only** in the checked-in `syncDbSdk.ts`; projects stay manual or a follow-up when a syncdb screen ships for them. |
|
|
46
52
|
|
|
47
53
|
## Architecture
|
|
48
54
|
|
|
@@ -59,9 +65,10 @@ example-backend example-frontend
|
|
|
59
65
|
store/syncDbSdk.ts (generated, never edited)
|
|
60
66
|
├─ interface Todo / CreateTodoBody / UpdateTodoBody
|
|
61
67
|
├─ export const SYNC_COLLECTIONS = ["todos"] as const
|
|
62
|
-
└─ export const {
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
└─ export const {useListQuery: useTodos, useReadQuery: useTodo,
|
|
69
|
+
useCreateMutation: useCreateTodo,
|
|
70
|
+
useUpdateMutation: useUpdateTodo,
|
|
71
|
+
useDeleteMutation: useDeleteTodo} =
|
|
65
72
|
createCollectionHooks<...>({collection: "todos"})
|
|
66
73
|
│ extended by (custom hooks)
|
|
67
74
|
▼
|
|
@@ -69,11 +76,10 @@ example-backend example-frontend
|
|
|
69
76
|
└─ createCollectionHooks<LocalNote>({collection: "notes"})
|
|
70
77
|
```
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
Two packages change:
|
|
73
80
|
|
|
74
|
-
1. **`@terreno/syncdb`** (runtime): `createCollectionHooks` factory in `src/react/`,
|
|
81
|
+
1. **`@terreno/syncdb`** (runtime + CLI): `createCollectionHooks` factory in `src/react/`, per-mutation `maxAttempts` plumbing through `client.mutate` → outbox → replay coordinator, and `src/codegen/` (`terreno-syncdb-codegen` bin).
|
|
75
82
|
2. **`@terreno/api`**: `listOpenApiMiddleware` (or the shared path-building step in `api/src/openApi.ts`) adds `"x-terreno-sync": {collection, scope}` to the list operation when `options.sync` is present.
|
|
76
|
-
3. **`@terreno/syncdb-codegen`** (new): CLI that parses the spec, discovers synced collections, and emits the SDK file.
|
|
77
83
|
|
|
78
84
|
### Runtime factory (`@terreno/syncdb/react`)
|
|
79
85
|
|
|
@@ -106,7 +112,7 @@ export const createCollectionHooks = <
|
|
|
106
112
|
>(config: CollectionHooksConfig): CollectionHooks<TData, TCreate, TUpdate> => { ... };
|
|
107
113
|
```
|
|
108
114
|
|
|
109
|
-
The factory returns **operation-named keys** (`useListQuery`, `useReadQuery`, `useCreateMutation`, `useUpdateMutation`, `useDeleteMutation`); the generated file renames them to
|
|
115
|
+
The factory returns **operation-named keys** (`useListQuery`, `useReadQuery`, `useCreateMutation`, `useUpdateMutation`, `useDeleteMutation`); the generated file renames them to friendly collection-specific names via destructuring (`useListQuery: useTodos`, `useReadQuery: useTodo`, …). Custom hooks can keep the generic factory keys or apply the same rename pattern. Each hook delegates to the existing `useQuery`/`useEntity`/`useMutate` internals (no listener logic duplicated) and threads `retries` into `client.mutate` as `maxAttempts`.
|
|
110
116
|
|
|
111
117
|
### Generated file shape
|
|
112
118
|
|
|
@@ -122,24 +128,24 @@ export type UpdateTodoBody = Partial<CreateTodoBody>;
|
|
|
122
128
|
export const SYNC_COLLECTIONS = ["todos"] as const;
|
|
123
129
|
|
|
124
130
|
export const {
|
|
125
|
-
useListQuery:
|
|
126
|
-
useReadQuery:
|
|
127
|
-
useCreateMutation:
|
|
128
|
-
useUpdateMutation:
|
|
129
|
-
useDeleteMutation:
|
|
131
|
+
useListQuery: useTodos,
|
|
132
|
+
useReadQuery: useTodo,
|
|
133
|
+
useCreateMutation: useCreateTodo,
|
|
134
|
+
useUpdateMutation: useUpdateTodo,
|
|
135
|
+
useDeleteMutation: useDeleteTodo,
|
|
130
136
|
} = createCollectionHooks<Todo, CreateTodoBody, UpdateTodoBody>({collection: "todos"});
|
|
131
137
|
```
|
|
132
138
|
|
|
133
139
|
Call-site comparison for migration:
|
|
134
140
|
|
|
135
141
|
```typescript
|
|
136
|
-
// RTK
|
|
137
|
-
const {data} = useGetTodosQuery({});
|
|
138
|
-
const {data} = useGetTodosByIdQuery({id});
|
|
139
|
-
const [createTodo] = usePostTodosMutation();
|
|
140
|
-
await createTodo({body}).unwrap();
|
|
141
|
-
const [patchTodo] = usePatchTodosByIdMutation();
|
|
142
|
-
const [deleteTodo] = useDeleteTodosByIdMutation();
|
|
142
|
+
// RTK (openApiSdk) // syncdb (syncDbSdk — generated)
|
|
143
|
+
const {data} = useGetTodosQuery({}); const {data} = useTodos();
|
|
144
|
+
const {data} = useGetTodosByIdQuery({id}); const {data, isPending} = useTodo(id);
|
|
145
|
+
const [createTodo] = usePostTodosMutation(); const [createTodo] = useCreateTodo();
|
|
146
|
+
await createTodo({body}).unwrap(); createTodo({data}); // sync, returns {mutationId, id}
|
|
147
|
+
const [patchTodo] = usePatchTodosByIdMutation(); const [patchTodo] = useUpdateTodo();
|
|
148
|
+
const [deleteTodo] = useDeleteTodosByIdMutation(); const [deleteTodo] = useDeleteTodo();
|
|
143
149
|
```
|
|
144
150
|
|
|
145
151
|
`SYNC_COLLECTIONS` replaces the hand-maintained list in `example-frontend/store/syncdb.ts`, closing the "client string must match backend route" gap.
|
|
@@ -176,22 +182,22 @@ None needed.
|
|
|
176
182
|
|
|
177
183
|
## UI
|
|
178
184
|
|
|
179
|
-
No new screens. `example-frontend/components/SyncTodosScreen.tsx` swaps its hand-rolled `SyncTodo` interface and string-based hooks for the generated `
|
|
185
|
+
No new screens. `example-frontend/components/SyncTodosScreen.tsx` swaps its hand-rolled `SyncTodo` interface and string-based hooks for the generated `useTodos`/`useCreateTodo`/`useUpdateTodo`/`useDeleteTodo`/`useTodo`, and `store/syncdb.ts` imports `SYNC_COLLECTIONS` from the generated file. Behavior is unchanged; the existing syncdb Playwright e2e suite is the regression net.
|
|
180
186
|
|
|
181
187
|
## Phases
|
|
182
188
|
|
|
183
|
-
Single PR
|
|
189
|
+
**Single PR** (approved). Implementation order inside the PR:
|
|
184
190
|
|
|
185
|
-
1. **syncdb runtime** — `createCollectionHooks` + `maxAttempts` plumbing (mutate → outbox → replay coordinator), with unit tests.
|
|
186
|
-
2. **api spec extension** — `x-terreno-sync` on list operations, with an OpenAPI generation test
|
|
187
|
-
3. **codegen
|
|
188
|
-
4. **integration + docs** — `example-frontend` `sync-sdk` script and screen migration; README
|
|
191
|
+
1. **syncdb runtime** — `createCollectionHooks` + `maxAttempts` plumbing (mutate → outbox → replay coordinator), with unit tests.
|
|
192
|
+
2. **api spec extension** — `x-terreno-sync` on list operations (+ pass `routePath` into `listOpenApiMiddleware`), with an OpenAPI generation test.
|
|
193
|
+
3. **codegen CLI** — `syncdb/src/codegen/`: arg parsing, spec loading, collection discovery, type emitter, file emitter, snapshot tests, `build:binary` on `@terreno/syncdb`.
|
|
194
|
+
4. **integration + docs** — `example-frontend` `sync-sdk` script and screen migration; README / reference / migration-guide updates. Codegen ships with existing `publish-syncdb` (no extra npm package).
|
|
189
195
|
|
|
190
196
|
## Feature Flags & Migrations
|
|
191
197
|
|
|
192
198
|
- No new feature flags. The existing `use-syncdb` flag continues to gate the syncdb path in `example-frontend`.
|
|
193
199
|
- No data migrations. The new `_outbox.maxAttempts` cell is optional and defaulted.
|
|
194
|
-
- Publishing
|
|
200
|
+
- **Publishing:** codegen is a bin of `@terreno/syncdb`; the existing `publish-syncdb` job is sufficient. Do not add a separate `@terreno/syncdb-codegen` package or publish job.
|
|
195
201
|
|
|
196
202
|
## Activity Log & User Updates
|
|
197
203
|
|
|
@@ -199,41 +205,38 @@ None — developer tooling only.
|
|
|
199
205
|
|
|
200
206
|
## Not Included / Future Work
|
|
201
207
|
|
|
202
|
-
- Publishing `@terreno/syncdb` and `@terreno/syncdb-codegen` to npm (add `publish-syncdb` + `publish-syncdb-codegen` jobs to `publish-on-tag.yml`).
|
|
203
208
|
- Per-platform prebuilt binaries as release artifacts.
|
|
204
209
|
- Generated wrappers for `useSyncStatus`/`useConflicts` (no per-collection typing to add).
|
|
205
210
|
- Server-driven filters/sorts in `useQuery` options (syncdb filters run client-side in JS; nothing for codegen to type beyond `TData`).
|
|
206
211
|
- Zod/AJV runtime validation of mutation payloads against the OpenAPI schema.
|
|
207
|
-
-
|
|
208
|
-
- RTK-compatible async mutation state (`isLoading`, promise-returning triggers with `.unwrap()`)
|
|
212
|
+
- Optional RTK-compatible hook naming mode (`overrides.todos.naming: "rtk"`) for teams that want import-swap migration without renames.
|
|
213
|
+
- RTK-compatible async mutation state (`isLoading`, promise-returning triggers with `.unwrap()`).
|
|
209
214
|
- A `--watch` mode.
|
|
215
|
+
- Generating `projects` hooks in example-frontend until a syncdb screen exists for that collection.
|
|
210
216
|
|
|
211
217
|
## Files to Create / Modify
|
|
212
218
|
|
|
213
219
|
**Create**
|
|
214
|
-
- `syncdb
|
|
215
|
-
- `syncdb
|
|
216
|
-
- `syncdb
|
|
217
|
-
- `syncdb
|
|
218
|
-
- `syncdb
|
|
219
|
-
- `syncdb
|
|
220
|
-
- `syncdb-codegen/src/emitSdk.ts` — assemble the output file (header, types, `SYNC_COLLECTIONS`, `createCollectionHooks` calls with overrides applied).
|
|
221
|
-
- `syncdb-codegen/src/*.test.ts` — unit + snapshot tests against `syncdb-codegen/src/fixtures/openapi.example.json`.
|
|
222
|
-
- `syncdb-codegen/README.md` — usage, args, config format, custom-hooks pattern.
|
|
220
|
+
- `syncdb/src/codegen/cli.ts` — `#!/usr/bin/env bun` entry; arg parsing (`Bun.argv` + `util.parseArgs`), exit codes, error messages.
|
|
221
|
+
- `syncdb/src/codegen/loadSpec.ts` — fetch/read + JSON parse of the OpenAPI document.
|
|
222
|
+
- `syncdb/src/codegen/discoverCollections.ts` — walk `paths`, collect `x-terreno-sync` operations, resolve entity/create/update schema refs; apply `--collections` filter/fallback.
|
|
223
|
+
- `syncdb/src/codegen/emitTypes.ts` — OpenAPI schema subset → TS interfaces.
|
|
224
|
+
- `syncdb/src/codegen/emitSdk.ts` — assemble the output file (header, types, `SYNC_COLLECTIONS`, `createCollectionHooks` calls with overrides applied).
|
|
225
|
+
- `syncdb/src/codegen/*.test.ts` — unit + snapshot tests against `syncdb/src/codegen/fixtures/openapi.example.json`.
|
|
223
226
|
- `example-frontend/syncdb-codegen.json` — per-collection overrides example (todos with default retries).
|
|
224
227
|
|
|
225
228
|
**Modify**
|
|
226
|
-
- `syncdb/
|
|
229
|
+
- `syncdb/package.json` — `"bin": {"terreno-syncdb-codegen": "./src/codegen/cli.ts"}`, `build:binary`.
|
|
230
|
+
- `syncdb/src/react/collectionHooks.ts` + `syncdb/src/react/index.ts` — `createCollectionHooks` export.
|
|
227
231
|
- `syncdb/src/client.ts` — `MutateArgs.maxAttempts?`, pass-through to outbox.
|
|
228
232
|
- `syncdb/src/mutations/outbox.ts`, `syncdb/src/storage/schema.ts` — optional `maxAttempts` cell.
|
|
229
233
|
- `syncdb/src/sync/replayCoordinator.ts` — `row.maxAttempts ?? MAX_ERROR_NACK_ATTEMPTS` in the error-nack branch.
|
|
230
234
|
- `api/src/openApi.ts` (`listOpenApiMiddleware`) — emit `x-terreno-sync` when `options.sync` is set.
|
|
231
|
-
- `example-frontend/package.json` — `"sync-sdk"
|
|
235
|
+
- `example-frontend/package.json` — `"sync-sdk"` script.
|
|
232
236
|
- `example-frontend/store/syncDbSdk.ts` — generated output (checked in, like `openApiSdk.ts`).
|
|
233
237
|
- `example-frontend/store/syncdb.ts` — import `SYNC_COLLECTIONS` from the generated file.
|
|
234
|
-
- `example-frontend/components/SyncTodosScreen.tsx` — use generated hooks
|
|
235
|
-
-
|
|
236
|
-
- `syncdb/README.md`, `docs/how-to/migrate-rtk-to-syncdb.md` — codegen sections.
|
|
238
|
+
- `example-frontend/components/SyncTodosScreen.tsx` — use generated hooks (`useTodo`, `useCreateTodo`, `useUpdateTodo`, `useDeleteTodo`); keep `useEntityIds` for list virtualization.
|
|
239
|
+
- `syncdb/README.md`, `docs/how-to/migrate-rtk-to-syncdb.md`, `docs/reference/syncdb.md` — codegen sections.
|
|
237
240
|
|
|
238
241
|
## Task List
|
|
239
242
|
|
|
@@ -241,12 +244,13 @@ See [docs/tasks/syncdb-codegen.md](../tasks/syncdb-codegen.md).
|
|
|
241
244
|
|
|
242
245
|
## Acceptance Criteria
|
|
243
246
|
|
|
244
|
-
- [
|
|
245
|
-
- [
|
|
246
|
-
- [
|
|
247
|
-
- [
|
|
248
|
-
- [
|
|
249
|
-
- [
|
|
250
|
-
- [
|
|
247
|
+
- [x] `createCollectionHooks<Todo>({collection: "todos"})` returns the five per-operation hooks (`useListQuery`, `useReadQuery`, `useCreateMutation`, `useUpdateMutation`, `useDeleteMutation`) whose behavior matches direct `useQuery`/`useEntity`/`useMutate` calls; mutation hooks return `[trigger]` tuples so RTK-style destructuring compiles (unit tests in `syncdb/src/react/`).
|
|
248
|
+
- [x] `createCollectionHooks({collection: "todos", retries: false})` produces mutations whose outbox rows fail terminally after a single `error` nack; `retries: 3` fails after three attempts; omitted keeps the current 5-attempt backoff (replay coordinator tests).
|
|
249
|
+
- [x] `GET /openapi.json` from a backend with `sync: {scope: {type: "owner"}}` on `/todos` includes `x-terreno-sync: {collection: "todos", scope: "owner"}` on the list operation; non-synced routes have no extension (api test).
|
|
250
|
+
- [x] `terreno-syncdb-codegen --schema <fixture> --out out.ts` emits a file that type-checks under the example-frontend tsconfig and contains `SYNC_COLLECTIONS`, typed interfaces, and per synced collection the five friendly hooks (`useTodos`, `useTodo`, `useCreateTodo`, `useUpdateTodo`, `useDeleteTodo` for the todos fixture — snapshot test).
|
|
251
|
+
- [x] `--collections todos` filters output to that collection; a spec without extensions and no `--collections` flag exits non-zero with an actionable message.
|
|
252
|
+
- [x] A config file with `{overrides: {todos: {retries: false}}}` produces `createCollectionHooks<...>({collection: "todos", retries: false})` in the output.
|
|
253
|
+
- [x] `bun run build:binary` in `syncdb/` produces a standalone executable that generates identical output to the bin entry.
|
|
251
254
|
- [ ] `bun run sync-sdk` in `example-frontend` (backend running) regenerates `store/syncDbSdk.ts` with no diff when the backend is unchanged; SyncTodosScreen works against the generated hooks (existing syncdb e2e suite passes).
|
|
252
|
-
- [
|
|
255
|
+
- [x] `bun run lint` (syncdb, example-frontend), `bun test` in `syncdb/` (500 pass), and `api/src/openApi.test.ts` (26 pass).
|
|
256
|
+
- [x] No separate `@terreno/syncdb-codegen` package or `publish-syncdb-codegen` job; the CLI ships with `@terreno/syncdb`.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
4
4
|
|
|
5
5
|
**Status:** Shaped, ready for review
|
|
6
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1081
|
|
6
7
|
**Priority:** High
|
|
7
8
|
**Effort:** Large
|
|
8
9
|
**Supersedes:** the prior v1 plan (PR #739, previous content of this file) and the foundation implementation in PR #835 (to be closed with a comment linking here). Proven pieces of #835 — the outbox state machine, AES-GCM codec, delta applier, and type contracts — are harvested and adapted, not reused wholesale.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# SyncDB Phase C Design (syncdb-2)
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1081
|
|
3
4
|
Design authority doc for Phase C (server protocol correctness). Hand this verbatim to a
|
|
4
5
|
Sonnet implementer. Read alongside `terreno-syncdb-2.md` (INV-1..4, C1-C8). C5 (mutation
|
|
5
6
|
ledger lease) is owned/implemented elsewhere — this doc designs *around* it and never
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# @terreno/langfuse
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1093
|
|
3
4
|
A TerrenoApp plugin that integrates [Langfuse](https://langfuse.com) into Terreno backends and frontends. Provides prompt management with MongoDB caching, OpenTelemetry tracing, Vercel AI SDK helpers, React hooks, and an admin UI — all wired up via `.install()`.
|
|
4
5
|
|
|
5
6
|
> **Prerequisite:** This package relies on the new TerrenoApp plugin system (see [PR #149](https://github.com/FlourishHealth/terreno/pull/149)). TerrenoApp must support `.install()` before this package can be used.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# SyncDB Hardening Plan (terreno-syncdb-2)
|
|
2
2
|
|
|
3
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1081
|
|
3
4
|
Implementation plan for hardening PR #869 (`@terreno/syncdb` local-first data layer,
|
|
4
5
|
branch `worktree-ip-syncdb`). This plan is self-contained: it restates each defect,
|
|
5
6
|
the exact fix, the files involved, and the tests that prove it. Work happens on a
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Implementation Plan: Upgrade Banner
|
|
2
2
|
|
|
3
|
+
**Status:** Complete
|
|
4
|
+
**Closed:** 2026-08-20 — Shipped via `VersionConfig` model, `VersionCheckPlugin` (`GET /version-check`), `AdminVersionConfig`, `useUpgradeCheck` (`@terreno/rtk`), and `UpgradeRequiredScreen` (`@terreno/ui`).
|
|
5
|
+
**Roadmap issue:** https://github.com/FlourishHealth/terreno/issues/1079
|
|
6
|
+
|
|
3
7
|
*When an engineer is assigned to a project but before you begin coding, you should fill in the implementation plan and get feedback from the engineering team. Once you have finished or you make any changes, tag Josh with the @ symbol so he can review. Also tag anyone else that needs to be notified, has conflicting work, etc.*
|
|
4
8
|
|
|
5
9
|
## Models
|
|
@@ -6,7 +6,6 @@ Technical reference for Terreno packages and APIs. Information-oriented, precise
|
|
|
6
6
|
|
|
7
7
|
- [@terreno/api](api.md) — modelRouter, auth, permissions, setupServer
|
|
8
8
|
- [@terreno/ui](ui.md) — Components, theming, layout
|
|
9
|
-
- [@terreno/rtk](rtk.md) — Auth slice, SDK codegen, token handling
|
|
10
9
|
- [@terreno/syncdb](syncdb.md) — Local-first data layer (reads, writes, offline sync)
|
|
11
10
|
- [@terreno/ai](ai.md) — AI service, GPT routes, Langfuse integration
|
|
12
11
|
- [@terreno/admin-backend](admin-backend.md) — Auto-generated admin CRUD endpoints
|
|
@@ -18,11 +17,17 @@ Technical reference for Terreno packages and APIs. Information-oriented, precise
|
|
|
18
17
|
- [@terreno/mcp](mcp-server.md) — AI coding assistant integration (MCP)
|
|
19
18
|
- [@terreno/test](test.md) — Bun test helpers and in-memory MongoDB
|
|
20
19
|
|
|
20
|
+
## Migrating from RTK
|
|
21
|
+
|
|
22
|
+
`@terreno/rtk` is deprecated for **data synchronization**. New apps should use [`@terreno/syncdb`](syncdb.md). Existing RTK consumers: follow [Migrate from @terreno/rtk to @terreno/syncdb](../how-to/migrate-rtk-to-syncdb.md). Archived RTK reference: [legacy/rtk.md](legacy/rtk.md).
|
|
23
|
+
|
|
21
24
|
## Configuration
|
|
22
25
|
|
|
23
26
|
- [Environment Variables](environment-variables.md) — Complete environment variable reference for all packages
|
|
24
27
|
|
|
25
28
|
## Other references
|
|
26
29
|
|
|
30
|
+
- [Lifecycle plugin](lifecycle-plugin.md) — Grow/Pick/Roast/Brew/Taste contracts, results, and transitions
|
|
31
|
+
- [Install agent skills](../how-to/install-agent-skills.md) — Published `skills/` tree for `npx skills`
|
|
27
32
|
- Root [package.json](https://github.com/flourishhealth/terreno/blob/master/package.json) — Workspace scripts and catalog
|
|
28
33
|
- Per-package `package.json` in each package directory — Commands and dependencies
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Admin configuration reference
|
|
2
|
+
|
|
3
|
+
`AdminConfig` is the `admin` object accepted by `modelRouter`.
|
|
4
|
+
|
|
5
|
+
| Field | Django admin analogue | Description |
|
|
6
|
+
| --- | --- | --- |
|
|
7
|
+
| `displayName` | `verbose_name_plural` | Required navigation and page label. |
|
|
8
|
+
| `listFields` | `list_display` | Required allowlist of list/form fields. |
|
|
9
|
+
| `listDisplay` | `list_display` | Optional visible list columns. |
|
|
10
|
+
| `listDisplayLinks` | `list_display_links` | Columns linking to the edit screen. |
|
|
11
|
+
| `searchFields` | `search_fields` | String fields matched by the list search box as a case-insensitive partial query. |
|
|
12
|
+
| `sortableFields` | `sortable_by` | Columns allowed to sort. |
|
|
13
|
+
| `defaultSort` | `ordering` | Initial sort field(s), using `-field` for descending. |
|
|
14
|
+
| `pageSize` | `list_per_page` | Rows per page. |
|
|
15
|
+
| `filters` | `list_filter` | Filter drawer definitions. |
|
|
16
|
+
| `fieldsets` | `fieldsets` | Ordered collapsible form groups. |
|
|
17
|
+
| `fieldOrder` | `fields` | Flat form order when fieldsets are absent. |
|
|
18
|
+
| `readonlyFields` | `readonly_fields` | Visible, non-editable fields. |
|
|
19
|
+
| `hiddenFields` | `exclude` | Fields omitted from admin forms and responses. |
|
|
20
|
+
| `excludeFields` | `exclude` | Additional response-scrubbing fields. |
|
|
21
|
+
| `autocompleteFields` | `autocomplete_fields` | Reference fields using async search. |
|
|
22
|
+
| `fieldOverrides` | `formfield_overrides` | Per-field label/help/widget metadata. |
|
|
23
|
+
| `adminPermissions` | `has_*_permission` | Admin-route permission method overrides. |
|
|
24
|
+
| `adminFilter` | `get_queryset` | Server-enforced query constraint. |
|
|
25
|
+
| `actions` | `actions` | Declarative bulk-patch/background actions. |
|
|
26
|
+
| `bulkPatchAllowlist` | — | Fields accepted by bulk patch. |
|
|
27
|
+
| `recordTitleField` | `__str__` | Field used for edit-page titles. |
|
|
28
|
+
| `group` | `app_label` | Navigation group. |
|
|
29
|
+
| `icon` | — | Navigation icon name. |
|
|
30
|
+
| `realtime` | — | Emit scrubbed admin change events. |
|
|
31
|
+
| `includeDeleted` | — | Reserved compatibility option. |
|
|
32
|
+
|
|
33
|
+
## Supporting types
|
|
34
|
+
|
|
35
|
+
`AdminFilter` has `field`, `kind`, and optional `label`. Kinds are `boolean`, `text`, `ref`,
|
|
36
|
+
`dateRange`, and `choice`; refs add `refModel`, choices add `{label,value}[]`.
|
|
37
|
+
|
|
38
|
+
`AdminFieldset` has `title`, `fields`, optional `description`, and optional `collapsed`.
|
|
39
|
+
|
|
40
|
+
`AdminAction` has `id`, `label`, optional `confirm`, `background`, and `patchKeys`.
|
|
41
|
+
|
|
42
|
+
`AdminContribution` may provide `models`, `customScreens`, `homeWidgets`, and `scripts`.
|
|
43
|
+
Model contributions contain `model`, `routePath`, and `admin`. Duplicate `routePath`s
|
|
44
|
+
throw for registered routers; the same Mongoose model at two paths gets unique config
|
|
45
|
+
`name`s so the admin UI can route to each.
|
|
46
|
+
|
|
47
|
+
## Legacy migration
|
|
48
|
+
|
|
49
|
+
Move each object from `AdminApp.models` to the model's `modelRouter({admin: ...})`; remove
|
|
50
|
+
`model` and `routePath` from the nested config because those come from the router registration.
|
|
51
|
+
Plugin-owned models should come from `plugin.adminContribution()` instead of host duplication.
|
|
@@ -72,6 +72,7 @@ src/
|
|
|
72
72
|
service/
|
|
73
73
|
aiService.ts # Provider-agnostic AI service
|
|
74
74
|
fileStorage.ts # GCS upload helper
|
|
75
|
+
getMCPTools.ts # modelRouter MCP tools as Vercel AI SDK tools
|
|
75
76
|
mcpService.ts # MCP client connections
|
|
76
77
|
parseAiJson.ts # LLM JSON normalization/parsing
|
|
77
78
|
prompts.ts # System prompt constants
|
|
@@ -84,7 +85,8 @@ src/
|
|
|
84
85
|
## Key exports
|
|
85
86
|
|
|
86
87
|
- **Plugins:** `AiApp`, `LangfuseApp`
|
|
87
|
-
- **Service:** `AIService`, `TemperaturePresets`, `FileStorageService`, `MCPService
|
|
88
|
+
- **Service:** `AIService`, `TemperaturePresets`, `FileStorageService`, `MCPService`,
|
|
89
|
+
`getMCPTools`
|
|
88
90
|
- **Models:** `AIRequest`, `GptHistory`, `FileAttachment`, `Project`
|
|
89
91
|
- **Routes:** `addGptRoutes`, `addGptHistoryRoutes`, `addAiRequestsExplorerRoutes`, `addFileRoutes`, `addProjectRoutes`, `addMcpRoutes`
|
|
90
92
|
- **Structured output:** `parseAiJson`, `normalizeLlmJsonTextForStructuredOutput`, re-exported `Output`, `jsonSchema`, `JSONValue`, `FlexibleSchema` from `ai`
|
|
@@ -393,6 +395,18 @@ await storage.getSignedUrl(gcsKey); // 1-hour v4 signed URL
|
|
|
393
395
|
await storage.delete(gcsKey); // GCS delete + soft-delete FileAttachment
|
|
394
396
|
```
|
|
395
397
|
|
|
398
|
+
## getMCPTools
|
|
399
|
+
|
|
400
|
+
Wraps registered `modelRouter` MCP tools as Vercel AI SDK `Tool` objects for
|
|
401
|
+
in-process `streamText` / `generateText`. HTTP MCP clients still use `POST /mcp`
|
|
402
|
+
from `@terreno/api`; this helper is the chat-route path.
|
|
403
|
+
|
|
404
|
+
```typescript
|
|
405
|
+
import {getMCPTools} from "@terreno/ai";
|
|
406
|
+
|
|
407
|
+
const tools = getMCPTools(req.user);
|
|
408
|
+
```
|
|
409
|
+
|
|
396
410
|
## MCPService
|
|
397
411
|
|
|
398
412
|
Manages SSE MCP client connections for tool calling.
|
|
@@ -5,6 +5,7 @@ REST API framework built on Express and Mongoose. Provides modelRouter (CRUD end
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
7
|
- [Server Setup](#server-setup)
|
|
8
|
+
- [MCP tools](#mcp-tools)
|
|
8
9
|
- [Authentication](#authentication)
|
|
9
10
|
- [Model Schema Conventions](#model-schema-conventions)
|
|
10
11
|
- [Mongoose Plugins](#mongoose-plugins)
|
|
@@ -20,6 +21,7 @@ REST API framework built on Express and Mongoose. Provides modelRouter (CRUD end
|
|
|
20
21
|
## Key exports
|
|
21
22
|
|
|
22
23
|
- `TerrenoApp`, `setupServer`, `modelRouter`, `Permissions`, `OwnerQueryFilter`
|
|
24
|
+
- `registerMCPTool`, `getMCPRegistry`
|
|
23
25
|
- `APIError`, `logger`, `asyncHandler`, `authenticateMiddleware`
|
|
24
26
|
- Logging: `logger`, `createScopedLogger`, `createFeatureFlaggedLogger`, `setupLogging`, `formatLogContextSuffix`
|
|
25
27
|
- Correlation: `runWithRequestContext`, `getCurrentLogContext`, `requestContextMiddleware`, `REQUEST_CONTEXT_ATTRIBUTE_NAMES`
|
|
@@ -84,6 +86,22 @@ setupServer({
|
|
|
84
86
|
|
|
85
87
|
Both patterns create the same middleware stack (CORS, auth, logging, OpenAPI).
|
|
86
88
|
|
|
89
|
+
## MCP tools
|
|
90
|
+
|
|
91
|
+
Opt a model into Model Context Protocol tools with `mcp` on `modelRouter`. `TerrenoApp` mounts `POST /mcp` when any model has `mcp` or a custom tool is registered.
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
const todoRouter = modelRouter("/todos", Todo, {
|
|
95
|
+
mcp: {
|
|
96
|
+
excludeFields: ["ownerId"],
|
|
97
|
+
methods: ["list", "read", "create", "update", "delete"],
|
|
98
|
+
},
|
|
99
|
+
permissions: { /* same as REST */ },
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Cross-model tools use `registerMCPTool` (see the example backend's `users_todo_statuses`). How-to: [Expose MCP tools](../how-to/expose-mcp-tools.md). In-process Vercel AI SDK wrappers: `getMCPTools` from `@terreno/ai`.
|
|
104
|
+
|
|
87
105
|
## Authentication
|
|
88
106
|
|
|
89
107
|
@terreno/api includes built-in authentication with multiple strategies:
|