@terreno/mcp 57.3.0 → 57.4.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/README.md +7 -1
- package/dist/bootstrap.d.ts +0 -18
- package/dist/bootstrap.d.ts.map +1 -1
- package/dist/bootstrap.js +10 -2428
- package/dist/bootstrap.js.map +1 -1
- package/dist/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
- package/dist/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
- package/dist/docs/resources/syncdb.md +4 -1
- package/dist/docs/upgrades/57.4.0.md +140 -0
- package/dist/docs/versioned/next/README.md +3 -0
- package/dist/docs/versioned/next/explanation/README.md +4 -1
- package/dist/docs/versioned/next/explanation/admin-interface.md +20 -13
- package/dist/docs/versioned/next/explanation/charts.md +29 -0
- package/dist/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
- package/dist/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/dist/docs/versioned/next/explanation/example-coverage.md +43 -0
- package/dist/docs/versioned/next/explanation/local-first-data.md +5 -2
- package/dist/docs/versioned/next/explanation/loop-engineering.md +9 -6
- package/dist/docs/versioned/next/explanation/model-router-actions.md +19 -2
- package/dist/docs/versioned/next/explanation/organizations.md +88 -0
- package/dist/docs/versioned/next/explanation/planning-plugins.md +114 -0
- package/dist/docs/versioned/next/explanation/positioning.md +3 -3
- package/dist/docs/versioned/next/explanation/repository-settings.md +6 -5
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
- package/dist/docs/versioned/next/explanation/source-rules.md +1 -1
- package/dist/docs/versioned/next/explanation/static-analysis.md +76 -16
- package/dist/docs/versioned/next/how-to/README.md +10 -0
- package/dist/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
- package/dist/docs/versioned/next/how-to/add-organizations.md +204 -0
- package/dist/docs/versioned/next/how-to/admin-add-model.md +9 -1
- package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
- package/dist/docs/versioned/next/how-to/audit-log.md +85 -0
- package/dist/docs/versioned/next/how-to/background-jobs.md +296 -0
- package/dist/docs/versioned/next/how-to/build-admin-screens.md +42 -13
- package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
- package/dist/docs/versioned/next/how-to/circleci.md +136 -27
- package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
- package/dist/docs/versioned/next/how-to/configure-better-auth.md +4 -0
- package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
- package/dist/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
- package/dist/docs/versioned/next/how-to/github-actions-ci.md +4 -3
- package/dist/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
- package/dist/docs/versioned/next/how-to/in-app-notifications.md +147 -0
- package/dist/docs/versioned/next/how-to/install-agent-skills.md +24 -12
- package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
- package/dist/docs/versioned/next/how-to/product-announcements.md +155 -0
- package/dist/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
- package/dist/docs/versioned/next/how-to/run-tests-locally.md +102 -0
- package/dist/docs/versioned/next/how-to/seed-a-database.md +7 -2
- package/dist/docs/versioned/next/implementationPlans/README.md +8 -5
- package/dist/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
- package/dist/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
- package/dist/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
- package/dist/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
- package/dist/docs/versioned/next/implementationPlans/announcements.md +289 -0
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
- package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
- package/dist/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
- package/dist/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
- package/dist/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
- package/dist/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
- package/dist/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
- package/dist/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
- package/dist/docs/versioned/next/implementationPlans/job-queues.md +330 -0
- package/dist/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
- package/dist/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
- package/dist/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
- package/dist/docs/versioned/next/implementationPlans/notification-center.md +185 -0
- package/dist/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
- package/dist/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/dist/docs/versioned/next/reference/README.md +4 -0
- package/dist/docs/versioned/next/reference/admin-backend.md +83 -5
- package/dist/docs/versioned/next/reference/admin-config.md +7 -0
- package/dist/docs/versioned/next/reference/admin-frontend.md +178 -5
- package/dist/docs/versioned/next/reference/admin-spa.md +18 -1
- package/dist/docs/versioned/next/reference/ai.md +2 -0
- package/dist/docs/versioned/next/reference/announcements.md +179 -0
- package/dist/docs/versioned/next/reference/api.md +343 -4
- package/dist/docs/versioned/next/reference/comms.md +33 -0
- package/dist/docs/versioned/next/reference/create-terreno-app.md +44 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +29 -0
- package/dist/docs/versioned/next/reference/jobs.md +319 -0
- package/dist/docs/versioned/next/reference/legacy/rtk.md +5 -1
- package/dist/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
- package/dist/docs/versioned/next/reference/mcp-server.md +2 -43
- package/dist/docs/versioned/next/reference/scan-plugin.md +169 -0
- package/dist/docs/versioned/next/reference/syncdb.md +38 -12
- package/dist/docs/versioned/next/reference/test.md +57 -6
- package/dist/docs/versioned/next/reference/ui-performance.md +2 -1
- package/dist/docs/versioned/next/reference/ui.md +257 -3
- package/dist/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
- package/dist/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
- package/dist/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
- package/dist/docs/versioned/next/tasks/announcements.md +173 -0
- package/dist/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
- package/dist/docs/versioned/next/tasks/create-terreno-app.md +82 -0
- package/dist/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
- package/dist/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
- package/dist/docs/versioned/next/tasks/framework-audit-log.md +86 -0
- package/dist/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
- package/dist/docs/versioned/next/tasks/job-queues.md +144 -0
- package/dist/docs/versioned/next/tasks/knip-cleanup.md +146 -0
- package/dist/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
- package/dist/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
- package/dist/docs/versioned/next/tasks/notification-center.md +102 -0
- package/dist/docs/versioned/next/tasks/org-management-ui.md +14 -14
- package/dist/docs/versioned/next/tutorials/README.md +1 -1
- package/dist/docs/versioned/next/tutorials/getting-started.md +62 -22
- package/dist/help/excerpt.d.ts +2 -0
- package/dist/help/excerpt.d.ts.map +1 -0
- package/dist/help/excerpt.js +8 -0
- package/dist/help/excerpt.js.map +1 -0
- package/dist/help/updateNotes.d.ts +42 -0
- package/dist/help/updateNotes.d.ts.map +1 -0
- package/dist/help/updateNotes.js +235 -0
- package/dist/help/updateNotes.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/local/tools/databaseQuery.d.ts +1 -0
- package/dist/local/tools/databaseQuery.d.ts.map +1 -1
- package/dist/local/tools/databaseQuery.js +1 -0
- package/dist/local/tools/databaseQuery.js.map +1 -1
- package/dist/packageGuidelines.d.ts +2 -2
- package/dist/packageGuidelines.d.ts.map +1 -1
- package/dist/packageGuidelines.js +2 -8
- package/dist/packageGuidelines.js.map +1 -1
- package/dist/resources.d.ts +3 -0
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +3 -0
- package/dist/resources.js.map +1 -1
- package/dist/search/chunker.d.ts +1 -0
- package/dist/search/chunker.d.ts.map +1 -1
- package/dist/search/chunker.js +1 -0
- package/dist/search/chunker.js.map +1 -1
- package/dist/search/docIndex.d.ts +1 -5
- package/dist/search/docIndex.d.ts.map +1 -1
- package/dist/search/docIndex.js +1 -0
- package/dist/search/docIndex.js.map +1 -1
- package/dist/search/docVersion.d.ts +2 -1
- package/dist/search/docVersion.d.ts.map +1 -1
- package/dist/search/docVersion.js +3 -1
- package/dist/search/docVersion.js.map +1 -1
- package/dist/tools.d.ts +2 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +113 -2
- package/dist/tools.js.map +1 -1
- package/package.json +28 -27
- package/src/docs/guidelines/admin-frontend/guidelines/core.md +5 -1
- package/src/docs/guidelines/api/skills/mongoose-schema-safety/SKILL.md +23 -10
- package/src/docs/resources/syncdb.md +4 -1
- package/src/docs/upgrades/57.4.0.md +140 -0
- package/src/docs/versioned/next/README.md +3 -0
- package/src/docs/versioned/next/explanation/README.md +4 -1
- package/src/docs/versioned/next/explanation/admin-interface.md +20 -13
- package/src/docs/versioned/next/explanation/charts.md +29 -0
- package/src/docs/versioned/next/explanation/deployment-architecture-gcp.md +32 -0
- package/src/docs/versioned/next/explanation/deployment-baseline.md +1 -1
- package/src/docs/versioned/next/explanation/example-coverage.md +43 -0
- package/src/docs/versioned/next/explanation/local-first-data.md +5 -2
- package/src/docs/versioned/next/explanation/loop-engineering.md +9 -6
- package/src/docs/versioned/next/explanation/model-router-actions.md +19 -2
- package/src/docs/versioned/next/explanation/organizations.md +88 -0
- package/src/docs/versioned/next/explanation/planning-plugins.md +114 -0
- package/src/docs/versioned/next/explanation/positioning.md +3 -3
- package/src/docs/versioned/next/explanation/repository-settings.md +6 -5
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +44 -34
- package/src/docs/versioned/next/explanation/source-rules.md +1 -1
- package/src/docs/versioned/next/explanation/static-analysis.md +76 -16
- package/src/docs/versioned/next/how-to/README.md +10 -0
- package/src/docs/versioned/next/how-to/add-gpt-chat-mascot.md +92 -0
- package/src/docs/versioned/next/how-to/add-organizations.md +204 -0
- package/src/docs/versioned/next/how-to/admin-add-model.md +9 -1
- package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +7 -1
- package/src/docs/versioned/next/how-to/audit-log.md +85 -0
- package/src/docs/versioned/next/how-to/background-jobs.md +296 -0
- package/src/docs/versioned/next/how-to/build-admin-screens.md +42 -13
- package/src/docs/versioned/next/how-to/charts-and-dashboards.md +37 -0
- package/src/docs/versioned/next/how-to/circleci.md +136 -27
- package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +36 -0
- package/src/docs/versioned/next/how-to/configure-better-auth.md +4 -0
- package/src/docs/versioned/next/how-to/create-a-terreno-app.md +154 -0
- package/src/docs/versioned/next/how-to/deploy-backend-to-cloud-run.md +2 -0
- package/src/docs/versioned/next/how-to/github-actions-ci.md +4 -3
- package/src/docs/versioned/next/how-to/github-issue-lifecycle.md +12 -8
- package/src/docs/versioned/next/how-to/in-app-notifications.md +147 -0
- package/src/docs/versioned/next/how-to/install-agent-skills.md +24 -12
- package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +19 -3
- package/src/docs/versioned/next/how-to/product-announcements.md +155 -0
- package/src/docs/versioned/next/how-to/run-mongodb-migrations.md +127 -0
- package/src/docs/versioned/next/how-to/run-tests-locally.md +102 -0
- package/src/docs/versioned/next/how-to/seed-a-database.md +7 -2
- package/src/docs/versioned/next/implementationPlans/README.md +8 -5
- package/src/docs/versioned/next/implementationPlans/admin-scripts-durable-jobs.md +44 -0
- package/src/docs/versioned/next/implementationPlans/agent-ui-blocks.md +386 -0
- package/src/docs/versioned/next/implementationPlans/agentic-sdlc-plugin.md +11 -8
- package/src/docs/versioned/next/implementationPlans/announcements-surfaces.md +231 -0
- package/src/docs/versioned/next/implementationPlans/announcements.md +289 -0
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +3 -1
- package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +246 -0
- package/src/docs/versioned/next/implementationPlans/comms-admin-dashboard.md +4 -3
- package/src/docs/versioned/next/implementationPlans/create-terreno-app.md +177 -0
- package/src/docs/versioned/next/implementationPlans/datatable-server-side-filtering.md +219 -0
- package/src/docs/versioned/next/implementationPlans/examples-demo-coverage.md +17 -17
- package/src/docs/versioned/next/implementationPlans/framework-audit-log.md +247 -0
- package/src/docs/versioned/next/implementationPlans/gpt-chat-mascot.md +40 -0
- package/src/docs/versioned/next/implementationPlans/job-queues.md +330 -0
- package/src/docs/versioned/next/implementationPlans/knip-cleanup.md +136 -0
- package/src/docs/versioned/next/implementationPlans/migrate-admin-to-syncdb.md +210 -0
- package/src/docs/versioned/next/implementationPlans/mongodb-migrations-tooling.md +254 -0
- package/src/docs/versioned/next/implementationPlans/notification-center.md +185 -0
- package/src/docs/versioned/next/implementationPlans/org-management-ui.md +8 -3
- package/src/docs/versioned/next/implementationPlans/positioning-django-rails-universal.md +1 -1
- package/src/docs/versioned/next/reference/README.md +4 -0
- package/src/docs/versioned/next/reference/admin-backend.md +83 -5
- package/src/docs/versioned/next/reference/admin-config.md +7 -0
- package/src/docs/versioned/next/reference/admin-frontend.md +178 -5
- package/src/docs/versioned/next/reference/admin-spa.md +18 -1
- package/src/docs/versioned/next/reference/ai.md +2 -0
- package/src/docs/versioned/next/reference/announcements.md +179 -0
- package/src/docs/versioned/next/reference/api.md +343 -4
- package/src/docs/versioned/next/reference/comms.md +33 -0
- package/src/docs/versioned/next/reference/create-terreno-app.md +44 -0
- package/src/docs/versioned/next/reference/environment-variables.md +29 -0
- package/src/docs/versioned/next/reference/jobs.md +319 -0
- package/src/docs/versioned/next/reference/legacy/rtk.md +5 -1
- package/src/docs/versioned/next/reference/lifecycle-plugin.md +29 -11
- package/src/docs/versioned/next/reference/mcp-server.md +2 -43
- package/src/docs/versioned/next/reference/scan-plugin.md +169 -0
- package/src/docs/versioned/next/reference/syncdb.md +38 -12
- package/src/docs/versioned/next/reference/test.md +57 -6
- package/src/docs/versioned/next/reference/ui-performance.md +2 -1
- package/src/docs/versioned/next/reference/ui.md +257 -3
- package/src/docs/versioned/next/tasks/admin-scripts-durable-jobs.md +10 -0
- package/src/docs/versioned/next/tasks/agent-ui-blocks.md +114 -0
- package/src/docs/versioned/next/tasks/announcements-surfaces.md +101 -0
- package/src/docs/versioned/next/tasks/announcements.md +173 -0
- package/src/docs/versioned/next/tasks/charts-and-dashboards.md +120 -0
- package/src/docs/versioned/next/tasks/create-terreno-app.md +82 -0
- package/src/docs/versioned/next/tasks/datatable-server-side-filtering.md +87 -0
- package/src/docs/versioned/next/tasks/examples-demo-coverage.md +123 -17
- package/src/docs/versioned/next/tasks/framework-audit-log.md +86 -0
- package/src/docs/versioned/next/tasks/gpt-chat-mascot.md +13 -0
- package/src/docs/versioned/next/tasks/job-queues.md +144 -0
- package/src/docs/versioned/next/tasks/knip-cleanup.md +146 -0
- package/src/docs/versioned/next/tasks/migrate-admin-to-syncdb.md +209 -0
- package/src/docs/versioned/next/tasks/mongodb-migrations-tooling.md +107 -0
- package/src/docs/versioned/next/tasks/notification-center.md +102 -0
- package/src/docs/versioned/next/tasks/org-management-ui.md +14 -14
- package/src/docs/versioned/next/tutorials/README.md +1 -1
- package/src/docs/versioned/next/tutorials/getting-started.md +62 -22
|
@@ -12,14 +12,19 @@ Problem-oriented, practical steps. Use these when you know what you want to do.
|
|
|
12
12
|
|
|
13
13
|
## Contents
|
|
14
14
|
|
|
15
|
+
- [Create a Terreno app](create-a-terreno-app.md) — Scaffold a full-stack app with `create-terreno-app` (Bun or npm)
|
|
15
16
|
- [Update dependencies](update-dependencies.md) — Daily rolling PR, exercise test, Expo fingerprint freeze
|
|
16
17
|
- [CircleCI](circleci.md) — Dual-run CI on CircleCI (package CI / policy / e2e; deploys later)
|
|
18
|
+
- [Run tests locally](run-tests-locally.md) — Map every CircleCI test job to a Cursor Cloud / laptop command
|
|
17
19
|
- [GitHub Actions CI](github-actions-ci.md) — Cut PR runtime: docs previews, compile-once e2e, pinned Bun
|
|
18
20
|
- [Add feature flags](add-feature-flags.md) — Add feature flags, A/B testing, and OpenFeature migration
|
|
19
21
|
- [Rate limiting](rate-limiting.md) — Opt-in HTTP limiter on `TerrenoApp` (memory or Redis)
|
|
20
22
|
- [Receive inbound webhooks](inbound-webhooks.md) — HMAC, Stripe, Twilio, and SendGrid signatures on `WebhooksApp`
|
|
23
|
+
- [Durable background jobs](background-jobs.md) — `@terreno/jobs`: enqueue, workers, schedules, Cloud Tasks / Vercel Queues
|
|
21
24
|
- [Create a Mongoose model](create-a-model.md) — Define models with proper conventions
|
|
25
|
+
- [Enable the framework audit log](audit-log.md) — `AuditApp`, per-router `audit`, append-only events
|
|
22
26
|
- [Seed a database](seed-a-database.md) — Idempotent sync, dry runs, and guarded reset-and-reseed
|
|
27
|
+
- [Run MongoDB migrations](run-mongodb-migrations.md) — Versioned `up`/`down` files, `terreno-migrate`, production gate
|
|
23
28
|
- [Add GitHub OAuth authentication](add-github-oauth.md) — Enable GitHub login for your API
|
|
24
29
|
- [Configure Better Auth](configure-better-auth.md) — Set up Better Auth with social OAuth (Google, GitHub, Apple)
|
|
25
30
|
- [Password reset and email verification](password-reset.md) — JWT routes, comms templates, Better Auth hooks
|
|
@@ -32,7 +37,12 @@ Problem-oriented, practical steps. Use these when you know what you want to do.
|
|
|
32
37
|
- [Upgrade banner](upgrade-banner.md) — Soft warning and hard-block app update UX
|
|
33
38
|
- [Upgrade Terreno](upgrade-terreno.md) — Lockstep `@terreno/*` bumps, note range, order, rollback
|
|
34
39
|
- [Track upstream Expo](track-upstream-expo.md) — Morning probe for SDK betas; `release-X.Y.Z` loop log + native fingerprint deps
|
|
40
|
+
- [Compose charts in a dashboard grid](charts-and-dashboards.md) — `LineChart` / `BarChart` / `AreaChart` / `DonutChart` inside `Card`s in `DashboardGrid`
|
|
41
|
+
- [Compare rendered chart snapshots](compare-chart-rendered-snapshots.md) — PNG goldens for the chart visual gallery, not JSON snapshots
|
|
35
42
|
- [Migrate from @terreno/rtk to @terreno/syncdb](migrate-rtk-to-syncdb.md) — Move data sync to the local-first layer
|
|
43
|
+
- [In-app notifications](in-app-notifications.md) — Register `NotificationsApp`, syncdb inbox, and UI bell
|
|
44
|
+
- [Add organizations](add-organizations.md) — Opt into Membership-backed tenancy, RBAC, admin, and seeded examples
|
|
45
|
+
- [Add a GPT chat mascot](add-gpt-chat-mascot.md) — Optional consumer-owned character on empty `GPTChat`
|
|
36
46
|
|
|
37
47
|
## Admin
|
|
38
48
|
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Add a GPT chat mascot
|
|
2
|
+
|
|
3
|
+
Pass a `mascot` node into `GPTChat`. Terreno does not ship a character — the consumer owns the image, Lottie, icon, or copy.
|
|
4
|
+
|
|
5
|
+
## When it shows
|
|
6
|
+
|
|
7
|
+
| Chat state | Mascot |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| `currentMessages` is empty | Rendered, centered in the chat panel above suggested prompts |
|
|
10
|
+
| One or more messages | Hidden |
|
|
11
|
+
|
|
12
|
+
The empty state fills the message area and centers both axes. GPTChat sizes that hero to the message viewport so a short mascot stays centered, and the same scroller still reaches a large mascot, suggested prompts, or an open keyboard. Streaming feedback stays with the centered hero until the first message arrives. Size the mascot for a hero slot (`Heading size="2xl"`, a large `Image`, or a Lottie view).
|
|
13
|
+
|
|
14
|
+
Omit `mascot` to keep the default empty chat (prompts only, or a blank panel).
|
|
15
|
+
|
|
16
|
+
## Steps
|
|
17
|
+
|
|
18
|
+
### 1. Build the node in your app
|
|
19
|
+
|
|
20
|
+
Keep the asset in the consumer package. `GPTChat` only mounts whatever you pass.
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
import {Box, Heading} from "@terreno/ui";
|
|
24
|
+
import {Image} from "react-native";
|
|
25
|
+
import {useState} from "react";
|
|
26
|
+
|
|
27
|
+
const MASCOTS = [
|
|
28
|
+
require("../assets/mascot-1.png"),
|
|
29
|
+
require("../assets/mascot-2.png"),
|
|
30
|
+
require("../assets/mascot-3.png"),
|
|
31
|
+
require("../assets/mascot-4.png"),
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
const ChatScreen: React.FC = (): React.ReactElement => {
|
|
35
|
+
// Select once per screen mount; ordinary re-renders keep the same character.
|
|
36
|
+
const [mascot] = useState(() => MASCOTS[Math.floor(Math.random() * MASCOTS.length)]);
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<GPTChat
|
|
40
|
+
currentMessages={currentMessages}
|
|
41
|
+
histories={histories}
|
|
42
|
+
mascot={
|
|
43
|
+
<Box alignItems="center" gap={3}>
|
|
44
|
+
<Image
|
|
45
|
+
accessibilityLabel="App mascot"
|
|
46
|
+
resizeMode="cover"
|
|
47
|
+
source={mascot}
|
|
48
|
+
style={{borderRadius: 112, height: 224, width: 224}}
|
|
49
|
+
/>
|
|
50
|
+
<Heading align="center" size="md">Ask anything.</Heading>
|
|
51
|
+
</Box>
|
|
52
|
+
}
|
|
53
|
+
onCreateHistory={handleCreateHistory}
|
|
54
|
+
onDeleteHistory={handleDeleteHistory}
|
|
55
|
+
onSelectHistory={handleSelectHistory}
|
|
56
|
+
onSubmit={handleSubmit}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
For a single fixed mascot, pass one asset directly instead of selecting from an array.
|
|
63
|
+
|
|
64
|
+
### 2. Pass it to `GPTChat`
|
|
65
|
+
|
|
66
|
+
```tsx
|
|
67
|
+
<GPTChat
|
|
68
|
+
currentMessages={currentMessages}
|
|
69
|
+
histories={histories}
|
|
70
|
+
mascot={ChatMascot}
|
|
71
|
+
onCreateHistory={handleCreateHistory}
|
|
72
|
+
onDeleteHistory={handleDeleteHistory}
|
|
73
|
+
onSelectHistory={handleSelectHistory}
|
|
74
|
+
onSubmit={handleSubmit}
|
|
75
|
+
suggestedPrompts={["What can you help with?"]}
|
|
76
|
+
/>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 3. Confirm empty vs thread
|
|
80
|
+
|
|
81
|
+
Open a new chat: the mascot is visible. After the first user or assistant message, it is gone.
|
|
82
|
+
|
|
83
|
+
## Example
|
|
84
|
+
|
|
85
|
+
The example app bundles four consumer-owned plant robots in
|
|
86
|
+
`example-frontend/assets/gptMascots/` and selects one on each AI screen mount. The
|
|
87
|
+
demo story `GPTChat` → `Mascot` shows the slot without a backend.
|
|
88
|
+
|
|
89
|
+
## Related
|
|
90
|
+
|
|
91
|
+
- [`GPTChat` props](../reference/ui.md#gptchat)
|
|
92
|
+
- Demo: `http://localhost:8085/dev/GPTChat?story=Mascot`
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# Add organizations to a Terreno app
|
|
2
|
+
|
|
3
|
+
Enable organizations only when the app is multi-tenant. Existing single-tenant
|
|
4
|
+
apps can omit every option in this guide.
|
|
5
|
+
|
|
6
|
+
## 1. Enable organization RBAC
|
|
7
|
+
|
|
8
|
+
``````typescript
|
|
9
|
+
import {createAccess, terrenoStatements} from "@terreno/api";
|
|
10
|
+
|
|
11
|
+
export const access = createAccess({
|
|
12
|
+
connection: mongoose.connection,
|
|
13
|
+
organizations: true,
|
|
14
|
+
statements: terrenoStatements,
|
|
15
|
+
userModel: User,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
await access.roles.seedDefaults();
|
|
19
|
+
``````
|
|
20
|
+
|
|
21
|
+
This adds Membership-based `org-admin` grants and seeds the locked `operator`
|
|
22
|
+
role. It does not change apps that omit `organizations`.
|
|
23
|
+
|
|
24
|
+
## 2. Mount organization routes
|
|
25
|
+
|
|
26
|
+
``````typescript
|
|
27
|
+
const app = new TerrenoApp({
|
|
28
|
+
accessControl: access,
|
|
29
|
+
organizations: true,
|
|
30
|
+
userModel: User,
|
|
31
|
+
});
|
|
32
|
+
``````
|
|
33
|
+
|
|
34
|
+
This mounts `OrgsApp` at `/orgs`. To configure audit handling or a different
|
|
35
|
+
path, pass an options object:
|
|
36
|
+
|
|
37
|
+
``````typescript
|
|
38
|
+
organizations: {
|
|
39
|
+
basePath: "/organizations",
|
|
40
|
+
onOrgAudit: recordOrganizationAudit,
|
|
41
|
+
settingsSchema: organizationSettingsSchema,
|
|
42
|
+
}
|
|
43
|
+
``````
|
|
44
|
+
|
|
45
|
+
## 3. Type organization settings
|
|
46
|
+
|
|
47
|
+
Keep native `Organization` fields unchanged. Put app fields on
|
|
48
|
+
`Organization.settings` with a nested Mongoose schema and a TypeScript
|
|
49
|
+
interface.
|
|
50
|
+
|
|
51
|
+
1. Declare the settings shape (optional module augmentation):
|
|
52
|
+
|
|
53
|
+
``````typescript
|
|
54
|
+
// src/types/models/organizationSettingsTypes.ts
|
|
55
|
+
export interface ExampleOrganizationSettings {
|
|
56
|
+
timezone?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
declare module "@terreno/api" {
|
|
60
|
+
interface OrganizationSettings extends ExampleOrganizationSettings {}
|
|
61
|
+
}
|
|
62
|
+
``````
|
|
63
|
+
|
|
64
|
+
2. Build the nested schema. Use optional fields or defaults so existing
|
|
65
|
+
organizations still validate:
|
|
66
|
+
|
|
67
|
+
``````typescript
|
|
68
|
+
// src/models/organizationSettings.ts
|
|
69
|
+
import {createOrganizationSettingsSchema} from "@terreno/api";
|
|
70
|
+
import type {ExampleOrganizationSettings} from "../types/models/organizationSettingsTypes";
|
|
71
|
+
|
|
72
|
+
export const organizationSettingsSchema = createOrganizationSettingsSchema<ExampleOrganizationSettings>({
|
|
73
|
+
timezone: {
|
|
74
|
+
description: "IANA timezone used for organization-local dates",
|
|
75
|
+
type: String,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
``````
|
|
79
|
+
|
|
80
|
+
3. Register before you create or patch organizations (startup and seeds):
|
|
81
|
+
|
|
82
|
+
``````typescript
|
|
83
|
+
import {registerOrganizationSettings} from "@terreno/api";
|
|
84
|
+
import {organizationSettingsSchema} from "./models/organizationSettings";
|
|
85
|
+
|
|
86
|
+
registerOrganizationSettings(organizationSettingsSchema);
|
|
87
|
+
``````
|
|
88
|
+
|
|
89
|
+
Or pass the same schema on `TerrenoApp` / `OrgsApp`:
|
|
90
|
+
|
|
91
|
+
``````typescript
|
|
92
|
+
new TerrenoApp({
|
|
93
|
+
accessControl: access,
|
|
94
|
+
organizations: {settingsSchema: organizationSettingsSchema},
|
|
95
|
+
userModel: User,
|
|
96
|
+
});
|
|
97
|
+
``````
|
|
98
|
+
|
|
99
|
+
Read typed settings with `organizationSettingsOf(organization)` or
|
|
100
|
+
`organizationSettingsOf<ExampleOrganizationSettings>(organization)`.
|
|
101
|
+
|
|
102
|
+
Without a registered schema, `settings` stays Mixed and accepts any object.
|
|
103
|
+
Unknown keys and wrong types fail with **400** `Organization settings are invalid`
|
|
104
|
+
once a schema is registered.
|
|
105
|
+
|
|
106
|
+
## 4. Scope tenant models
|
|
107
|
+
|
|
108
|
+
Add `orgScopedPlugin` to models that use ObjectId organization keys. Put
|
|
109
|
+
`orgContextMiddleware({required: true})` before their routes, use
|
|
110
|
+
`OrgQueryFilter` for lists, and overwrite client organization ids during
|
|
111
|
+
create:
|
|
112
|
+
|
|
113
|
+
``````typescript
|
|
114
|
+
import {
|
|
115
|
+
authenticateMiddleware,
|
|
116
|
+
getOrgContext,
|
|
117
|
+
modelRouter,
|
|
118
|
+
OrgQueryFilter,
|
|
119
|
+
orgContextMiddleware,
|
|
120
|
+
orgScopedPlugin,
|
|
121
|
+
Permissions,
|
|
122
|
+
type TerrenoPlugin,
|
|
123
|
+
} from "@terreno/api";
|
|
124
|
+
import type express from "express";
|
|
125
|
+
|
|
126
|
+
schema.plugin(orgScopedPlugin);
|
|
127
|
+
|
|
128
|
+
const projectOrgContextPlugin: TerrenoPlugin = {
|
|
129
|
+
register(expressApp: express.Application): void {
|
|
130
|
+
expressApp.use(
|
|
131
|
+
"/projects",
|
|
132
|
+
authenticateMiddleware(),
|
|
133
|
+
orgContextMiddleware({required: true})
|
|
134
|
+
);
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const projectRouter = modelRouter("/projects", Project, {
|
|
139
|
+
permissions: {
|
|
140
|
+
create: [Permissions.IsAuthenticated, Permissions.IsOrganizationMember],
|
|
141
|
+
delete: [Permissions.IsAuthenticated, Permissions.IsOrganizationMember],
|
|
142
|
+
list: [Permissions.IsAuthenticated],
|
|
143
|
+
read: [Permissions.IsAuthenticated, Permissions.IsOrganizationMember],
|
|
144
|
+
update: [Permissions.IsAuthenticated, Permissions.IsOrganizationMember],
|
|
145
|
+
},
|
|
146
|
+
preCreate: (body, _req) => ({
|
|
147
|
+
...body,
|
|
148
|
+
organizationId: getOrgContext()?.organization?._id,
|
|
149
|
+
}),
|
|
150
|
+
queryFilter: OrgQueryFilter,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
app.register(projectOrgContextPlugin).register(projectRouter);
|
|
154
|
+
``````
|
|
155
|
+
|
|
156
|
+
For admin CRUD, use `new AdminApp({accessControl: access, organizations:
|
|
157
|
+
true})`. Models with an `organizationId` schema path are scoped automatically,
|
|
158
|
+
except `AuditEvent` (the platform audit log lists without a selected org).
|
|
159
|
+
Admin-window `POST /sync/mutate` and socket `sync:mutate` use the same membership
|
|
160
|
+
check as REST for update and delete when `X-Organization-Id` (HTTP) or
|
|
161
|
+
`organizationId` (socket payload) is set. Admin `createSyncDb({organizationIdProvider})`
|
|
162
|
+
sends that id on every mutate.
|
|
163
|
+
|
|
164
|
+
`orgScopedPlugin` also makes `organizationId` immutable after the document is
|
|
165
|
+
created. Client PATCH bodies, admin writes, sync updates, and direct Mongoose
|
|
166
|
+
saves that change `organizationId` return HTTP 400 with
|
|
167
|
+
`organizationId cannot be changed`.
|
|
168
|
+
|
|
169
|
+
## 5. Send organization context
|
|
170
|
+
|
|
171
|
+
Send `X-Organization-Id` on tenant-scoped requests. `org-admin` callers with
|
|
172
|
+
exactly one administered organization may omit it; Terreno infers that org.
|
|
173
|
+
Operators and superadmins must select an org.
|
|
174
|
+
|
|
175
|
+
Frontend admin hosts use `OrgContextProvider` + `OrgSwitcher`.
|
|
176
|
+
`useAdminApi` adds the selected organization header automatically.
|
|
177
|
+
|
|
178
|
+
Disabled organizations are excluded from `/orgs/mine` and cannot be selected as
|
|
179
|
+
tenant context. Operators re-enable them from the organization directory UI or
|
|
180
|
+
with `PATCH /orgs/:id` and `disabled: false` (see
|
|
181
|
+
[How organization tenancy works](../explanation/organizations.md#disabled-organizations)).
|
|
182
|
+
|
|
183
|
+
Creating or renaming an organization to a name that generates an existing slug,
|
|
184
|
+
including a slug still held by a **soft-deleted** organization, returns **409**
|
|
185
|
+
`Organization name already in use`. `OrgSwitcher` hides itself
|
|
186
|
+
when `/orgs/mine` returns **403** for callers without org-admin memberships.
|
|
187
|
+
|
|
188
|
+
## Example app accounts
|
|
189
|
+
|
|
190
|
+
After `bun run backend:seed`, all accounts use password `testpassword123`:
|
|
191
|
+
|
|
192
|
+
| Account | Access |
|
|
193
|
+
| --- | --- |
|
|
194
|
+
| `operator@example.com` | Platform organization directory; may enter either org |
|
|
195
|
+
| `orgadmin-alpha@example.com` | Org admin for Alpha Workspace |
|
|
196
|
+
| `orgadmin-beta@example.com` | Org admin for Beta Workspace |
|
|
197
|
+
| `test@example.com` | Member of Alpha Workspace |
|
|
198
|
+
| `admin@example.com` / `superadmin@example.com` | Existing example superadmin accounts |
|
|
199
|
+
|
|
200
|
+
The seed creates Alpha Workspace and Beta Workspace plus two projects in Alpha
|
|
201
|
+
Workspace. Membership rows, not fields on User, are the source of tenant access.
|
|
202
|
+
In-process Better Auth seeding sets `disableRateLimit: true` so production
|
|
203
|
+
preview smoke tests can create all six accounts without hitting Better Auth's
|
|
204
|
+
sign-up limiter.
|
|
@@ -38,7 +38,15 @@ Common fields:
|
|
|
38
38
|
- `searchFields`: string fields matched by the list search box (`q`) as a
|
|
39
39
|
case-insensitive partial (`$regex`) query.
|
|
40
40
|
- `sortableFields`: the only columns with sorting enabled.
|
|
41
|
-
- `filters`: typed
|
|
41
|
+
- `filters`: typed list filters rendered on `DataTable` column headers (web) or the
|
|
42
|
+
mobile **Filters** sheet. `text` filters are case-insensitive contains (`$regex`);
|
|
43
|
+
`choice` filters support multi-select (`$in`); `ref` stays a single ObjectId. The
|
|
44
|
+
backend accepts only escaped literal regex patterns and rejects extra nested operators.
|
|
45
|
+
Optional choice fields (Mongoose path not `required`) automatically expose an **Empty**
|
|
46
|
+
filter option; set `allowEmpty: false` on the filter config to hide it, or
|
|
47
|
+
`allowEmpty: true` on required fields when you intentionally want unset matching.
|
|
48
|
+
Empty filters can be combined with toolbar search or concrete choices. The wire
|
|
49
|
+
sentinel is consumed before Mongo receives the query.
|
|
42
50
|
- `fieldsets`: grouped form sections.
|
|
43
51
|
- `readonlyFields` / `hiddenFields`: display-only or omitted form fields.
|
|
44
52
|
- `adminPermissions`: optional admin-specific permission methods.
|
|
@@ -3,12 +3,15 @@
|
|
|
3
3
|
Register first-party plugins on the same `TerrenoApp` as `AdminApp`:
|
|
4
4
|
|
|
5
5
|
```ts
|
|
6
|
+
import {JobsApp} from "@terreno/jobs";
|
|
7
|
+
|
|
6
8
|
const app = new TerrenoApp({userModel: User})
|
|
7
9
|
.register(new FeatureFlagsApp())
|
|
8
10
|
.register(new ConsentApp({auditTrail: true}))
|
|
9
11
|
.register(new DocumentStorageApp({bucketName: process.env.GCS_BUCKET ?? ""}))
|
|
10
12
|
.register(new AIAdminApp())
|
|
11
|
-
.register(new
|
|
13
|
+
.register(new JobsApp({accessControl})) // optional — needs admin:access + admin:jobs
|
|
14
|
+
.register(new AdminApp({home: {slots: {main: ["modelStats", "jobs"]}}}));
|
|
12
15
|
```
|
|
13
16
|
|
|
14
17
|
`AdminApp` aggregates each plugin's `adminContribution()`:
|
|
@@ -17,6 +20,9 @@ const app = new TerrenoApp({userModel: User})
|
|
|
17
20
|
- `ConsentApp`: ConsentForm and ConsentResponse models plus consent field-widget IDs.
|
|
18
21
|
- `DocumentStorageApp`: `documents` custom screen.
|
|
19
22
|
- `AIAdminApp`: `ai-requests` custom screen and explorer API.
|
|
23
|
+
- `JobsApp`: `jobs` custom screen and home widget (`dead` / `running` counts). With
|
|
24
|
+
`accessControl`, requires **both** `admin:access` and `admin:jobs`; without it, legacy
|
|
25
|
+
`user.admin`. See [Durable background jobs](background-jobs.md).
|
|
20
26
|
|
|
21
27
|
Wrap frontend admin routes in `AdminProvider`. First-party widgets are already in the built-in
|
|
22
28
|
registry, so no consumer-side switch statement or custom-screen map is required.
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Enable the framework audit log
|
|
2
|
+
|
|
3
|
+
Register `AuditApp` and set `audit: true` on routers you want logged. Successful HTTP mutations write append-only `AuditEvent` rows. Default retention is forever.
|
|
4
|
+
|
|
5
|
+
## Steps
|
|
6
|
+
|
|
7
|
+
### 1. Register the plugin
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
import {AuditApp, TerrenoApp} from "@terreno/api";
|
|
11
|
+
|
|
12
|
+
new TerrenoApp({userModel: User}).register(new AuditApp()).start();
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Importing `@terreno/api` does not register `AuditEvent`. HTTP list/read is admin-only at `GET /audit-events` and `GET /admin/audit-events`. Create/update/delete over HTTP are disabled (405), including the admin remount.
|
|
16
|
+
|
|
17
|
+
### 2. Opt in per router
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
modelRouter("/todos", Todo, {
|
|
21
|
+
audit: true, // or {redact: ["ssn"]}
|
|
22
|
+
permissions: {
|
|
23
|
+
/* ... */
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`audit: true` without `AuditApp` still returns 2xx and logs an error once per process.
|
|
29
|
+
|
|
30
|
+
## Operations
|
|
31
|
+
|
|
32
|
+
| HTTP | `verb` | `operation` | Diff |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| POST `/` | `created` | `create` | `after` only |
|
|
35
|
+
| PATCH `/:id` | `updated` | `update` | Changed fields in `before` / `after` |
|
|
36
|
+
| DELETE `/:id` | `deleted` | `delete` | `before` only |
|
|
37
|
+
| POST `/:id/:field` | `updated` | `arrayPush` | Changed fields; `recordId` set |
|
|
38
|
+
| PATCH `/:id/:field/:itemId` | `updated` | `arrayUpdate` | Changed fields; `recordId` set |
|
|
39
|
+
| DELETE `/:id/:field/:itemId` | `updated` | `arrayRemove` | Changed fields; `recordId` set |
|
|
40
|
+
|
|
41
|
+
`source` is `modelRouter` for these writes. Secrets (`password`, `hash`, `salt`, `token`,
|
|
42
|
+
`secret`, `refreshToken`) are omitted at every object and array depth, including compound
|
|
43
|
+
keys such as `tokenHash`. Extra `redact` names merge with that list as exact field names.
|
|
44
|
+
|
|
45
|
+
### 3. Admin mutations
|
|
46
|
+
|
|
47
|
+
When `AuditApp` is registered, AdminApp writes the same `AuditEvent` collection with
|
|
48
|
+
`source: "admin"` after successful admin POST/PATCH/DELETE. You do not need `onAdminAudit` for
|
|
49
|
+
that. `onAdminAudit` remains an extra sink if the app still wants a second destination.
|
|
50
|
+
Admin diffs also omit each model's `hiddenFields` and `excludeFields`.
|
|
51
|
+
|
|
52
|
+
RBAC mutations fan into the same collection when `createAccess({auditSink: persistRbacAuditToAuditEvent})` is set. `source` is `rbac`.
|
|
53
|
+
|
|
54
|
+
`organizationId` is copied onto the event when `req.organization` is set (`id` or `_id`), otherwise from the request org AsyncLocalStorage, then `X-Organization-Id`, then the mutated document's `organizationId` string. Socket admin-window mutates attach `organizationId` on the payload so non-tenant models still land in the selected organization's audit log. The field is omitted when none of those exist. Admin list/read for `AuditEvent` is not organization-scoped, so unscoped rows stay visible when no organization is selected.
|
|
55
|
+
|
|
56
|
+
`actorId` comes from `req.user.id` (or `_id`). Only a 24-hex Mongo ObjectId is stored;
|
|
57
|
+
mongoose's looser `isValidObjectId` (12-character strings) is not used. Any other id
|
|
58
|
+
shape still writes the event with `actorId` omitted and a warning logged.
|
|
59
|
+
|
|
60
|
+
Update and delete snapshot `before` only when `audit` is on. If that `toJSON` throws, the
|
|
61
|
+
mutation still completes and the event is recorded without a `before` snapshot.
|
|
62
|
+
|
|
63
|
+
### 4. Fire-and-forget persist
|
|
64
|
+
|
|
65
|
+
HTTP handlers do not await Mongo. Diffs still run before the response so `before` / `after` are captured. Failures log; the mutation stays 2xx.
|
|
66
|
+
|
|
67
|
+
To persist completely off the API process, set Cloud Tasks env (`GCP_PROJECT`, `GCP_LOCATION`, `GCP_TASKS_AUDIT_QUEUE`, `AUDIT_TASKS_URL`, `AUDIT_TASKS_SECRET`) and `bun add @google-cloud/tasks`. `AuditApp` then enqueues the write and handles `POST /internal/audit-events` with header `X-Terreno-Audit-Secret`. Or pass `enqueue` yourself:
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
new AuditApp({
|
|
71
|
+
enqueue: async (write) => {
|
|
72
|
+
await myQueue.push(write);
|
|
73
|
+
},
|
|
74
|
+
processQueuePath: "/internal/audit-events",
|
|
75
|
+
processQueueSecret: process.env.AUDIT_TASKS_SECRET,
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
Authenticated malformed queue payloads are acknowledged without persistence. They cannot
|
|
80
|
+
become valid on retry, and returning a non-2xx response would make Cloud Tasks retry the
|
|
81
|
+
same poison task indefinitely.
|
|
82
|
+
|
|
83
|
+
### 5. Retention
|
|
84
|
+
|
|
85
|
+
Omit `retentionDays` or set `0` to keep events forever (no TTL index). `new AuditApp({retentionDays: 90})` creates a Mongo TTL index on `created` with `expireAfterSeconds = 90 * 86400`, replacing the default `{created: 1}` field index so Mongo does not reject a duplicate key pattern. Mongo expires documents in the background; lowering or removing TTL later requires dropping that index yourself (`db.auditevents.dropIndex(...)`) — Mongoose will not remove it.
|