@terreno/mcp 57.19.2 → 57.20.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.
Files changed (197) hide show
  1. package/CHANGELOG.md +393 -0
  2. package/README.md +32 -269
  3. package/dist/bootstrap.d.ts +12 -0
  4. package/dist/bootstrap.d.ts.map +1 -1
  5. package/dist/bootstrap.js +77 -122
  6. package/dist/bootstrap.js.map +1 -1
  7. package/dist/docs/guidelines/ui/guidelines/core.md +1 -1
  8. package/dist/docs/resources/overview.md +4 -2
  9. package/dist/docs/resources/patterns.md +237 -176
  10. package/dist/docs/resources/ui.md +48 -17
  11. package/dist/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +0 -26
  12. package/dist/docs/templates/bootstrap/frontend/app/_layout.template.tsx +3 -2
  13. package/dist/docs/templates/bootstrap/frontend/app/login.template.tsx +1 -1
  14. package/dist/docs/upgrades/57.20.0.md +238 -0
  15. package/dist/docs/versioned/next/approval-rubric.md +30 -12
  16. package/dist/docs/versioned/next/explanation/README.md +5 -0
  17. package/dist/docs/versioned/next/explanation/account-linking.md +169 -0
  18. package/dist/docs/versioned/next/explanation/admin-interface.md +1 -1
  19. package/dist/docs/versioned/next/explanation/agent-guidelines.md +173 -0
  20. package/dist/docs/versioned/next/explanation/ai-development-loop.md +196 -0
  21. package/dist/docs/versioned/next/explanation/authentication.md +45 -1
  22. package/dist/docs/versioned/next/explanation/consent-forms.md +105 -0
  23. package/dist/docs/versioned/next/explanation/example-coverage.md +1 -0
  24. package/dist/docs/versioned/next/explanation/local-first-data.md +1 -1
  25. package/dist/docs/versioned/next/explanation/organizations.md +37 -0
  26. package/dist/docs/versioned/next/explanation/planning-plugins.md +4 -0
  27. package/dist/docs/versioned/next/explanation/responsive-design.md +143 -0
  28. package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +11 -0
  29. package/dist/docs/versioned/next/explanation/schema-metadata.md +5 -0
  30. package/dist/docs/versioned/next/how-to/README.md +7 -2
  31. package/dist/docs/versioned/next/how-to/audit-log.md +22 -3
  32. package/dist/docs/versioned/next/how-to/automate-terreno-upgrades.md +169 -0
  33. package/dist/docs/versioned/next/how-to/build-list-detail-screens.md +160 -0
  34. package/dist/docs/versioned/next/how-to/circleci.md +15 -5
  35. package/dist/docs/versioned/next/how-to/configure-better-auth.md +673 -53
  36. package/dist/docs/versioned/next/how-to/create-a-terreno-app.md +8 -5
  37. package/dist/docs/versioned/next/how-to/debug-with-mcp.md +393 -23
  38. package/dist/docs/versioned/next/how-to/enable-mcp-oauth.md +54 -3
  39. package/dist/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +9 -0
  40. package/dist/docs/versioned/next/how-to/password-reset.md +1 -1
  41. package/dist/docs/versioned/next/how-to/recover-locked-out-user.md +112 -0
  42. package/dist/docs/versioned/next/how-to/run-tests-locally.md +37 -8
  43. package/dist/docs/versioned/next/how-to/set-up-social-login.md +424 -0
  44. package/dist/docs/versioned/next/how-to/set-up-terreno-mcp.md +241 -36
  45. package/dist/docs/versioned/next/how-to/sign-consent-on-behalf.md +139 -0
  46. package/dist/docs/versioned/next/how-to/upgrade-terreno.md +4 -0
  47. package/dist/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +47 -32
  48. package/dist/docs/versioned/next/implementationPlans/auth-experience.md +952 -0
  49. package/dist/docs/versioned/next/implementationPlans/consent-forms.md +8 -0
  50. package/dist/docs/versioned/next/implementationPlans/list-detail-layout.md +297 -0
  51. package/dist/docs/versioned/next/implementationPlans/login-redesign/HANDOFF.md +211 -0
  52. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/AnotherWay.html +121 -0
  53. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/BackupCode.html +72 -0
  54. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/Cancelled.html +87 -0
  55. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/ContinueEmail.html +64 -0
  56. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/FirstVisit.html +81 -0
  57. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/Main.html +88 -0
  58. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/ManageAccounts.html +77 -0
  59. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/NotLinked.html +92 -0
  60. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/SigningIn.html +60 -0
  61. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactor.html +76 -0
  62. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactorError.html +81 -0
  63. package/dist/docs/versioned/next/implementationPlans/login-redesign/boards/WrongPassword.html +69 -0
  64. package/dist/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -1
  65. package/dist/docs/versioned/next/plans/2026-10-23-provisional-consent.md +297 -0
  66. package/dist/docs/versioned/next/plans/2026-10-29-fix-mcp-oauth-native-and-machine.md +68 -0
  67. package/dist/docs/versioned/next/reference/README.md +1 -0
  68. package/dist/docs/versioned/next/reference/account-security.md +700 -0
  69. package/dist/docs/versioned/next/reference/admin-backend.md +3 -1
  70. package/dist/docs/versioned/next/reference/admin-frontend.md +225 -1
  71. package/dist/docs/versioned/next/reference/api.md +461 -35
  72. package/dist/docs/versioned/next/reference/cli.md +1 -1
  73. package/dist/docs/versioned/next/reference/comms.md +8 -0
  74. package/dist/docs/versioned/next/reference/create-terreno-app.md +3 -1
  75. package/dist/docs/versioned/next/reference/environment-variables.md +9 -3
  76. package/dist/docs/versioned/next/reference/legacy/rtk.md +19 -3
  77. package/dist/docs/versioned/next/reference/lifecycle-plugin.md +6 -0
  78. package/dist/docs/versioned/next/reference/mcp-server.md +1158 -441
  79. package/dist/docs/versioned/next/reference/syncdb.md +8 -1
  80. package/dist/docs/versioned/next/reference/ui.md +1271 -27
  81. package/dist/docs/versioned/next/tasks/ai-dev-loop-boost.md +28 -16
  82. package/dist/docs/versioned/next/tutorials/README.md +4 -0
  83. package/dist/httpApp.d.ts +14 -0
  84. package/dist/httpApp.d.ts.map +1 -0
  85. package/dist/httpApp.js +99 -0
  86. package/dist/httpApp.js.map +1 -0
  87. package/dist/index.js +8 -90
  88. package/dist/index.js.map +1 -1
  89. package/dist/local/localTools.d.ts.map +1 -1
  90. package/dist/local/localTools.js +44 -14
  91. package/dist/local/localTools.js.map +1 -1
  92. package/dist/local/metro/metroDevSession.d.ts +18 -0
  93. package/dist/local/metro/metroDevSession.d.ts.map +1 -1
  94. package/dist/local/metro/metroDevSession.js +42 -3
  95. package/dist/local/metro/metroDevSession.js.map +1 -1
  96. package/dist/local/tools/databaseQuery.d.ts.map +1 -1
  97. package/dist/local/tools/databaseQuery.js +3 -13
  98. package/dist/local/tools/databaseQuery.js.map +1 -1
  99. package/dist/local/tools/databaseSchema.d.ts.map +1 -1
  100. package/dist/local/tools/databaseSchema.js +3 -13
  101. package/dist/local/tools/databaseSchema.js.map +1 -1
  102. package/dist/local/tools/localMongo.d.ts +10 -0
  103. package/dist/local/tools/localMongo.d.ts.map +1 -0
  104. package/dist/local/tools/localMongo.js +30 -0
  105. package/dist/local/tools/localMongo.js.map +1 -0
  106. package/dist/local/tools/runtime.d.ts +18 -2
  107. package/dist/local/tools/runtime.d.ts.map +1 -1
  108. package/dist/local/tools/runtime.js +75 -20
  109. package/dist/local/tools/runtime.js.map +1 -1
  110. package/dist/local/tools/syncDb.d.ts +11 -0
  111. package/dist/local/tools/syncDb.d.ts.map +1 -1
  112. package/dist/local/tools/syncDb.js +15 -3
  113. package/dist/local/tools/syncDb.js.map +1 -1
  114. package/dist/rateLimit.d.ts +35 -0
  115. package/dist/rateLimit.d.ts.map +1 -0
  116. package/dist/rateLimit.js +112 -0
  117. package/dist/rateLimit.js.map +1 -0
  118. package/dist/tools.d.ts.map +1 -1
  119. package/dist/tools.js +42 -60
  120. package/dist/tools.js.map +1 -1
  121. package/package.json +4 -3
  122. package/src/docs/guidelines/ui/guidelines/core.md +1 -1
  123. package/src/docs/resources/overview.md +4 -2
  124. package/src/docs/resources/patterns.md +237 -176
  125. package/src/docs/resources/ui.md +48 -17
  126. package/src/docs/templates/bootstrap/frontend/app/(tabs)/profile.template.tsx +0 -26
  127. package/src/docs/templates/bootstrap/frontend/app/_layout.template.tsx +3 -2
  128. package/src/docs/templates/bootstrap/frontend/app/login.template.tsx +1 -1
  129. package/src/docs/upgrades/57.20.0.md +238 -0
  130. package/src/docs/versioned/next/approval-rubric.md +30 -12
  131. package/src/docs/versioned/next/explanation/README.md +5 -0
  132. package/src/docs/versioned/next/explanation/account-linking.md +169 -0
  133. package/src/docs/versioned/next/explanation/admin-interface.md +1 -1
  134. package/src/docs/versioned/next/explanation/agent-guidelines.md +173 -0
  135. package/src/docs/versioned/next/explanation/ai-development-loop.md +196 -0
  136. package/src/docs/versioned/next/explanation/authentication.md +45 -1
  137. package/src/docs/versioned/next/explanation/consent-forms.md +105 -0
  138. package/src/docs/versioned/next/explanation/example-coverage.md +1 -0
  139. package/src/docs/versioned/next/explanation/local-first-data.md +1 -1
  140. package/src/docs/versioned/next/explanation/organizations.md +37 -0
  141. package/src/docs/versioned/next/explanation/planning-plugins.md +4 -0
  142. package/src/docs/versioned/next/explanation/responsive-design.md +143 -0
  143. package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +11 -0
  144. package/src/docs/versioned/next/explanation/schema-metadata.md +5 -0
  145. package/src/docs/versioned/next/how-to/README.md +7 -2
  146. package/src/docs/versioned/next/how-to/audit-log.md +22 -3
  147. package/src/docs/versioned/next/how-to/automate-terreno-upgrades.md +169 -0
  148. package/src/docs/versioned/next/how-to/build-list-detail-screens.md +160 -0
  149. package/src/docs/versioned/next/how-to/circleci.md +15 -5
  150. package/src/docs/versioned/next/how-to/configure-better-auth.md +673 -53
  151. package/src/docs/versioned/next/how-to/create-a-terreno-app.md +8 -5
  152. package/src/docs/versioned/next/how-to/debug-with-mcp.md +393 -23
  153. package/src/docs/versioned/next/how-to/enable-mcp-oauth.md +54 -3
  154. package/src/docs/versioned/next/how-to/migrate-rtk-to-syncdb.md +9 -0
  155. package/src/docs/versioned/next/how-to/password-reset.md +1 -1
  156. package/src/docs/versioned/next/how-to/recover-locked-out-user.md +112 -0
  157. package/src/docs/versioned/next/how-to/run-tests-locally.md +37 -8
  158. package/src/docs/versioned/next/how-to/set-up-social-login.md +424 -0
  159. package/src/docs/versioned/next/how-to/set-up-terreno-mcp.md +241 -36
  160. package/src/docs/versioned/next/how-to/sign-consent-on-behalf.md +139 -0
  161. package/src/docs/versioned/next/how-to/upgrade-terreno.md +4 -0
  162. package/src/docs/versioned/next/implementationPlans/ai-dev-loop-boost.md +47 -32
  163. package/src/docs/versioned/next/implementationPlans/auth-experience.md +952 -0
  164. package/src/docs/versioned/next/implementationPlans/consent-forms.md +8 -0
  165. package/src/docs/versioned/next/implementationPlans/list-detail-layout.md +297 -0
  166. package/src/docs/versioned/next/implementationPlans/login-redesign/HANDOFF.md +211 -0
  167. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/AnotherWay.html +121 -0
  168. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/BackupCode.html +72 -0
  169. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/Cancelled.html +87 -0
  170. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/ContinueEmail.html +64 -0
  171. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/FirstVisit.html +81 -0
  172. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/Main.html +88 -0
  173. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/ManageAccounts.html +77 -0
  174. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/NotLinked.html +92 -0
  175. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/SigningIn.html +60 -0
  176. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactor.html +76 -0
  177. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/TwoFactorError.html +81 -0
  178. package/src/docs/versioned/next/implementationPlans/login-redesign/boards/WrongPassword.html +69 -0
  179. package/src/docs/versioned/next/implementationPlans/mcp-boost-parity.md +1 -1
  180. package/src/docs/versioned/next/plans/2026-10-23-provisional-consent.md +297 -0
  181. package/src/docs/versioned/next/plans/2026-10-29-fix-mcp-oauth-native-and-machine.md +68 -0
  182. package/src/docs/versioned/next/reference/README.md +1 -0
  183. package/src/docs/versioned/next/reference/account-security.md +700 -0
  184. package/src/docs/versioned/next/reference/admin-backend.md +3 -1
  185. package/src/docs/versioned/next/reference/admin-frontend.md +225 -1
  186. package/src/docs/versioned/next/reference/api.md +461 -35
  187. package/src/docs/versioned/next/reference/cli.md +1 -1
  188. package/src/docs/versioned/next/reference/comms.md +8 -0
  189. package/src/docs/versioned/next/reference/create-terreno-app.md +3 -1
  190. package/src/docs/versioned/next/reference/environment-variables.md +9 -3
  191. package/src/docs/versioned/next/reference/legacy/rtk.md +19 -3
  192. package/src/docs/versioned/next/reference/lifecycle-plugin.md +6 -0
  193. package/src/docs/versioned/next/reference/mcp-server.md +1158 -441
  194. package/src/docs/versioned/next/reference/syncdb.md +8 -1
  195. package/src/docs/versioned/next/reference/ui.md +1271 -27
  196. package/src/docs/versioned/next/tasks/ai-dev-loop-boost.md +28 -16
  197. package/src/docs/versioned/next/tutorials/README.md +4 -0
@@ -1 +1 @@
1
- {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,gCAAgC,EAChC,8BAA8B,EAE9B,4BAA4B,EAC5B,iCAAiC,GAClC,MAAM,wBAAwB,CAAC;AAQhC,MAAM,CAAC,MAAM,cAAc,GAAW;IACpC;QACE,WAAW,EACT,wMAAwM;QAC1M,WAAW,EAAE;YACX,UAAU,EAAE;gBACV,cAAc,EAAE;oBACd,WAAW,EAAE,mEAAmE;oBAChF,IAAI,EAAE,QAAQ;iBACf;gBACD,OAAO,EAAE;oBACP,WAAW,EACT,6FAA6F;oBAC/F,IAAI,EAAE,QAAQ;iBACf;gBACD,WAAW,EAAE;oBACX,WAAW,EAAE,2CAA2C;oBACxD,IAAI,EAAE,QAAQ;iBACf;gBACD,QAAQ,EAAE;oBACR,WAAW,EACT,uQAAuQ;oBACzQ,KAAK,EAAE,EAAC,IAAI,EAAE,QAAQ,EAAC;oBACvB,IAAI,EAAE,OAAO;iBACd;aACF;YACD,QAAQ,EAAE,CAAC,SAAS,EAAE,gBAAgB,CAAC;YACvC,IAAI,EAAE,QAAQ;SACf;QACD,IAAI,EAAE,4BAA4B;KACnC;CACF,CAAC;AAMF,MAAM,sBAAsB,GAAG,GAAW,EAAE;IAC1C,OAAO,IAAI,CAAC,SAAS,CACnB;QACE,OAAO,EACL,0FAA0F;QAC5F,QAAQ,EAAE,CAAC,GAAG,CAAC;QACf,MAAM,EAAE,IAAI;QACZ,QAAQ,EAAE,CAAC,OAAO,CAAC;QACnB,OAAO,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAE,SAAS,CAAC;QACxD,OAAO,EAAE,KAAK;KACf,EACD,IAAI,EACJ,CAAC,CACF,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,6BAA6B,GAAG;;;;;;;;;;;;;CAarC,CAAC;AAEF,MAAM,gCAAgC,GAAG,CACvC,IAAmB,EACnB,cAA6C,EACrC,EAAE;IACV,MAAM,EAAC,cAAc,EAAE,WAAW,EAAC,GAAG,IAAI,CAAC;IAC3C,MAAM,cAAc,GAClB,WAAW,IAAI,4DAA4D,CAAC;IAC9E,MAAM,MAAM,GAAG,gCAAgC,CAAC,cAAc,CAAC,CAAC;IAChE,MAAM,UAAU,GACd,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC;QACtB,CAAC,CAAC,2BAA2B,MAAM,EAAE;QACrC,CAAC,CAAC,kFAAkF,CAAC;IAEzF,OAAO,KAAK,cAAc;;EAE1B,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsDd,UAAU;;EAEV,6BAA6B,EAAE,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,qBAAqB,GAAG,CAC5B,IAAmB,EACnB,UAAyC,EACjC,EAAE;IACV,MAAM,EAAC,cAAc,EAAC,GAAG,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,gCAAgC,CAAC,IAAI,EAAE,8BAA8B,CAAC,UAAU,CAAC,CAAC,CAAC;IAChG,OAAO;;;gBAGO,cAAc;;;;EAI5B,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AAEF,MAAM,+BAA+B,GAAG,CACtC,IAAmB,EACnB,UAAyC,EACjC,EAAE;IACV,MAAM,EAAC,cAAc,EAAC,GAAG,IAAI,CAAC;IAC9B,MAAM,IAAI,GAAG,gCAAgC,CAAC,IAAI,EAAE,8BAA8B,CAAC,UAAU,CAAC,CAAC,CAAC;IAChG,OAAO;;;gBAGO,cAAc;;;;EAI5B,IAAI,EAAE,CAAC;AACT,CAAC,CAAC;AAEF,MAAM,4BAA4B,GAAG,CAAC,UAAyC,EAAU,EAAE;IACzF,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC;QAC1C,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,EAAE,GAAG,4BAA4B,CAAC,eAAe,CAAC,CAAC;IACzD,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AACjC,CAAC,CAAC;AAEF,MAAM,6BAA6B,GAAG,CAAC,UAAyC,EAAU,EAAE;IAC1F,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC3C,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,EAAE,GAAG,4BAA4B,CAAC,gBAAgB,CAAC,CAAC;IAC1D,OAAO,EAAE,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AACjC,CAAC,CAAC;AAEF,MAAM,wBAAwB,GAAG,CAC/B,IAAmB,EACnB,UAAyC,EACjC,EAAE;IACV,MAAM,EAAC,cAAc,EAAC,GAAG,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAG,4BAA4B,CAAC,UAAU,CAAC,CAAC;IACvD,OAAO;;;gBAGO,cAAc;;;;IAI1B,cAAc;;;;;;;;;;;;;;;;;;;;EAoBhB,KAAK;;;;;;;;;;;;;CAaN,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,+BAA+B,GAAG,CAAC,IAAmB,EAAU,EAAE;IACtE,MAAM,EAAC,cAAc,EAAC,GAAG,IAAI,CAAC;IAC9B,OAAO;;;gBAGO,cAAc;;;;IAI1B,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4BjB,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,yBAAyB,GAAG,CAChC,IAAmB,EACnB,UAAyC,EACjC,EAAE;IACV,MAAM,EAAC,cAAc,EAAC,GAAG,IAAI,CAAC;IAC9B,MAAM,KAAK,GAAG,6BAA6B,CAAC,UAAU,CAAC,CAAC;IACxD,OAAO;;;gBAGO,cAAc;;;;IAI1B,cAAc;;;;;;;;;;;;;;;;;;;;EAoBhB,KAAK;;;;;;;;;;;;;;;;;;CAkBN,CAAC;AACF,CAAC,CAAC;AAEF,MAAM,gCAAgC,GAAG,CAAC,IAAmB,EAAU,EAAE;IACvE,MAAM,EAAC,cAAc,EAAC,GAAG,IAAI,CAAC;IAC9B,OAAO;;;gBAGO,cAAc;;;;IAI1B,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BjB,CAAC;AACF,CAAC,CAAC;AAOF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,IAA0B,EAAiB,EAAE;IAChF,MAAM,UAAU,GAAG,iCAAiC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpE,4CAA4C;IAC5C,MAAM,gBAAgB,GAAG,CAAC,OAAe,EAAU,EAAE;QACnD,OAAO,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC;IAEF,MAAM,cAAc,GAAG,gBAAgB,CAAC,wBAAwB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IACpF,MAAM,eAAe,GAAG,gBAAgB,CAAC,yBAAyB,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC;IAEtF,OAAO;QACL,yEAAyE;QACzE,EAAC,OAAO,EAAE,qBAAqB,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,IAAI,EAAE,4BAA4B,EAAC;QACtF;YACE,OAAO,EAAE,+BAA+B,CAAC,IAAI,EAAE,UAAU,CAAC;YAC1D,IAAI,EAAE,uCAAuC;SAC9C;QAED,wEAAwE;QACxE,EAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,mBAAmB,EAAC;QACpD,EAAC,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,mBAAmB,EAAC;QAEpD,0EAA0E;QAC1E,EAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,EAAC;QACtD,EAAC,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,oBAAoB,EAAC;QAEtD,gDAAgD;QAChD,EAAC,OAAO,EAAE,sBAAsB,EAAE,EAAE,IAAI,EAAE,gBAAgB,EAAC;KAC5D,CAAC;AACJ,CAAC,CAAC;AAEF,MAAM,8BAA8B,GAAG,CACrC,IAA6B,EACmB,EAAE;IAClD,MAAM,aAAa,GAAG,IAAuC,CAAC;IAE9D,IAAI,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,aAAa,CAAC,cAAc,EAAE,CAAC;QAC5D,OAAO;YACL,OAAO,EAAE;gBACP;oBACE,IAAI,EAAE,2DAA2D;oBACjE,IAAI,EAAE,MAAM;iBACb;aACF;SACF,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,oBAAoB,CAAC,aAAa,CAAC,CAAC;IAElD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEhE,MAAM,YAAY,GAAG,4BAA4B,aAAa,CAAC,cAAc;;;;;;EAM7E,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmCT,CAAC;IAEA,MAAM,YAAY,GAAG,KAAK;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC;QACzF,OAAO,SAAS,CAAC,CAAC,IAAI;;QAEpB,IAAI;EACV,CAAC,CAAC,OAAO;;CAEV,CAAC;IACE,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;QACL,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,YAAY,GAAG,YAAY,EAAE,IAAI,EAAE,MAAM,EAAC,CAAC;KAC7D,CAAC;AACJ,CAAC,CAAC;AACF,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,IAAY,EACZ,IAA6B,EACmB,EAAE;IAClD,IAAI,IAAI,KAAK,4BAA4B,EAAE,CAAC;QAC1C,OAAO,8BAA8B,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO;QACL,OAAO,EAAE,CAAC,EAAC,IAAI,EAAE,2BAA2B,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAC,CAAC;KACnE,CAAC;AACJ,CAAC,CAAC;AAaF,MAAM,CAAC,MAAM,gBAAgB,GAAsB;IACjD;QACE,SAAS,EAAE;YACT;gBACE,WAAW,EAAE,iEAAiE;gBAC9E,IAAI,EAAE,SAAS;gBACf,QAAQ,EAAE,IAAI;aACf;YACD;gBACE,WAAW,EAAE,8DAA8D;gBAC3E,IAAI,EAAE,gBAAgB;gBACtB,QAAQ,EAAE,IAAI;aACf;SACF;QACD,WAAW,EACT,qHAAqH;QACvH,IAAI,EAAE,mBAAmB;KAC1B;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAC1C,IAAY,EACZ,IAA4B,EAC8C,EAAE;IAC5E,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;QACjC,OAAO;YACL,QAAQ,EAAE;gBACR;oBACE,OAAO,EAAE;wBACP,IAAI,EAAE,6BAA6B,IAAI,EAAE;wBACzC,IAAI,EAAE,MAAM;qBACb;oBACD,IAAI,EAAE,MAAM;iBACb;aACF;SACF,CAAC;IACJ,CAAC;IAED,MAAM,EAAC,OAAO,EAAE,cAAc,EAAC,GAAG,IAAI,CAAC;IAEvC,MAAM,MAAM,GAAG;;+BAEc,OAAO;sBAChB,cAAc;;;;;0BAKV,OAAO,oBAAoB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;OAwB5D,CAAC;IAEN,OAAO;QACL,QAAQ,EAAE;YACR;gBACE,OAAO,EAAE;oBACP,IAAI,EAAE,MAAM;oBACZ,IAAI,EAAE,MAAM;iBACb;gBACD,IAAI,EAAE,MAAM;aACb;SACF;KACF,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../src/bootstrap.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,7 @@
2
2
 
3
3
  React Native UI component library (a large component library):
4
4
 
5
- - **Layout**: Box, Page, SplitPage, Card
5
+ - **Layout**: Box, Page, ListDetailLayout, SplitPage, Card
6
6
  - **Forms**: TextField, SelectField, DateTimeField, CheckBox
7
7
  - **Display**: Text, Heading, Badge, DataTable
8
8
  - **Actions**: Button, IconButton, Link
@@ -41,8 +41,10 @@ New apps: **syncdb + Better Auth** for data and auth; **rtk** for generated SDK
41
41
 
42
42
  Use the hosted Terreno MCP for documentation and generators, and `terreno-mcp-local` for package
43
43
  versions, read-only Mongo inspection, merged logs, client state, and Metro/CDP runtime tools.
44
- Configure both with `docs/how-to/set-up-terreno-mcp.md`; follow
45
- `docs/how-to/debug-with-mcp.md` for the diagnostic workflow. The `terreno` CLI exposes the same
44
+ Configure both with
45
+ [Set up Terreno MCP](https://github.com/TerrenoLabs/terreno/blob/master/docs/how-to/set-up-terreno-mcp.md);
46
+ follow [Debug with MCP](https://github.com/TerrenoLabs/terreno/blob/master/docs/how-to/debug-with-mcp.md)
47
+ for the diagnostic workflow. The `terreno` CLI exposes the same
46
48
  local tools for terminal use.
47
49
 
48
50
  ## Development
@@ -1,216 +1,269 @@
1
1
  # Terreno Patterns & Best Practices
2
2
 
3
+ Collection CRUD is local-first: the backend syncs a collection with `syncPlugin` and a `sync`
4
+ config on `modelRouter`, and the frontend reads and writes it with `@terreno/syncdb/react`
5
+ (`useQuery`, `useMutate`). Generated RTK Query hooks from `openApiSdk.ts` are only for routes
6
+ that are not synced: auth (`/auth/me`), AI, feature flags, and custom request/response actions.
7
+
3
8
  ## Backend Patterns
4
9
 
5
10
  ### Model Definition
6
11
 
12
+ A synced model uses a String `_id` (offline clients mint ids), `isDeletedPlugin` (via the app's
13
+ default plugins), and `syncPlugin`. Types live in `src/types/models/`. Every field has a
14
+ `description`.
15
+
7
16
  ```typescript
17
+ import {syncPlugin} from "@terreno/api";
8
18
  import mongoose from "mongoose";
9
- import { addDefaultPlugins } from "@terreno/api";
10
-
11
- interface TodoDocument extends mongoose.Document {
12
- title: string;
13
- completed: boolean;
14
- ownerId: mongoose.Types.ObjectId;
15
- created: Date;
16
- updated: Date;
17
- }
18
-
19
- interface TodoModel extends mongoose.Model<TodoDocument> {
20
- findByOwner(ownerId: string): Promise<TodoDocument[]>;
21
- }
19
+ import type {TodoDocument, TodoModel} from "../types/models/todoTypes";
20
+ import {addDefaultPlugins} from "./modelPlugins";
22
21
 
23
22
  const todoSchema = new mongoose.Schema<TodoDocument, TodoModel>(
24
23
  {
25
- title: { type: String, required: true, trim: true },
26
- completed: { type: Boolean, default: false },
27
- ownerId: { type: mongoose.Schema.Types.ObjectId, ref: "User", required: true },
24
+ _id: {
25
+ default: (): string => new mongoose.Types.ObjectId().toHexString(),
26
+ description: "The document id (String so offline sync clients can mint ids)",
27
+ type: String,
28
+ },
29
+ completed: {
30
+ default: false,
31
+ description: "Whether the todo item has been completed",
32
+ type: Boolean,
33
+ },
34
+ ownerId: {
35
+ description: "The user who owns this todo",
36
+ ref: "User",
37
+ required: true,
38
+ type: mongoose.Schema.Types.ObjectId,
39
+ },
40
+ title: {
41
+ description: "The title of the todo item",
42
+ required: true,
43
+ trim: true,
44
+ type: String,
45
+ },
28
46
  },
29
- {
30
- strict: "throw",
31
- toJSON: { virtuals: true },
32
- toObject: { virtuals: true },
33
- }
47
+ {strict: "throw", toJSON: {virtuals: true}, toObject: {virtuals: true}}
34
48
  );
35
49
 
50
+ // addDefaultPlugins applies createdUpdatedPlugin, isDeletedPlugin, findExactlyOne, findOneOrNone.
36
51
  addDefaultPlugins(todoSchema);
37
-
38
- todoSchema.statics.findByOwner = async function (ownerId: string) {
39
- return this.find({ ownerId });
40
- };
52
+ // Stamps a per-stream _syncSeq on every write; required by the router's sync config.
53
+ todoSchema.plugin(syncPlugin);
41
54
 
42
55
  export const Todo = mongoose.model<TodoDocument, TodoModel>("Todo", todoSchema);
43
56
  ```
44
57
 
45
58
  ### Route Setup
46
59
 
60
+ Use the three-argument `modelRouter` and register it on `TerrenoApp` together with `SyncApp`
61
+ and `RealtimeApp`. Sync needs a MongoDB replica set (change streams).
62
+
47
63
  ```typescript
48
- import { Router } from "express";
49
- import { modelRouter, Permissions, OwnerQueryFilter } from "@terreno/api";
50
- import { Todo, TodoDocument } from "../models/todo";
51
- import { UserDocument } from "../models/user";
52
-
53
- export const addTodoRoutes = (router: Router) => {
54
- router.use(
55
- "/todos",
56
- modelRouter(Todo, {
57
- permissions: {
58
- create: [Permissions.IsAuthenticated],
59
- list: [Permissions.IsAuthenticated],
60
- read: [Permissions.IsOwner],
61
- update: [Permissions.IsOwner],
62
- delete: [Permissions.IsOwner],
63
- },
64
- queryFields: ["completed"],
65
- queryFilter: OwnerQueryFilter,
66
- sort: "-created",
67
- preCreate: (body, req) => ({
68
- ...body,
69
- ownerId: (req.user as UserDocument)?._id,
70
- }),
71
- postCreate: async (todo, req) => {
72
- // Send notification, update stats, etc.
73
- logger.info("Todo created", { todoId: todo._id });
74
- },
75
- })
76
- );
77
- };
64
+ import {modelRouter, OwnerQueryFilter, Permissions} from "@terreno/api";
65
+ import {Todo} from "../models/todo";
66
+ import type {UserDocument} from "../types/models/userTypes";
67
+
68
+ export const todoRouter = modelRouter("/todos", Todo, {
69
+ permissions: {
70
+ create: [Permissions.IsAuthenticated],
71
+ delete: [Permissions.IsOwner],
72
+ list: [Permissions.IsAuthenticated],
73
+ read: [Permissions.IsOwner],
74
+ update: [Permissions.IsOwner],
75
+ },
76
+ preCreate: (body, req) => ({
77
+ ...body,
78
+ ownerId: (req.user as unknown as UserDocument)?._id,
79
+ }),
80
+ queryFields: ["completed", "ownerId"],
81
+ queryFilter: OwnerQueryFilter,
82
+ sort: "-created",
83
+ sync: {scope: {type: "owner"}},
84
+ });
78
85
  ```
79
86
 
80
87
  ### Custom Endpoints
81
88
 
82
- ```typescript
83
- import { createOpenApiBuilder, APIError, authenticateMiddleware } from "@terreno/api";
84
-
85
- const toggleCompleteBuilder = createOpenApiBuilder()
86
- .withTags(["Todos"])
87
- .withSummary("Toggle todo completion status")
88
- .withResponse(200, TodoSchema);
89
-
90
- router.post(
91
- "/todos/:id/toggle",
92
- authenticateMiddleware,
93
- toggleCompleteBuilder.build(),
94
- async (req, res, next) => {
95
- try {
96
- const todo = await Todo.findOneOrThrow({ _id: req.params.id });
97
-
98
- if (!todo.ownerId.equals((req.user as UserDocument)._id)) {
99
- throw new APIError({
100
- status: 403,
101
- title: "Forbidden",
102
- detail: "You do not own this todo",
103
- });
104
- }
105
-
106
- todo.completed = !todo.completed;
107
- await todo.save();
89
+ Add non-CRUD endpoints as `collectionActions` or `instanceActions` on the same `modelRouter`,
90
+ not with `router.post`. They get permissions, validation, and OpenAPI automatically.
108
91
 
109
- res.json(todo);
110
- } catch (error) {
111
- next(error);
112
- }
113
- }
114
- );
92
+ ```typescript
93
+ import {APIError, modelRouter, Permissions, z} from "@terreno/api";
94
+
95
+ export const todoRouter = modelRouter("/todos", Todo, {
96
+ // ...permissions, sync, and the rest from Route Setup
97
+ collectionActions: {
98
+ bulkComplete: {
99
+ body: z.object({ids: z.array(z.string()).min(1)}).strict(),
100
+ handler: async ({body, user}) => {
101
+ const result = await Todo.updateMany(
102
+ {_id: {$in: body.ids}, ownerId: user?._id},
103
+ {$set: {completed: true}}
104
+ );
105
+ return {matched: result.matchedCount, modified: result.modifiedCount};
106
+ },
107
+ method: "POST",
108
+ permissions: [Permissions.IsAuthenticated],
109
+ },
110
+ },
111
+ instanceActions: {
112
+ markComplete: {
113
+ handler: async ({doc}) => {
114
+ if (doc.completed) {
115
+ throw new APIError({status: 409, title: "Todo is already completed"});
116
+ }
117
+ doc.completed = true;
118
+ await doc.save();
119
+ return doc;
120
+ },
121
+ method: "POST",
122
+ permissions: [Permissions.IsOwner],
123
+ },
124
+ },
125
+ });
115
126
  ```
116
127
 
117
128
  ## Frontend Patterns
118
129
 
119
130
  ### Screen with Data Fetching
120
131
 
132
+ Synced collections are read from the local store with `useQuery` and written with `useMutate`.
133
+ Writes apply immediately and sync through the durable outbox: there is no loading state per
134
+ request, no `.unwrap()`, and no refetch after a mutation. Wrap the screen (or the app) in
135
+ `SyncDbProvider`, and do not call `mutate` until `syncDb.start()` has resolved.
136
+
121
137
  ```typescript
122
- import React, { useCallback, useState } from "react";
123
- import { Box, Page, Text, Button, TextField, ScrollView } from "@terreno/ui";
124
- import { useGetTodosQuery, useCreateTodoMutation } from "@/store/openApiSdk";
138
+ import {generateMutationId} from "@terreno/syncdb";
139
+ import {SyncDbProvider, useMutate, useQuery} from "@terreno/syncdb/react";
140
+ import {Box, Button, Card, CheckBox, Heading, IconButton, Text, TextField} from "@terreno/ui";
141
+ import {DateTime} from "luxon";
142
+ import type React from "react";
143
+ import {useCallback, useState} from "react";
144
+ import {useSyncDbReady} from "@/hooks/useSyncDbReady";
145
+ import {syncDb} from "@/store/syncdb";
146
+
147
+ interface Todo {
148
+ _id: string;
149
+ completed?: boolean;
150
+ created: string;
151
+ ownerId: string;
152
+ title: string;
153
+ }
125
154
 
126
- const TodoScreen: React.FC = () => {
127
- const [newTitle, setNewTitle] = useState("");
128
- const { data: todos, isLoading, error, refetch } = useGetTodosQuery({});
129
- const [createTodo, { isLoading: isCreating }] = useCreateTodoMutation();
155
+ const TodoList: React.FC = () => {
156
+ const isSyncDbReady = useSyncDbReady();
157
+ const [newTitle, setNewTitle] = useState<string>("");
158
+ const todos = useQuery<Todo>("todos", {sort: "-created"});
159
+ const {create, update, remove} = useMutate("todos");
130
160
 
131
- const handleCreate = useCallback(async () => {
132
- if (!newTitle.trim()) {
161
+ const handleCreate = useCallback((): void => {
162
+ const title = newTitle.trim();
163
+ if (!title || !isSyncDbReady) {
133
164
  return;
134
165
  }
166
+ // Mint the id locally so the optimistic row is addressable before the server acks it.
167
+ const id = generateMutationId();
168
+ create({data: {_id: id, completed: false, created: DateTime.now().toISO(), title}, id});
169
+ setNewTitle("");
170
+ }, [create, isSyncDbReady, newTitle]);
171
+
172
+ const handleToggle = useCallback(
173
+ (todo: Todo): void => {
174
+ update({data: {completed: !todo.completed}, id: todo._id});
175
+ },
176
+ [update]
177
+ );
135
178
 
136
- try {
137
- await createTodo({ title: newTitle }).unwrap();
138
- setNewTitle("");
139
- } catch (err) {
140
- console.error("Failed to create todo:", err);
141
- }
142
- }, [newTitle, createTodo]);
143
-
144
- if (isLoading) {
145
- return (
146
- <Page navigation={undefined}>
147
- <Box flex={1} alignItems="center" justifyContent="center">
148
- <Text>Loading...</Text>
149
- </Box>
150
- </Page>
151
- );
152
- }
153
-
154
- if (error) {
155
- return (
156
- <Page navigation={undefined}>
157
- <Box flex={1} alignItems="center" justifyContent="center" gap={2}>
158
- <Text color="error500">Failed to load todos</Text>
159
- <Button text="Retry" onClick={refetch} />
160
- </Box>
161
- </Page>
162
- );
163
- }
179
+ const handleDelete = useCallback(
180
+ (id: string): void => {
181
+ remove({id});
182
+ },
183
+ [remove]
184
+ );
164
185
 
165
186
  return (
166
- <Page navigation={undefined} title="Todos">
167
- <ScrollView>
168
- <Box padding={4} gap={3}>
169
- <Box direction="row" gap={2}>
170
- <Box flex={1}>
171
- <TextField
172
- placeholder="New todo..."
173
- value={newTitle}
174
- onChangeText={setNewTitle}
175
- />
187
+ <Box gap={3} padding={4} testID="todos-screen">
188
+ <Heading size="lg">Todos</Heading>
189
+ <Card>
190
+ <Box gap={3}>
191
+ <TextField
192
+ onChange={setNewTitle}
193
+ onEnter={handleCreate}
194
+ placeholder="New todo"
195
+ testID="todos-title-input"
196
+ title="New Todo"
197
+ value={newTitle}
198
+ />
199
+ <Button
200
+ disabled={!isSyncDbReady || !newTitle.trim()}
201
+ iconName="plus"
202
+ onClick={handleCreate}
203
+ testID="todos-create-button"
204
+ text="Add Todo"
205
+ />
206
+ </Box>
207
+ </Card>
208
+ {todos.length === 0 ? <Text testID="todos-empty-state">No todos yet</Text> : null}
209
+ {todos.map((todo) => (
210
+ <Card key={todo._id} testID={`todo-item-${todo._id}`}>
211
+ <Box alignItems="center" direction="row" gap={2}>
212
+ <Box onClick={() => handleToggle(todo)} testID={`todo-toggle-${todo._id}`}>
213
+ <CheckBox selected={Boolean(todo.completed)} size="md" />
214
+ </Box>
215
+ <Box flex="grow">
216
+ <Text>{todo.title}</Text>
176
217
  </Box>
177
- <Button
178
- text="Add"
179
- onClick={handleCreate}
180
- loading={isCreating}
181
- disabled={!newTitle.trim()}
218
+ <IconButton
219
+ accessibilityLabel="Delete todo"
220
+ iconName="trash"
221
+ onClick={() => handleDelete(todo._id)}
222
+ testID={`todo-delete-${todo._id}`}
223
+ variant="destructive"
182
224
  />
183
225
  </Box>
184
-
185
- {todos?.map((todo) => (
186
- <TodoItem key={todo.id} todo={todo} />
187
- ))}
188
- </Box>
189
- </ScrollView>
190
- </Page>
226
+ </Card>
227
+ ))}
228
+ </Box>
191
229
  );
192
230
  };
193
231
 
194
- export default TodoScreen;
232
+ const TodosScreen: React.FC = () => (
233
+ <SyncDbProvider client={syncDb}>
234
+ <TodoList />
235
+ </SyncDbProvider>
236
+ );
237
+
238
+ export default TodosScreen;
195
239
  ```
196
240
 
241
+ For large lists, render `useEntityIds` in the list and `useEntity` in each row, so a change to
242
+ one row re-renders only that row. Running `terreno-syncdb-codegen` generates typed hooks such as
243
+ `useTodo`, `useCreateTodo`, and `useUpdateTodo` in `store/syncDbSdk.ts`, which wrap the same
244
+ calls.
245
+
197
246
  ### Form with Validation
198
247
 
248
+ A form that calls a non-synced route uses the generated RTK Query mutation and `.unwrap()`.
249
+ Here it updates the signed-in user's profile through `PATCH /auth/me`.
250
+
199
251
  ```typescript
200
- import React, { useCallback, useState } from "react";
201
- import { Box, Button, TextField, EmailField, Text } from "@terreno/ui";
202
- import { useCreateUserMutation } from "@/store/openApiSdk";
252
+ import {Box, Button, EmailField, TextField} from "@terreno/ui";
253
+ import type React from "react";
254
+ import {useCallback, useState} from "react";
255
+ import {usePatchMeMutation} from "@/store/sdk";
203
256
 
204
257
  interface FormErrors {
205
258
  name?: string;
206
259
  email?: string;
207
260
  }
208
261
 
209
- const CreateUserForm: React.FC = () => {
210
- const [name, setName] = useState("");
211
- const [email, setEmail] = useState("");
262
+ const ProfileForm: React.FC = () => {
263
+ const [name, setName] = useState<string>("");
264
+ const [email, setEmail] = useState<string>("");
212
265
  const [errors, setErrors] = useState<FormErrors>({});
213
- const [createUser, { isLoading }] = useCreateUserMutation();
266
+ const [updateProfile, {isLoading}] = usePatchMeMutation();
214
267
 
215
268
  const validate = useCallback((): boolean => {
216
269
  const newErrors: FormErrors = {};
@@ -229,40 +282,43 @@ const CreateUserForm: React.FC = () => {
229
282
  return Object.keys(newErrors).length === 0;
230
283
  }, [name, email]);
231
284
 
232
- const handleSubmit = useCallback(async () => {
285
+ const handleSubmit = useCallback(async (): Promise<void> => {
233
286
  if (!validate()) {
234
287
  return;
235
288
  }
236
289
 
237
290
  try {
238
- await createUser({ name, email }).unwrap();
239
- // Success - navigate or show message
240
- } catch (err: any) {
241
- if (err.data?.fields) {
242
- setErrors(err.data.fields);
291
+ await updateProfile({email, name}).unwrap();
292
+ } catch (error: unknown) {
293
+ const fields = (error as {data?: {fields?: FormErrors}}).data?.fields;
294
+ if (fields) {
295
+ setErrors(fields);
243
296
  }
244
297
  }
245
- }, [name, email, validate, createUser]);
298
+ }, [name, email, validate, updateProfile]);
246
299
 
247
300
  return (
248
- <Box padding={4} gap={3}>
301
+ <Box gap={3} padding={4}>
249
302
  <TextField
250
- label="Name"
303
+ errorText={errors.name}
304
+ onChange={setName}
305
+ testID="profile-name-input"
306
+ title="Name"
251
307
  value={name}
252
- onChangeText={setName}
253
- error={errors.name}
254
308
  />
255
309
  <EmailField
256
- label="Email"
310
+ errorText={errors.email}
311
+ onChange={setEmail}
312
+ testID="profile-email-input"
313
+ title="Email"
257
314
  value={email}
258
- onChangeText={setEmail}
259
- error={errors.email}
260
315
  />
261
316
  <Button
262
- text="Create User"
263
- onClick={handleSubmit}
264
- loading={isLoading}
265
317
  fullWidth
318
+ loading={isLoading}
319
+ onClick={handleSubmit}
320
+ testID="profile-save-button"
321
+ text="Save"
266
322
  />
267
323
  </Box>
268
324
  );
@@ -305,19 +361,24 @@ throw new APIError({
305
361
 
306
362
  ### Frontend
307
363
 
364
+ A synced write does not return the server's answer to the caller. A rejected or conflicting
365
+ mutation shows up in `useSyncStatus()` and `useConflicts()`; render the conflict UI from those
366
+ hooks. Errors from
367
+ non-synced RTK Query routes arrive on `.unwrap()`:
368
+
308
369
  ```typescript
309
370
  try {
310
371
  await someApiCall().unwrap();
311
- } catch (err: any) {
372
+ } catch (error: unknown) {
373
+ const data = (error as {data?: {detail?: string; fields?: Record<string, string>}}).data;
312
374
  // Handle field-specific errors
313
- if (err.data?.fields) {
314
- setErrors(err.data.fields);
375
+ if (data?.fields) {
376
+ setErrors(data.fields);
315
377
  return;
316
378
  }
317
379
 
318
380
  // Handle general errors
319
- const message = err.data?.detail || err.message || "An error occurred";
320
- toast.error(message);
381
+ toast.error(data?.detail ?? "An error occurred");
321
382
  }
322
383
  ```
323
384