fastapi-rtk 2.18.3__tar.gz → 2.19.1__tar.gz

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 (343) hide show
  1. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/PKG-INFO +1 -1
  2. fastapi_rtk-2.19.1/fastapi_rtk/_version.py +1 -0
  3. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/adapters/agents-md/AGENTS.md +4 -1
  4. fastapi_rtk-2.19.1/fastapi_rtk/agent_docs/adapters/claude/skill.frontmatter +2 -0
  5. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/guide/backend.md +4 -27
  6. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/guide/frontend.md +12 -197
  7. fastapi_rtk-2.19.1/fastapi_rtk/agent_docs/guide/jsonforms.md +165 -0
  8. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/agent_docs/guide/legacy.md +3 -3
  9. fastapi_rtk-2.19.1/fastapi_rtk/agent_docs/guide/overview.md +50 -0
  10. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_lifecycle_service.py +2 -0
  11. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/utils.py +4 -2
  12. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/const.py +6 -0
  13. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/routers.py +16 -0
  14. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/apis.py +17 -3
  15. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/auth/test_oauth.py +93 -0
  16. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_audit.py +19 -2
  17. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_utils.py +10 -0
  18. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_const.py +11 -0
  19. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_routers.py +85 -0
  20. fastapi_rtk-2.18.3/fastapi_rtk/_version.py +0 -1
  21. fastapi_rtk-2.18.3/fastapi_rtk/agent_docs/adapters/claude/skill.frontmatter +0 -2
  22. fastapi_rtk-2.18.3/fastapi_rtk/agent_docs/guide/overview.md +0 -106
  23. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/.gitignore +0 -0
  24. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/LICENSE +0 -0
  25. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/README.md +0 -0
  26. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/__init__.py +0 -0
  27. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/__init__.py +0 -0
  28. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/base_api.py +0 -0
  29. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/__init__.py +0 -0
  30. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/model_rest_api.py +0 -0
  31. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/params.py +0 -0
  32. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/__init__.py +0 -0
  33. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_add_service.py +0 -0
  34. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_bulk_service.py +0 -0
  35. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_delete_service.py +0 -0
  36. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_download_service.py +0 -0
  37. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_edit_service.py +0 -0
  38. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_file_service.py +0 -0
  39. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_info_service.py +0 -0
  40. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_list_service.py +0 -0
  41. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_schema_service.py +0 -0
  42. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/api/model_rest_api/services/_show_service.py +0 -0
  43. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/apis/__init__.py +0 -0
  44. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/apis/info.py +0 -0
  45. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/apis/license.py +0 -0
  46. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/__init__.py +0 -0
  47. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/auth.py +0 -0
  48. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/hashers/__init__.py +0 -0
  49. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/hashers/pbkdf2.py +0 -0
  50. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/hashers/scrypt.py +0 -0
  51. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/ldap.py +0 -0
  52. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/password_helpers/__init__.py +0 -0
  53. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/password_helpers/fab.py +0 -0
  54. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/__init__.py +0 -0
  55. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/config.py +0 -0
  56. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/db.py +0 -0
  57. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/auth/strategies/jwt.py +0 -0
  58. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/__init__.py +0 -0
  59. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/__init__.py +0 -0
  60. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/column.py +0 -0
  61. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/db.py +0 -0
  62. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/exceptions.py +0 -0
  63. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/filters.py +0 -0
  64. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/interface.py +0 -0
  65. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/model.py +0 -0
  66. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/generic/session.py +0 -0
  67. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/__init__.py +0 -0
  68. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/column.py +0 -0
  69. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/db.py +0 -0
  70. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/exceptions.py +0 -0
  71. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/__init__.py +0 -0
  72. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/__init__.py +0 -0
  73. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_decorators_service.py +0 -0
  74. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_logger.py +0 -0
  75. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/_processing_service.py +0 -0
  76. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/audit.py +0 -0
  77. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/audit/types.py +0 -0
  78. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/__init__.py +0 -0
  79. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/filters.py +0 -0
  80. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/extensions/geoalchemy2/geometry_converter.py +0 -0
  81. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/filters.py +0 -0
  82. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/interface.py +0 -0
  83. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/model.py +0 -0
  84. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/backends/sqla/session.py +0 -0
  85. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/__init__.py +0 -0
  86. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/cache.py +0 -0
  87. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/db.py +0 -0
  88. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/file_manager.py +0 -0
  89. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/filter.py +0 -0
  90. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/interface.py +0 -0
  91. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/loop_manager.py +0 -0
  92. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/model.py +0 -0
  93. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/session.py +0 -0
  94. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/bases/user_cache.py +0 -0
  95. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/__init__.py +0 -0
  96. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/cache.py +0 -0
  97. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/serializers.py +0 -0
  98. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/caches/storages.py +0 -0
  99. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/__init__.py +0 -0
  100. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/cli.py +0 -0
  101. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/__init__.py +0 -0
  102. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/_security_crypto.py +0 -0
  103. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/__init__.py +0 -0
  104. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/adapters/__init__.py +0 -0
  105. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/adapters/agents_md.py +0 -0
  106. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/adapters/claude.py +0 -0
  107. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/base.py +0 -0
  108. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/agent/docs.py +0 -0
  109. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/__init__.py +0 -0
  110. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/README +0 -0
  111. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/alembic.ini.mako +0 -0
  112. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/env.py +0 -0
  113. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi/script.py.mako +0 -0
  114. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/README +0 -0
  115. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/alembic.ini.mako +0 -0
  116. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/env.py +0 -0
  117. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/db/templates/fastapi-multidb/script.py.mako +0 -0
  118. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/export.py +0 -0
  119. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/security.py +0 -0
  120. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/commands/translate.py +0 -0
  121. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/const.py +0 -0
  122. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/decorators.py +0 -0
  123. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/cli/types.py +0 -0
  124. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/config.py +0 -0
  125. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/db.py +0 -0
  126. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/decorators.py +0 -0
  127. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/dependencies.py +0 -0
  128. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/exceptions.py +0 -0
  129. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/fastapi_react_toolkit.py +0 -0
  130. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/__init__.py +0 -0
  131. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/file_manager.py +0 -0
  132. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/image_manager.py +0 -0
  133. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/s3_file_manager.py +0 -0
  134. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/file_managers/s3_image_manager.py +0 -0
  135. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/filters.py +0 -0
  136. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/globals.py +0 -0
  137. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/__init__.py +0 -0
  138. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel/__init__.py +0 -0
  139. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel/cli.py +0 -0
  140. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel/config.py +0 -0
  141. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/babel.cfg +0 -0
  142. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/lazy_text.py +0 -0
  143. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/messages.pot +0 -0
  144. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.mo +0 -0
  145. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/de/LC_MESSAGES/messages.po +0 -0
  146. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.mo +0 -0
  147. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/lang/translations/en/LC_MESSAGES/messages.po +0 -0
  148. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/loop_managers/__init__.py +0 -0
  149. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/loop_managers/loop_manager.py +0 -0
  150. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/manager.py +0 -0
  151. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/middlewares.py +0 -0
  152. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/mixins.py +0 -0
  153. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/models.py +0 -0
  154. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/py.typed +0 -0
  155. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/registry.py +0 -0
  156. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/schemas.py +0 -0
  157. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/__init__.py +0 -0
  158. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/__init__.py +0 -0
  159. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/models.py +0 -0
  160. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/__init__.py +0 -0
  161. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_builtin_role_service.py +0 -0
  162. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_entities.py +0 -0
  163. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_exception_filters.py +0 -0
  164. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_export.py +0 -0
  165. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_import.py +0 -0
  166. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_permission_service.py +0 -0
  167. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_results.py +0 -0
  168. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_role_service.py +0 -0
  169. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/_user_service.py +0 -0
  170. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/security_manager/security_manager.py +0 -0
  171. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/security/sqla/user_cache.py +0 -0
  172. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/setting.py +0 -0
  173. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/types.py +0 -0
  174. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/__init__.py +0 -0
  175. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/async_task_runner.py +0 -0
  176. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/class_factory.py +0 -0
  177. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/csv_json_converter.py +0 -0
  178. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/deep_merge.py +0 -0
  179. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/extender_mixin.py +0 -0
  180. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/flask_appbuilder_utils.py +0 -0
  181. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/formatter.py +0 -0
  182. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/hooks.py +0 -0
  183. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/lazy.py +0 -0
  184. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/merge_schema.py +0 -0
  185. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/multiple_async_contexts.py +0 -0
  186. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/pydantic.py +0 -0
  187. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/run_utils.py +0 -0
  188. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/self_dependencies.py +0 -0
  189. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/smartdefaultdict.py +0 -0
  190. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/sqla.py +0 -0
  191. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/timezone.py +0 -0
  192. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/update_signature.py +0 -0
  193. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/utils/use_default_when_none.py +0 -0
  194. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/fastapi_rtk/version.py +0 -0
  195. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/pyproject.toml +0 -0
  196. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/conftest.py +0 -0
  197. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/docker-compose.gis.yml +0 -0
  198. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/__init__.py +0 -0
  199. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/api/__init__.py +0 -0
  200. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/api/test_add_edit_relation_response.py +0 -0
  201. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/api/test_model_rest_api.py +0 -0
  202. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/apis/__init__.py +0 -0
  203. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/apis/test_info_callable_schema.py +0 -0
  204. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/apis/test_info_contract.py +0 -0
  205. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/auth/__init__.py +0 -0
  206. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/auth/test_auth_flow.py +0 -0
  207. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/conftest.py +0 -0
  208. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/file_managers/__init__.py +0 -0
  209. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/file_managers/test_file_routes.py +0 -0
  210. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/__init__.py +0 -0
  211. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/__init__.py +0 -0
  212. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/conftest.py +0 -0
  213. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_permission_service.py +0 -0
  214. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_role_service.py +0 -0
  215. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_security_manager.py +0 -0
  216. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/security/sqla/test_user_service.py +0 -0
  217. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/integration/test_app_smoke.py +0 -0
  218. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/__init__.py +0 -0
  219. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/__init__.py +0 -0
  220. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/conftest.py +0 -0
  221. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/__init__.py +0 -0
  222. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_columns.py +0 -0
  223. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_download_service.py +0 -0
  224. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_file_mixin.py +0 -0
  225. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_init.py +0 -0
  226. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/model_rest_api/test_service_delegators.py +0 -0
  227. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/api/test_base_api.py +0 -0
  228. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/apis/__init__.py +0 -0
  229. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/apis/test_info.py +0 -0
  230. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/apis/test_license.py +0 -0
  231. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/__init__.py +0 -0
  232. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/hashers/__init__.py +0 -0
  233. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/hashers/test_pbkdf2.py +0 -0
  234. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/hashers/test_scrypt.py +0 -0
  235. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/password_helpers/__init__.py +0 -0
  236. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/password_helpers/test_fab.py +0 -0
  237. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/__init__.py +0 -0
  238. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/test_config.py +0 -0
  239. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/test_db.py +0 -0
  240. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/strategies/test_jwt.py +0 -0
  241. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/auth/test_auth.py +0 -0
  242. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/__init__.py +0 -0
  243. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/__init__.py +0 -0
  244. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_column.py +0 -0
  245. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_db.py +0 -0
  246. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_exceptions.py +0 -0
  247. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_filters.py +0 -0
  248. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_interface.py +0 -0
  249. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_model.py +0 -0
  250. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/generic/test_session.py +0 -0
  251. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/__init__.py +0 -0
  252. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/conftest.py +0 -0
  253. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_audit_service_delegators.py +0 -0
  254. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_audit_types.py +0 -0
  255. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_column.py +0 -0
  256. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_db.py +0 -0
  257. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_filters.py +0 -0
  258. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_geoalchemy2.py +0 -0
  259. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_interface.py +0 -0
  260. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_model.py +0 -0
  261. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/backends/sqla/test_session.py +0 -0
  262. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/__init__.py +0 -0
  263. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_db.py +0 -0
  264. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_file_manager.py +0 -0
  265. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_filter.py +0 -0
  266. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_interface.py +0 -0
  267. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_model.py +0 -0
  268. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/bases/test_session.py +0 -0
  269. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/__init__.py +0 -0
  270. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/__init__.py +0 -0
  271. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/conftest.py +0 -0
  272. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_db.py +0 -0
  273. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_export.py +0 -0
  274. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_security.py +0 -0
  275. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_security_crypto.py +0 -0
  276. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/commands/test_translate.py +0 -0
  277. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/conftest.py +0 -0
  278. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_cli.py +0 -0
  279. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_decorators.py +0 -0
  280. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/cli/test_types.py +0 -0
  281. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/__init__.py +0 -0
  282. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/conftest.py +0 -0
  283. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_file_manager.py +0 -0
  284. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_image_manager.py +0 -0
  285. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_s3_file_manager.py +0 -0
  286. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/file_managers/test_s3_image_manager.py +0 -0
  287. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/__init__.py +0 -0
  288. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/test_babel_cli.py +0 -0
  289. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/test_babel_config.py +0 -0
  290. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/lang/test_lazy_text.py +0 -0
  291. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/__init__.py +0 -0
  292. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/__init__.py +0 -0
  293. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/conftest.py +0 -0
  294. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/__init__.py +0 -0
  295. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_builtin_role_service.py +0 -0
  296. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_entities.py +0 -0
  297. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_permission_service.py +0 -0
  298. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_results.py +0 -0
  299. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_role_service.py +0 -0
  300. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/security_manager/test_user_service.py +0 -0
  301. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/test_apis.py +0 -0
  302. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/test_models.py +0 -0
  303. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/security/sqla/test_security_manager.py +0 -0
  304. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_caches.py +0 -0
  305. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_config.py +0 -0
  306. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_db.py +0 -0
  307. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_decorators.py +0 -0
  308. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_dependencies.py +0 -0
  309. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_exceptions.py +0 -0
  310. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_fastapi_react_toolkit.py +0 -0
  311. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_filters.py +0 -0
  312. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_globals.py +0 -0
  313. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_manager.py +0 -0
  314. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_middlewares.py +0 -0
  315. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_mixins.py +0 -0
  316. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_models.py +0 -0
  317. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_registry.py +0 -0
  318. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_schemas.py +0 -0
  319. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_setting.py +0 -0
  320. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_types.py +0 -0
  321. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_user_cache.py +0 -0
  322. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/test_version.py +0 -0
  323. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/__init__.py +0 -0
  324. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_async_task_runner.py +0 -0
  325. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_class_factory.py +0 -0
  326. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_csv_json_converter.py +0 -0
  327. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_deep_merge.py +0 -0
  328. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_extender_mixin.py +0 -0
  329. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_flask_appbuilder_utils.py +0 -0
  330. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_formatter.py +0 -0
  331. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_hooks.py +0 -0
  332. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_lazy.py +0 -0
  333. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_merge_schema.py +0 -0
  334. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_multiple_async_contexts.py +0 -0
  335. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_pydantic.py +0 -0
  336. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_run_utils.py +0 -0
  337. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_self_dependencies.py +0 -0
  338. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_smartdefaultdict.py +0 -0
  339. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_sqla.py +0 -0
  340. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_timezone.py +0 -0
  341. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_update_signature.py +0 -0
  342. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/tests/unit/utils/test_use_default_when_none.py +0 -0
  343. {fastapi_rtk-2.18.3 → fastapi_rtk-2.19.1}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: fastapi-rtk
3
- Version: 2.18.3
3
+ Version: 2.19.1
4
4
  Summary: A package that provides a set of tools to build a FastAPI application with a Class-Based CRUD API.
5
5
  Project-URL: Homepage, https://codeberg.org/datatactics/fastapi-rtk
6
6
  Project-URL: Issues, https://codeberg.org/datatactics/fastapi-rtk/issues
@@ -0,0 +1 @@
1
+ __version__ = "2.19.1"
@@ -11,7 +11,10 @@ Detailed guidance ships with the package and was installed into this project:
11
11
  - `docs/agents/fastapi-rtk/backend.md`: models, `ModelRestApi`, hooks, filters, permissions,
12
12
  migrations, file uploads, bulk handlers, OAuth.
13
13
  - `docs/agents/fastapi-rtk/frontend.md`: `Provider`, `ApiProvider`, `useApi`, `NextGenDataGrid`,
14
- `useForms`, dialogs, JsonForms, i18n.
14
+ `useForms`, dialogs, i18n.
15
+ - `docs/agents/fastapi-rtk/jsonforms.md`: `JsonFormsWithCustomizer`, schema conditionals, per-control
16
+ props, custom inputs through the `customizer` slot. Open it only for nested or conditional
17
+ schemas; ordinary CRUD forms are covered by `frontend.md`.
15
18
  - `docs/agents/fastapi-rtk/legacy.md`: workarounds for older releases, keyed by symptom. Open it only
16
19
  when this project pins an older fastapi-rtk than the guide was verified against.
17
20
 
@@ -0,0 +1,2 @@
1
+ name: rtk-developer
2
+ description: Use when building, modifying or debugging anything in a fastapi-rtk project: Python models, ModelRestApi or BaseApi endpoints, pre/post hooks, permissions, migrations, file uploads, bulk operations, or the React side (Provider setup, ApiProvider, useApi, NextGenDataGrid, useForms, dialogs, JsonForms). Also on any mention of fastapi-rtk or "rtk backend" / "rtk frontend", and in any project importing from `fastapi_rtk` or the `fastapi-rtk` JS package, where it is preferred over plain FastAPI or React guidance.
@@ -2,31 +2,6 @@
2
2
 
3
3
  Deep reference for the Python side of fastapi-rtk: project setup, models, APIs, hooks, permissions, migrations, file uploads, bulk operations, async tasks. Load this when the user is working on Python code inside an `app/` folder or writing anything that touches `fastapi_rtk` imports.
4
4
 
5
- ## Table of contents
6
-
7
- 1. Install and project layout
8
- 2. Configuration
9
- 3. App initialization
10
- 4. Models
11
- 5. REST APIs: `ModelRestApi`
12
- 6. REST APIs: `BaseApi` for custom endpoints
13
- 7. Pre/post hooks
14
- 8. Column configuration (list / show / add / edit / extra)
15
- 9. Filters and custom filters
16
- 10. Relationship filtering
17
- 11. File and image uploads
18
- 12. Bulk operations
19
- 13. Background and deferred tasks
20
- 14. Permissions, roles, and auth
21
- 15. OAuth providers
22
- 16. Migrations and user management CLI
23
- 17. The `g` global context
24
- 18. Multi-database / binds
25
- 19. Extensions and other backends
26
- 20. Production checklist
27
-
28
- ---
29
-
30
5
  ## 1. Install and project layout
31
6
 
32
7
  ```bash
@@ -608,7 +583,7 @@ Top-level shape: `dict[role_name, list[[api_glob, permission_glob]]]`. Both fiel
608
583
 
609
584
  ### The admin role sits outside `ROLES`
610
585
 
611
- `Setting.AUTH_ROLE_ADMIN` (`setting.py:342`, default `DEFAULT_ADMIN_ROLE = "Admin"` from `const.py:71`) names a role that is deliberately absent from `ROLES`, reachable as `g.admin_role` (`globals.py:141-147`). It holds **every permission of every registered Api**, and a `ROLES` edit cannot narrow that: `_associate_permission_with_api` (`fastapi_react_toolkit.py:506`) writes one `PermissionApi` row per `(permission, api)` taken from each Api's exposed routes plus one per `ROLES` entry, then `_associate_role_with_permission_api` (`:545`) grants the admin role every one of those rows except the ones whose permission name contains `|` (`:566-576`). The per-route row for the same permission is a different row and is never excluded, so piping a `ROLES` entry to hide something from the admin role is a no-op. The cleanup pass also exempts that role from disassociation by name, so an existing grant row survives every restart.
586
+ `Setting.AUTH_ROLE_ADMIN` (`setting.py:368`, default `DEFAULT_ADMIN_ROLE = "Admin"` from `const.py:71`) names a role that is deliberately absent from `ROLES`, reachable as `g.admin_role` (`globals.py:141-147`). It holds **every permission of every registered Api**, and a `ROLES` edit cannot narrow that: `_associate_permission_with_api` (`fastapi_react_toolkit.py:506`) writes one `PermissionApi` row per `(permission, api)` taken from each Api's exposed routes plus one per `ROLES` entry, then `_associate_role_with_permission_api` (`:545`) grants the admin role every one of those rows except the ones whose permission name contains `|` (`:566-576`). The per-route row for the same permission is a different row and is never excluded, so piping a `ROLES` entry to hide something from the admin role is a no-op. The cleanup pass also exempts that role from disassociation by name, so an existing grant row survives every restart.
612
587
 
613
588
  Revoking a permission from it means setting `AUTH_ROLE_ADMIN` to `None` or to an existing `ROLES` role name (which strips the blanket grant entirely, since `if g.admin_role:` guards it), or checking inside the route.
614
589
 
@@ -696,10 +671,12 @@ OAUTH_PROVIDERS = [
696
671
  OAUTH_REDIRECT_URI = "http://localhost:6006"
697
672
  ```
698
673
 
699
- Top-level shape: `list[dict]`. Each dict's `"oauth_client"` is the `httpx_oauth` client; optional keys: `associate_by_email`, `redirect_url`, `redirect_url_factory`, `redirect_url_after_callback` (defaults to `OAUTH_REDIRECT_URI`), `on_before_register`, `on_after_register` (receives the freshly-created user), `on_before_login`. The setting key `OAUTH_CLIENTS` is the canonical name; `OAUTH_PROVIDERS` is accepted as an alternate (`setting.py:311`).
674
+ Top-level shape: `list[dict]`. Each dict's `"oauth_client"` is the `httpx_oauth` client; optional keys: `backend` (`"cookie"` default, or `"jwt"`), `associate_by_email`, `redirect_url`, `redirect_url_factory`, `redirect_url_after_callback` (defaults to `OAUTH_REDIRECT_URI` on a cookie client), `on_before_register`, `on_after_register` (receives the freshly-created user), `on_before_login`. The setting key `OAUTH_CLIENTS` is the canonical name; `OAUTH_PROVIDERS` is accepted as an alternate (`setting.py:390-391`).
700
675
 
701
676
  The toolkit wires the login/callback routes; the frontend's OAuth sign-in hook kicks off the flow.
702
677
 
678
+ A `"backend": "jwt"` client (for clients without a browser cookie store, such as React Native; web apps default to `cookie` and can opt in) answers the callback with `{"access_token", "token_type": "bearer"}` JSON instead of a cookie, so the client has to make the callback request itself: point `redirect_url` (or `redirect_url_factory`) at the URL the app receives the redirect on (on React Native the same URL goes to `createReactNativeAdapters({ oauthRedirectUrl })`, and `oauthSignin` then completes the sign-in itself), or call `oauthCallback("<name>", redirectQuery)` from `useAuth()` with that URL's query string, which forwards `code` and `state` to `GET /api/v1/auth/oauth-authorized/<name>` and stores the returned token. A JWT client rejects `redirect_url_after_callback` with a `ValueError` at startup and a popup login (`?popup=true`) with 400 `OAUTH_POPUP_NOT_SUPPORTED`, since both would drop the token.
679
+
703
680
  ## 16. Migrations and user management CLI
704
681
 
705
682
  ```bash
@@ -2,36 +2,15 @@
2
2
 
3
3
  Deep reference for the React side of fastapi-rtk: Provider setup, ApiProvider, hooks (`useApi`, `useAuth`, `useForms`, …), `NextGenDataGrid`, dialogs, JsonForms, bulk actions, i18n. Load this when the user is working on React code inside an `src/` folder or anything that imports from `fastapi-rtk` (the JS package) or `@mantine/*`.
4
4
 
5
- ## Table of contents
6
-
7
- 1. Install and provider setup
8
- 2. The `ApiProvider` context
9
- 3. `useApi`: the CRUD hook
10
- 4. `useCustomFetch`: non-CRUD endpoints
11
- 5. `useInfo`: metadata-driven UIs
12
- 6. Auth: `useAuth` and OAuth
13
- 7. `NextGenDataGrid`: grids
14
- 8. Forms: `useForms` and dialogs
15
- 9. JsonForms for complex schemas
16
- 10. Bulk actions
17
- 11. Infinite scroll and lazy loading
18
- 12. Language / i18n (`useLang`, `LanguageSelector`)
19
- 13. Standard routes: `UserMenu`, `/users`, `/roles`, `/permissions`
20
- 14. Platform adapters (React Native / Expo)
21
- 15. Other notable exports
22
- 16. Rules of thumb
23
-
24
- ---
25
-
26
5
  ## 1. Install and provider setup
27
6
 
28
- The toolkit expects Mantine as the UI kit.
7
+ The toolkit expects Mantine as the UI kit. Use `Stack`, `Group`, `Grid`, `SimpleGrid` for layout and Mantine inputs for one-off forms; mixing in a second UI kit drifts the theme.
29
8
 
30
9
  ```bash
31
- pnpm add fastapi-rtk @mantine/core @mantine/dates @mantine/form @mantine/hooks
10
+ pnpm add fastapi-rtk @mantine/core @mantine/dates @mantine/form @mantine/hooks dayjs react-router
32
11
  ```
33
12
 
34
- Peer deps: the four `@mantine/*` packages (`^9.0.0`) plus `react` / `react-dom` `^19`. Everything else (mantine-react-table, TanStack Query, zustand, i18next, JsonForms) is bundled inside `fastapi-rtk`. Mantine >= 9.4.1 requires fastapi-rtk >= 2.8.0; older releases loop forever in the grid's `useColumns` ("Maximum update depth exceeded").
13
+ Peer deps: the four `@mantine/*` packages (`^9.0.0`), `react` / `react-dom` (`^19.2.3`), `dayjs` (`^1.11.0`) and `react-router` (`^7.5.3 || ^8.0.0`). Everything else (mantine-react-table, TanStack Query, zustand, i18next, JsonForms) is bundled inside `fastapi-rtk`. Mantine >= 9.4.1 requires fastapi-rtk >= 2.8.0; older releases loop forever in the grid's `useColumns` ("Maximum update depth exceeded").
35
14
 
36
15
  **Provider order is load-bearing.** Mantine must wrap the fastapi-rtk `Provider`, and the stylesheet order matters: Mantine CSS first, fastapi-rtk CSS last (RTK overrides).
37
16
 
@@ -79,7 +58,7 @@ Other `Provider` props: `langProps` (i18n extension, §12), `adapters` (§14), `
79
58
 
80
59
  Key props:
81
60
 
82
- - `resource_name`: **must match** the backend's `ModelRestApi.resource_name`. This is the contract; no magic fixes a mismatch.
61
+ - `resource_name`: **must match** the backend's `ModelRestApi.resource_name`. If the backend says `items`, this says `items`: not `item`, not `/items`, not `Items`. This is the contract; no magic fixes a mismatch.
83
62
  - `initialQueryParams`: seeds pagination, sort, and filters. Use a small, stable object; don't reconstruct inline each render. Full `QueryParams` keys: `page`, `page_size`, `order_column`, `order_direction`, `filters` (list of `{col, opr, value}`), `opr_filters`, `global_filter`, `columns`, `with_count`.
84
63
  - `streaming`: switch the provider to infinite-scroll mode (§11).
85
64
  - Behavior knobs: `relation`, `fetchInfo`, `clearInfoOnRefetch`, `clearDataOnRefetch`, `refetchInfoBasedOnLang`, `refetchDataBasedOnLang`, `resetQueryParamsOnPathChange`, plus raw TanStack overrides `infoQueryProps` / `dataQueryProps` / `streamingQueryProps`. `withApiProvider` is the HOC variant.
@@ -165,7 +144,7 @@ await addEntry({ name: "x" }); // POST /{resource}
165
144
  await updateEntry(id, { name: "y" }); // PUT /{resource}/{id}
166
145
  ```
167
146
 
168
- **Option B, use the TanStack Query namespace `rq`.** `fastapi-rtk` re-exports `@tanstack/react-query` as `rq` (`export * as rq from '@tanstack/react-query'` in `core/src/index.ts:83`; the sibling `mrt` namespace re-exports `mantine-react-table`, with long-form aliases `reactQuery` / `mantineReactTable`). Wrap a `useApi` action (or any toolkit-provided async function) in `rq.useQuery` / `rq.useMutation` so the result participates in the shared query cache (invalidation, refetch-on-focus, devtools). Never call raw `fetch` from the `queryFn` / `mutationFn`, since that re-introduces the exact problems `useCustomFetch` has.
147
+ **Option B, use the TanStack Query namespace `rq`.** `fastapi-rtk` re-exports `@tanstack/react-query` as `rq` (`export * as rq from '@tanstack/react-query'` in `api/src/index.ts:38`, surfaced by `core/src/index.ts:94`; the sibling `mrt` namespace re-exports `mantine-react-table` from `core/src/index.ts:95`, with long-form aliases `reactQuery` / `mantineReactTable`). Wrap a `useApi` action (or any toolkit-provided async function) in `rq.useQuery` / `rq.useMutation` so the result participates in the shared query cache (invalidation, refetch-on-focus, devtools). Never call raw `fetch` from the `queryFn` / `mutationFn`, since that re-introduces the exact problems `useCustomFetch` has.
169
148
 
170
149
  ```jsx
171
150
  import { useApi, rq } from "fastapi-rtk";
@@ -234,6 +213,8 @@ user?.permissions; // string[] of API NAMES the user holds any grant on
234
213
 
235
214
  Gate routes/components on `user` and `user.roles`. The Provider keeps the session via cookie, so `user` is populated on every page load automatically.
236
215
 
216
+ An OAuth provider configured with `"backend": "jwt"` is for clients without a browser cookie store (React Native on `createReactNativeAdapters`, whose bearer transport stores the token); web apps default to `cookie` providers, since a bearer transport on web keeps the token in `localStorage`, readable by any script on the origin. A web app can still opt into JWT: `adapters={{ authTransport: createBearerTransport(webAdapters.storage) }}`, the provider's `redirect_url` at an app route, and `oauthCallback("google", window.location.search)` on that route. On React Native: point the provider's `redirect_url` at the app's deep link (`myapp://oauth/google`), pass the same URL as `createReactNativeAdapters({ ..., oauthRedirectUrl })` (a string for every provider, or `{ google: "myapp://oauth/google", github: "..." }` keyed by provider name), and call `oauthSignin("google", false)` (no popup). A provider missing from that map fails before any browser opens (`error` is `Could not sign in`, `originalError` names the provider). iOS ends the session on any URL with the redirect URL's scheme (so `myapp://oauth/google` and `myapp://oauth/github` are not told apart there), Android on the first deep link starting with it; for an `https` redirect on iOS 17.4+ pass `oauthPreferUniversalLinks: true` (needs the Associated Domains entitlement) to match host and path instead. The RN opener runs `webBrowser.openAuthSessionAsync(loginUrl, oauthRedirectUrl, { preferUniversalLinks })` and hands the redirected URL back, and `oauthSignin` forwards its `code` / `state` to `auth/oauth-authorized/<provider>`, stores the returned token through `authTransport.persistLogin`, and refetches the user; a failure sets `error` to `Could not sign in`. An app that receives the redirect another way calls `oauthCallback("google", redirectQuery)` from `useAuth()` with that URL's query string.
217
+
237
218
  **`user.permissions` holds Api names, not permission names.** `auth/user` builds it as `select(Api.name)` joined through the user's roles (backend reference §14, "What the browser gets"), so `user.permissions.includes("ItemApi")` answers "has any grant on that Api" and nothing finer: `can_get` and `can_post` are indistinguishable there. It is the right gate for "may this user reach this resource at all", and it beats shipping a role list through the frontend config, since it follows the grants themselves and leaves no second list to keep in step. For per-verb detail read `useApi().info.permissions` (§5), which is that Api's `/_info` intersected with the user's grants.
238
219
 
239
220
  ## 7. `NextGenDataGrid`: grids
@@ -274,11 +255,11 @@ import { NextGenDataGrid } from "fastapi-rtk";
274
255
  Key props:
275
256
 
276
257
  - Columns are auto-generated from `/_info` + `list_columns`; there is NO `columns` prop. Per-column overrides go in `columnProps`: a dict keyed by column name whose values are partial MRT `ColumnDef` objects (or factories).
277
- - Row identity comes from the list response's top-level `ids` array, never from a column. `useProvideApi` maps the payload to `{...item, id: data.ids[index]}` (`api/esm/ApiProvider/hooks/useProvideApi.mjs:148-151`, plus the streaming twin at `:187-190`), and `getRowId` reads `getItemId` = `item.id_ ?? item.id`. The zipped `id` is spread last, so it also overrides a same-named column. So adding `"id"` to the backend `list_columns` gives the grid nothing it does not already have, and only surfaces a raw `id` column to the user; `list_obj_schema` is generated `with_id=False`, which is why the raw JSON lacks one and a backend test reading `row["id"]` fails. Such a test should read the response's `ids` array instead of widening the projection.
258
+ - Row identity comes from the list response's top-level `ids` array, never from a column. `useProvideApi` maps the payload to `{...item, id: data.ids[index]}` (`api/src/ApiProvider/hooks/useProvideApi.ts:183-186`, plus the streaming twin at `:236-239`), and `getRowId` reads `getItemId` = `item.id_ ?? item.id`. The zipped `id` is spread last, so it also overrides a same-named column. So adding `"id"` to the backend `list_columns` gives the grid nothing it does not already have, and only surfaces a raw `id` column to the user; `list_obj_schema` is generated `with_id=False`, which is why the raw JSON lacks one and a backend test reading `row["id"]` fails. Such a test should read the response's `ids` array instead of widening the projection.
278
259
  - `quickFilters`: one-click filter presets rendered above the grid (`quickFiltersComponent` swaps the renderer).
279
260
  - `enableAdvancedFilters`: shows the structured filter builder.
280
261
  - `queryOnlyVisibleColumns`: perf, only requests visible columns from the backend.
281
- - `header` / `body` / `form`: per-column customization objects. `header` (keyed by column name): `filterComponent`, `filterProps`, `enableFilterModes` (per-operator component maps), `filterState` (external control), `filterSync`, `withLocalStorage` + `localStorageKey` (persist filter state), `defaultOpr`. `body` (keyed by column name): `truncate`, `component`, `separator` (multi-row cells), `highlight` / `highlightProps`, `tooltipProps`. `form` is keyed by `add` / `edit` / `view` / `delete`, each a JsonForms customizer applied to that auto-generated dialog plus dialog-level keys `initialView` / `dialogProps` (custom inputs: see §9.2).
262
+ - `header` / `body` / `form`: per-column customization objects. `header` (keyed by column name): `filterComponent`, `filterProps`, `enableFilterModes` (per-operator component maps), `filterState` (external control), `filterSync`, `withLocalStorage` + `localStorageKey` (persist filter state), `defaultOpr`. `body` (keyed by column name): `truncate`, `component`, `separator` (multi-row cells), `highlight` / `highlightProps`, `tooltipProps`. `form` is keyed by `add` / `edit` / `view` / `delete`, each a JsonForms customizer applied to that auto-generated dialog plus dialog-level keys `initialView` / `dialogProps` (custom inputs: jsonforms.md §3).
282
263
  - `toolbar`: `customizer` hides (`null`) or replaces the built-in buttons (`toggleGlobalFilter`, `filter`, `refresh`, `add`, `download`, `showHideColumns`, `toggleDensePadding`, `toggleFullScreen`); `leftToolbar` / `centerToolbar` / `custom` add slots; the alert banner is customizable the same way.
283
264
  - Misc: `table` (raw MRT options, merged as overrides on top of the generated ones), `optimizations`, `memo` (`'filters' | 'cells'`), `withBorder`, `wrapperProps`, `fetchOnMount`, `hideToolbar` / `hideActions` / `hidePagination` / `hideError`, `textFilterSeparator`, and a `children` render prop.
284
265
 
@@ -339,165 +320,9 @@ Mounting these dialogs **outside** a `NextGenDataGrid` (e.g. wiring CRUD onto a
339
320
 
340
321
  ## 9. JsonForms for complex schemas
341
322
 
342
- When a form needs nested objects, arrays of subforms, or conditional sections, the built-in dialog form isn't enough; use `JsonFormsWithCustomizer`, which wraps `@jsonforms/react` with Mantine renderers. Input cells cover DateTime, Date, Time, File, Image, Boolean, Enum, Text, Number and full layout/control renderers.
343
-
344
- ```jsx
345
- import { JsonFormsWithCustomizer } from "fastapi-rtk";
346
-
347
- <JsonFormsWithCustomizer
348
- schema={jsonSchema}
349
- uischema={uiSchema}
350
- data={formData}
351
- onChange={({ data, errors }) => setState({ data, errors })}
352
- />;
353
- ```
354
-
355
- For most CRUD forms you don't need this, since the default dialog plus `/_info` schema covers it. Reach for JsonForms when you're rendering user-defined schemas or deeply nested data. Full prop set: `schema`, `uischema`, `data`, `onChange`, `customizer`, `debounce`, `translations`, `hideWarnings`, `createTranslator`, `middleware`.
356
-
357
- **Schema-level `allOf` / `if`/`then`/`else` resolve live against the form data.** `JsonFormsWithCustomizer` installs a JsonForms middleware that resolves schema conditionals on every data change and renders the branch-applied schema: the matching branch's `properties` merge recursively over the base (narrowing one nested field keeps its siblings), scalar keywords win per key (a narrowed `enum` replaces the base list), `required` unions at each level, and the combinator is stripped. Conditionals resolve at the root and inside nested object properties: a nested `if` evaluates against that object's own data slice, while a root-level `if` sees the whole form data and may reference nested paths (and its `then`/`else` may narrow nested fields). Not resolved: `anyOf` / `oneOf` and conditionals inside array `items`. A supplied `uischema` is preserved; with none, the UI schema is regenerated from the resolved schema. Exported for programmatic use: `resolveConditionalSchema(schema, data, ajv)`, `createConditionalMiddleware(schema, { regenerateUischema })`, and `composeMiddleware(outer, inner)`; a caller-supplied `middleware` prop is composed to run underneath the conditional middleware.
358
-
359
- ### 9.1 Per-control props via `options.props` and `options.ifProps`
360
-
361
- Every Mantine-themed control built on `useCommonProps` (the hook backing the toolkit's JsonForms renderers) spreads two extra props blocks from the uischema, in this order:
362
-
363
- 1. `uischema.options.props`: static props passed straight to the underlying component.
364
- 2. `uischema.options.ifProps`: dynamic props chosen by an `if/then/else` schema evaluated against the **whole form data** (not the control's own value).
365
-
366
- `ifProps` only works on control components. Both `then` and `else` are optional; the matching branch's `props` object is spread onto the control, the other is dropped.
367
-
368
- ```js
369
- {
370
- "type": "Control",
371
- "scope": "#/properties/details",
372
- "options": {
373
- "ifProps": {
374
- "if": {
375
- "properties": { "category": { "const": "C" } },
376
- "required": ["category"]
377
- },
378
- "then": { "props": { "required": true } },
379
- "else": { "props": { "disabled": true } }
380
- }
381
- }
382
- }
383
- ```
384
-
385
- **Conditional `required` indicator.** A schema-level `if` / `then.required` produces the conditional asterisk and validation on its own: the resolver merges `then.required` into the live `required` array (at the root or inside a nested object) and JsonForms derives the asterisk from it, updating as the trigger field changes. No `options.ifProps` layer is needed for `required`; keep `ifProps` for non-schema props (`disabled`, `placeholder`, a custom `component`). Rule effects cover only `SHOW` / `HIDE` / `ENABLE` / `DISABLE` (there is no `REQUIRE`), and a static `options.props.required: true` is read unconditionally.
386
-
387
- ```js
388
- // schema (the uischema needs nothing on the dependent field)
389
- {
390
- "type": "object",
391
- "required": ["category"],
392
- "properties": {
393
- "category": { "type": "string", "enum": ["A", "B", "C"] },
394
- "details": { "type": "string" }
395
- },
396
- "if": { "properties": { "category": { "const": "C" } }, "required": ["category"] },
397
- "then": { "required": ["details"] }
398
- }
399
- ```
400
-
401
- When a field is **unconditionally** required, lift it onto top-level `required: [...]`; JsonForms derives the asterisk from the schema, no `options.props.required` needed.
402
-
403
- **Pitfall: always pair `properties` with `required` inside `if`.** A JSON Schema `if` with only `{ "properties": { "x": { "const": "Y" } } }` matches when `x` is absent (an undefined value satisfies the property schema trivially), so `then.props.required: true` fires on first render before the user has touched the selector. Adding `"required": ["x"]` gates the match on "x is set AND equals Y".
404
-
405
- **`ifProps` conditions always evaluate against the form root, not the control's container.** `useIfProps` reads `core.data` from `useJsonForms()`, which is the whole form data, not the sub-slice the control sits in. A conditional control deep inside `#/properties/details/properties/subcategory` must reference siblings by their root path (`properties.details.properties.category`, not `properties.category`). Schema-level conditionals differ by placement: a root-level `allOf[].if` is also root-scoped, while a conditional inside a nested object property evaluates against that object's own data slice.
406
-
407
- Since js 2.0.1, `useIfProps` short-circuits on `null` root data (no values to inspect, no useful `then`), so an `if` never fires against effectively-empty data; matching a _property_ whose value is `null` still works (`if: { properties: { x: { const: null } }, required: ["x"] }`) because the root is still an object.
408
-
409
- ### 9.2 Custom Input components via the `customizer` slot
323
+ When a form needs nested objects, arrays of subforms, or conditional sections, the built-in dialog form isn't enough; use `JsonFormsWithCustomizer`, which wraps `@jsonforms/react` with Mantine renderers. For most CRUD forms you don't need it, since the default dialog plus the `/_info` schema covers them (§8).
410
324
 
411
- To add a custom input, you do **not** register a `withJsonFormsControlProps` renderer + a `rankWith` tester into a renderers array. `JsonFormsWithCustomizer` hardcodes `renderers={mantineRenderers}` and ships all the classic renderers, cells, and testers inside `fastapi-rtk`; you consume that machinery, you don't re-declare it. A custom input is a plain Mantine component bound to a schema field **by key**, through a `{ component, props }` customizer slot.
412
-
413
- **The slot, and the two props every slot factory receives:**
414
-
415
- ```jsx
416
- const customizer = {
417
- [key]: {
418
- component, // ReactNode | ((args) => ReactNode) : REPLACE the input
419
- props, // object | ((args) => object) : MERGE props onto the built-in
420
- },
421
- __: { debounce: true }, // reserved: { debounce, debounceOptions: { delay, flushOnUnmount }, setNull }, not a slot
422
- };
423
- ```
424
-
425
- `args` is always `{ componentProps, jsonFormsProps }`:
426
-
427
- - `componentProps` : the fully computed Mantine props the built-in would have used: `value`, `onChange` (already wired to JsonForms `handleChange(path, value)`), `onBlur`, `label`, `description`, `required`, `disabled`, `error`, plus `uischema.options.props` and `ifProps` (§9.1). Spread it onto a value-based Mantine input and the field is controlled and validated for free.
428
- - `jsonFormsProps` : the raw control props (`data`, `handleChange`, `path`, `schema`, `uischema`, `enabled`, `errors`, `visible`, `id`). Reach here when `componentProps` is not enough, or call `useCommonProps(jsonFormsProps)` (from `fastapi-rtk`) to have the library rebuild the `handleChange(path, value)` wrapper for you.
429
-
430
- `key` is a **renderer name** (`Text`, `Number`, `Integer`, `Boolean`, `Date`, `DateTime`, `Time`, `Enum`, `EnumArray`, `OneOfEnum`, `File`, `Image`, `Json`, `Object`, `Label`, `Table`, `Array`, `Array.Accordion`), a **JsonForms path / field name** (`status`, `details.category`), or `all`. Precedence when several match: `all` < renderer-name < path (slots deep-merge in that order, most specific wins). Since 2.8.0 every structural wrapper is addressable too: `Table.Thead` / `Table.Tbody` / `Table.Empty`, `Table.Header.*` (`Tr`, `Actions`, `Validation`, `Toolbar`, `Wrapper`, `Flex`), `Table.Row.*` (`Tr`, `Td`, `Actions`, `Grid`, `Up`, `Down`, `Remove`), per-row / per-cell addressing (`Table.Row.{index}`, `Table.Row.{index}.{subPath}`, `Table.Row.{subPath}`), `Array.Stack` / `Array.Items` / `Array.Empty` / `Array.Header.*` / `Array.Item.*` / `Array.DeleteDialog`, `File.Box` / `Image.Box`, and `Label.{text}`. (Collision escape: when a real field is named `all`, the wildcard moves to the exported `specialKey.all`.)
431
-
432
- **Write the component** as a plain Mantine component. For a blob field, `JSON.parse` on read and `JSON.stringify` on write; keep spreading `{...componentProps}` so the label / description / error wrapper still renders:
433
-
434
- ```jsx
435
- import { TagsInput } from "@mantine/core";
436
-
437
- function TagsField({ componentProps }) {
438
- const value = componentProps.value ? JSON.parse(componentProps.value) : [];
439
- return (
440
- <TagsInput
441
- {...componentProps}
442
- value={value}
443
- onChange={(next) => componentProps.onChange(JSON.stringify(next))}
444
- />
445
- );
446
- }
447
- ```
448
-
449
- **Register it by key** (no tester, no `{ tester, renderer }`, no `renderers` array). Three equivalent entry points:
450
-
451
- 1. **On a grid's add/edit dialogs** (the common CRUD path) via `NextGenDataGrid`'s `form` prop. `form.add` / `form.edit` are customizer trees keyed by field name; the grid forwards them to its internal `<AddDialog jsonForms>` / `<EditDialog jsonForms>`. Dialog-level keys (`initialView`, `dialogProps`) sit next to the field keys.
452
-
453
- ```jsx
454
- <NextGenDataGrid
455
- form={{
456
- add: {
457
- tags: {
458
- component: ({ componentProps }) => (
459
- <TagsField componentProps={componentProps} />
460
- ),
461
- },
462
- },
463
- edit: {
464
- tags: {
465
- component: ({ componentProps }) => (
466
- <TagsField componentProps={componentProps} />
467
- ),
468
- },
469
- },
470
- }}
471
- />
472
- ```
473
-
474
- 2. **On the standalone dialogs**: `<AddDialog jsonForms={customizer} />` / `<EditDialog jsonForms={customizer} />`.
475
-
476
- 3. **On a raw element** for a bespoke (non-CRUD) form: `<JsonFormsWithCustomizer schema={schema} uischema={uischema} data={data} onChange={onChange} customizer={customizer} />`.
477
-
478
- **`component` swaps the whole input; `props` only tweaks the built-in.** Use `props` to nudge every control of a type, or to derive a prop from the field's own state:
479
-
480
- ```jsx
481
- const customizer = {
482
- Enum: { props: { searchable: true } }, // every Enum control
483
- all: {
484
- props: ({ componentProps }) => ({ withAsterisk: componentProps.required }),
485
- },
486
- };
487
- ```
488
-
489
- A `props` value of `null` explicitly unsets that prop on the built-in; `undefined` is a no-op that leaves the prior value. Function-valued slots receive the fully-merged default props as `componentProps` (since 2.8.0), including a structural slot's computed attributes and children, so an override can wrap or reuse exactly what the built-in would have rendered.
490
-
491
- **Pitfall - a hook-using custom input must be returned as an element, not passed as a component type.** The slot value runs through `parseFromValuesOrFunc`, which _calls_ a function slot inline (`slot({ componentProps, jsonFormsProps })`). Passing the component type directly (`component: MyInput`) therefore executes `MyInput`'s hooks inside the built-in renderer's render pass, a rule-of-hooks violation. Always wrap it in an arrow that returns an element so React mounts it normally:
492
-
493
- ```jsx
494
- const customizer = {
495
- color: { component: (args) => <ColorField {...args} /> }, // arrow returns an element -> hooks safe
496
- // NOT: { color: { component: ColorField } } // called inline -> hooks run in the wrong pass
497
- };
498
- ```
499
-
500
- **Escape hatch - a genuinely new control type** not covered by any built-in renderer: pass a standard `@jsonforms` `renderers` array (your `withJsonFormsControlProps` component + a `rankWith` tester) through the dialog's `jsonFormsProps`, which spreads onto the inner `<JsonForms>` (`<AddDialog jsonFormsProps={{ renderers: [...myRenderers] }} />`). Include the built-in `mantineRenderers` in that array or you lose every default input. TypeScript nit: `jsonFormsProps` is typed `Partial<JsonFormsWithCustomizerProps>`, which omits `renderers`; the runtime spread honors it anyway, so cast to pass it. The renderer-building kit is exported for this path: `mantineRenderers`, `mantineCells`, and the per-type prop hooks (`useContextProps`, `useTextProps`, `useDateProps`, `useFileProps`, `useNumberProps`, ...). Prefer the `customizer` for anything that maps onto an existing input: it keeps the Mantine theme, i18n, debounce, and error wiring.
325
+ **Read `jsonforms.md` for the whole subsystem**: schema-level `allOf` / `if`/`then`/`else` resolution, per-control props via `options.props` and `options.ifProps`, and custom inputs through the `customizer` slot.
501
326
 
502
327
  ## 10. Bulk actions
503
328
 
@@ -648,15 +473,5 @@ Cookie login is on by default on the backend; the JWT endpoint exists only when
648
473
  - Headless fetcher layer for code outside any provider: `buildRequest`, `getItems` / `getItem` / `createItem` / `updateItem` / `deleteItem` / `getInfo`, `formCreateItem` / `formUpdateItem`, `uploadItems` / `downloadItems`, `postBulk`, plus `useAdapters` / `webAdapters`.
649
474
  - Legacy `DataGrid` is still exported alongside `NextGenDataGrid`; standalone `ActionIcons` (`Actions`, `Add`, `Delete`, `Download`, `Edit`, `Refresh`, `Upload`, `View`) and `Filter` compose custom layouts.
650
475
  - Utilities: `deepMerge`, `parseFromValuesOrFunc`, `createSafeContext`, `getItemId`, `urlJoin`, `Memoizer`, `FastAPIRTKLogger`; hooks `useActionIcon`, `useSyncValues`, `useTrueOnce`.
651
- - Router integration targets `react-router` v7.
476
+ - Router integration targets `react-router` v7 or v8 (peer range `^7.5.3 || ^8.0.0`).
652
477
  - Streaming loads render a progress bar instead of a blocking overlay (2.8.0+); toolbar buttons auto-hide when the backend disables the matching feature.
653
-
654
- ## 16. Rules of thumb
655
-
656
- 1. **Never `fetch` a standard resource.** Use `useApi`. Direct fetch calls miss session refresh, error handling, and `/_info`-driven UI hydration.
657
- 2. **Respect the provider hierarchy.** If `useApi` throws "not inside an ApiProvider", wrap the component; don't switch to `useCustomFetch` as a workaround.
658
- 3. **`resource_name` is the contract.** If the backend says `items`, the frontend says `items`. Not `item`, not `/items`, not `Items`.
659
- 4. **Configure the grid; don't wrap it.** The `header` / `body` / `form` props cover almost all customization. Wrapping breaks selection/filter sync.
660
- 5. **Let `/_info` drive UI.** When the backend adds a column or changes permissions, a well-built frontend picks it up for free. Hardcoding column lists is a per-release maintenance tax.
661
- 6. **Mantine layout primitives only.** Use `Stack`, `Group`, `Grid`, `SimpleGrid` for layout, Mantine inputs for one-off forms. Mixing Mantine with another UI kit leads to theme drift.
662
- 7. **CSS import order: Mantine → fastapi-rtk.** Otherwise grid/form styles render oddly because fastapi-rtk CSS depends on Mantine tokens.
@@ -0,0 +1,165 @@
1
+ # JsonForms reference (fastapi-rtk React)
2
+
3
+ Deep reference for `JsonFormsWithCustomizer`: schema-level conditionals, per-control props, and custom inputs through the `customizer` slot. Load this only when a form needs nested objects, arrays of subforms, conditional sections, or a custom input; the default dialog form plus the `/_info` schema covers ordinary CRUD, which is frontend.md §8.
4
+
5
+ ## 1. When to reach for it
6
+
7
+ When a form needs nested objects, arrays of subforms, or conditional sections, the built-in dialog form isn't enough; use `JsonFormsWithCustomizer`, which wraps `@jsonforms/react` with Mantine renderers. Input cells cover DateTime, Date, Time, File, Image, Boolean, Enum, Text, Number and full layout/control renderers.
8
+
9
+ ```jsx
10
+ import { JsonFormsWithCustomizer } from "fastapi-rtk";
11
+
12
+ <JsonFormsWithCustomizer
13
+ schema={jsonSchema}
14
+ uischema={uiSchema}
15
+ data={formData}
16
+ onChange={({ data, errors }) => setState({ data, errors })}
17
+ />;
18
+ ```
19
+
20
+ For most CRUD forms you don't need this, since the default dialog plus `/_info` schema covers it. Reach for JsonForms when you're rendering user-defined schemas or deeply nested data. Full prop set: `schema`, `uischema`, `data`, `onChange`, `customizer`, `debounce`, `translations`, `hideWarnings`, `createTranslator`, `middleware`.
21
+
22
+ **Schema-level `allOf` / `if`/`then`/`else` resolve live against the form data.** `JsonFormsWithCustomizer` installs a JsonForms middleware that resolves schema conditionals on every data change and renders the branch-applied schema: the matching branch's `properties` merge recursively over the base (narrowing one nested field keeps its siblings), scalar keywords win per key (a narrowed `enum` replaces the base list), `required` unions at each level, and the combinator is stripped. Conditionals resolve at the root and inside nested object properties: a nested `if` evaluates against that object's own data slice, while a root-level `if` sees the whole form data and may reference nested paths (and its `then`/`else` may narrow nested fields). Not resolved: `anyOf` / `oneOf` and conditionals inside array `items`. A supplied `uischema` is preserved; with none, the UI schema is regenerated from the resolved schema. Exported for programmatic use: `resolveConditionalSchema(schema, data, ajv)`, `createConditionalMiddleware(schema, { regenerateUischema })`, and `composeMiddleware(outer, inner)`; a caller-supplied `middleware` prop is composed to run underneath the conditional middleware.
23
+
24
+ ## 2. Per-control props via `options.props` and `options.ifProps`
25
+
26
+ Every Mantine-themed control built on `useCommonProps` (the hook backing the toolkit's JsonForms renderers) spreads two extra props blocks from the uischema, in this order:
27
+
28
+ 1. `uischema.options.props`: static props passed straight to the underlying component.
29
+ 2. `uischema.options.ifProps`: dynamic props chosen by an `if/then/else` schema evaluated against the **whole form data** (not the control's own value).
30
+
31
+ `ifProps` only works on control components. Both `then` and `else` are optional; the matching branch's `props` object is spread onto the control, the other is dropped.
32
+
33
+ ```js
34
+ {
35
+ "type": "Control",
36
+ "scope": "#/properties/details",
37
+ "options": {
38
+ "ifProps": {
39
+ "if": {
40
+ "properties": { "category": { "const": "C" } },
41
+ "required": ["category"]
42
+ },
43
+ "then": { "props": { "required": true } },
44
+ "else": { "props": { "disabled": true } }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ **Conditional `required` indicator.** A schema-level `if` / `then.required` produces the conditional asterisk and validation on its own: the resolver merges `then.required` into the live `required` array (at the root or inside a nested object) and JsonForms derives the asterisk from it, updating as the trigger field changes. No `options.ifProps` layer is needed for `required`; keep `ifProps` for non-schema props (`disabled`, `placeholder`, a custom `component`). Rule effects cover only `SHOW` / `HIDE` / `ENABLE` / `DISABLE` (there is no `REQUIRE`), and a static `options.props.required: true` is read unconditionally.
51
+
52
+ ```js
53
+ // schema (the uischema needs nothing on the dependent field)
54
+ {
55
+ "type": "object",
56
+ "required": ["category"],
57
+ "properties": {
58
+ "category": { "type": "string", "enum": ["A", "B", "C"] },
59
+ "details": { "type": "string" }
60
+ },
61
+ "if": { "properties": { "category": { "const": "C" } }, "required": ["category"] },
62
+ "then": { "required": ["details"] }
63
+ }
64
+ ```
65
+
66
+ When a field is **unconditionally** required, lift it onto top-level `required: [...]`; JsonForms derives the asterisk from the schema, no `options.props.required` needed.
67
+
68
+ **Pitfall: always pair `properties` with `required` inside `if`.** A JSON Schema `if` with only `{ "properties": { "x": { "const": "Y" } } }` matches when `x` is absent (an undefined value satisfies the property schema trivially), so `then.props.required: true` fires on first render before the user has touched the selector. Adding `"required": ["x"]` gates the match on "x is set AND equals Y".
69
+
70
+ **`ifProps` conditions always evaluate against the form root, not the control's container.** `useIfProps` reads `core.data` from `useJsonForms()`, which is the whole form data, not the sub-slice the control sits in. A conditional control deep inside `#/properties/details/properties/subcategory` must reference siblings by their root path (`properties.details.properties.category`, not `properties.category`). Schema-level conditionals differ by placement: a root-level `allOf[].if` is also root-scoped, while a conditional inside a nested object property evaluates against that object's own data slice.
71
+
72
+ Since js 2.0.1, `useIfProps` short-circuits on `null` root data (no values to inspect, no useful `then`), so an `if` never fires against effectively-empty data; matching a _property_ whose value is `null` still works (`if: { properties: { x: { const: null } }, required: ["x"] }`) because the root is still an object.
73
+
74
+ ## 3. Custom Input components via the `customizer` slot
75
+
76
+ To add a custom input, you do **not** register a `withJsonFormsControlProps` renderer + a `rankWith` tester into a renderers array. `JsonFormsWithCustomizer` hardcodes `renderers={mantineRenderers}` and ships all the classic renderers, cells, and testers inside `fastapi-rtk`; you consume that machinery, you don't re-declare it. A custom input is a plain Mantine component bound to a schema field **by key**, through a `{ component, props }` customizer slot.
77
+
78
+ **The slot, and the two props every slot factory receives:**
79
+
80
+ ```jsx
81
+ const customizer = {
82
+ [key]: {
83
+ component, // ReactNode | ((args) => ReactNode) : REPLACE the input
84
+ props, // object | ((args) => object) : MERGE props onto the built-in
85
+ },
86
+ __: { debounce: true }, // reserved: { debounce, debounceOptions: { delay, flushOnUnmount }, setNull }, not a slot
87
+ };
88
+ ```
89
+
90
+ `args` is always `{ componentProps, jsonFormsProps }`:
91
+
92
+ - `componentProps` : the fully computed Mantine props the built-in would have used: `value`, `onChange` (already wired to JsonForms `handleChange(path, value)`), `onBlur`, `label`, `description`, `required`, `disabled`, `error`, plus `uischema.options.props` and `ifProps` (§2). Spread it onto a value-based Mantine input and the field is controlled and validated for free.
93
+ - `jsonFormsProps` : the raw control props (`data`, `handleChange`, `path`, `schema`, `uischema`, `enabled`, `errors`, `visible`, `id`). Reach here when `componentProps` is not enough, or call `useCommonProps(jsonFormsProps)` (from `fastapi-rtk`) to have the library rebuild the `handleChange(path, value)` wrapper for you.
94
+
95
+ `key` is a **renderer name** (`Text`, `Number`, `Integer`, `Boolean`, `Date`, `DateTime`, `Time`, `Enum`, `EnumArray`, `OneOfEnum`, `File`, `Image`, `Json`, `Object`, `Label`, `Table`, `Array`, `Array.Accordion`), a **JsonForms path / field name** (`status`, `details.category`), or `all`. Precedence when several match: `all` < renderer-name < path (slots deep-merge in that order, most specific wins). Since 2.8.0 every structural wrapper is addressable too: `Table.Thead` / `Table.Tbody` / `Table.Empty`, `Table.Header.*` (`Tr`, `Actions`, `Validation`, `Toolbar`, `Wrapper`, `Flex`), `Table.Row.*` (`Tr`, `Td`, `Actions`, `Grid`, `Up`, `Down`, `Remove`), per-row / per-cell addressing (`Table.Row.{index}`, `Table.Row.{index}.{subPath}`, `Table.Row.{subPath}`), `Array.Stack` / `Array.Items` / `Array.Empty` / `Array.Header.*` / `Array.Item.*` / `Array.DeleteDialog`, `File.Box` / `Image.Box`, and `Label.{text}`. (Collision escape: when a real field is named `all`, the wildcard moves to the exported `specialKey.all`.)
96
+
97
+ **Write the component** as a plain Mantine component. For a blob field, `JSON.parse` on read and `JSON.stringify` on write; keep spreading `{...componentProps}` so the label / description / error wrapper still renders:
98
+
99
+ ```jsx
100
+ import { TagsInput } from "@mantine/core";
101
+
102
+ function TagsField({ componentProps }) {
103
+ const value = componentProps.value ? JSON.parse(componentProps.value) : [];
104
+ return (
105
+ <TagsInput
106
+ {...componentProps}
107
+ value={value}
108
+ onChange={(next) => componentProps.onChange(JSON.stringify(next))}
109
+ />
110
+ );
111
+ }
112
+ ```
113
+
114
+ **Register it by key** (no tester, no `{ tester, renderer }`, no `renderers` array). Three equivalent entry points:
115
+
116
+ 1. **On a grid's add/edit dialogs** (the common CRUD path) via `NextGenDataGrid`'s `form` prop. `form.add` / `form.edit` are customizer trees keyed by field name; the grid forwards them to its internal `<AddDialog jsonForms>` / `<EditDialog jsonForms>`. Dialog-level keys (`initialView`, `dialogProps`) sit next to the field keys.
117
+
118
+ ```jsx
119
+ <NextGenDataGrid
120
+ form={{
121
+ add: {
122
+ tags: {
123
+ component: ({ componentProps }) => (
124
+ <TagsField componentProps={componentProps} />
125
+ ),
126
+ },
127
+ },
128
+ edit: {
129
+ tags: {
130
+ component: ({ componentProps }) => (
131
+ <TagsField componentProps={componentProps} />
132
+ ),
133
+ },
134
+ },
135
+ }}
136
+ />
137
+ ```
138
+
139
+ 2. **On the standalone dialogs**: `<AddDialog jsonForms={customizer} />` / `<EditDialog jsonForms={customizer} />`.
140
+
141
+ 3. **On a raw element** for a bespoke (non-CRUD) form: `<JsonFormsWithCustomizer schema={schema} uischema={uischema} data={data} onChange={onChange} customizer={customizer} />`.
142
+
143
+ **`component` swaps the whole input; `props` only tweaks the built-in.** Use `props` to nudge every control of a type, or to derive a prop from the field's own state:
144
+
145
+ ```jsx
146
+ const customizer = {
147
+ Enum: { props: { searchable: true } }, // every Enum control
148
+ all: {
149
+ props: ({ componentProps }) => ({ withAsterisk: componentProps.required }),
150
+ },
151
+ };
152
+ ```
153
+
154
+ A `props` value of `null` explicitly unsets that prop on the built-in; `undefined` is a no-op that leaves the prior value. Function-valued slots receive the fully-merged default props as `componentProps` (since 2.8.0), including a structural slot's computed attributes and children, so an override can wrap or reuse exactly what the built-in would have rendered.
155
+
156
+ **Pitfall - a hook-using custom input must be returned as an element, not passed as a component type.** The slot value runs through `parseFromValuesOrFunc`, which _calls_ a function slot inline (`slot({ componentProps, jsonFormsProps })`). Passing the component type directly (`component: MyInput`) therefore executes `MyInput`'s hooks inside the built-in renderer's render pass, a rule-of-hooks violation. Always wrap it in an arrow that returns an element so React mounts it normally:
157
+
158
+ ```jsx
159
+ const customizer = {
160
+ color: { component: (args) => <ColorField {...args} /> }, // arrow returns an element -> hooks safe
161
+ // NOT: { color: { component: ColorField } } // called inline -> hooks run in the wrong pass
162
+ };
163
+ ```
164
+
165
+ **Escape hatch - a genuinely new control type** not covered by any built-in renderer: pass a standard `@jsonforms` `renderers` array (your `withJsonFormsControlProps` component + a `rankWith` tester) through the dialog's `jsonFormsProps`, which spreads onto the inner `<JsonForms>` (`<AddDialog jsonFormsProps={{ renderers: [...myRenderers] }} />`). Include the built-in `mantineRenderers` in that array or you lose every default input. TypeScript nit: `jsonFormsProps` is typed `Partial<JsonFormsWithCustomizerProps>`, which omits `renderers`; the runtime spread honors it anyway, so cast to pass it. The renderer-building kit is exported for this path: `mantineRenderers`, `mantineCells`, and the per-type prop hooks (`useContextProps`, `useTextProps`, `useDateProps`, `useFileProps`, `useNumberProps`, ...). Prefer the `customizer` for anything that maps onto an existing input: it keeps the Mantine theme, i18n, debounce, and error wiring.
@@ -18,11 +18,11 @@ Before 2.0.1, `useIfProps` early-returned only on `data === undefined`. If the r
18
18
  }
19
19
  ```
20
20
 
21
- Fixed in 2.0.1 (`useIfProps` short-circuits on `null` root data); the explicit `type: "object"` gate is unnecessary on current versions (frontend.md §9.1).
21
+ Fixed in 2.0.1 (`useIfProps` short-circuits on `null` root data); the explicit `type: "object"` gate is unnecessary on current versions (jsonforms.md §2).
22
22
 
23
23
  ## Schema-level `allOf` / `if`/`then`/`else` hangs the form (js, fastapi-rtk <= 2.9.4)
24
24
 
25
- Through 2.9.4, `JsonFormsWithCustomizer` passed schema combinators straight to JsonForms. With no `uischema`, `Generate.uiSchema` treats an object schema carrying a combinator as a leaf and emits a `Control` scoped to the object itself; the object control then regenerates the same UI schema for its detail view and recurses until the render loop hangs. Applies to a combinator at the root or on any nested object property. Workaround on those versions: pass an explicit `uischema` whose controls target the leaf properties (never a bare control scoped to the combinator-carrying object), or resolve the conditionals out of the schema before passing it in. Later releases resolve schema conditionals automatically via middleware (frontend.md §9).
25
+ Through 2.9.4, `JsonFormsWithCustomizer` passed schema combinators straight to JsonForms. With no `uischema`, `Generate.uiSchema` treats an object schema carrying a combinator as a leaf and emits a `Control` scoped to the object itself; the object control then regenerates the same UI schema for its detail view and recurses until the render loop hangs. Applies to a combinator at the root or on any nested object property. Workaround on those versions: pass an explicit `uischema` whose controls target the leaf properties (never a bare control scoped to the combinator-carrying object), or resolve the conditionals out of the schema before passing it in. Later releases resolve schema conditionals automatically via middleware (jsonforms.md §1).
26
26
 
27
27
  ## Conditional `required` asterisk needs a two-layer `ifProps` recipe (js, fastapi-rtk <= 2.9.4)
28
28
 
@@ -42,4 +42,4 @@ Without the schema resolver, a schema-level `if` / `then.required` only gates AJ
42
42
  }
43
43
  ```
44
44
 
45
- Later releases derive the conditional asterisk from the schema alone, including inside nested objects (frontend.md §9.1).
45
+ Later releases derive the conditional asterisk from the schema alone, including inside nested objects (jsonforms.md §2).
@@ -0,0 +1,50 @@
1
+ # rtk-developer
2
+
3
+ The FastAPI React Toolkit is ONE framework with two halves designed to fit together. Treat it as a single system, not two independent stacks.
4
+
5
+ ## The core concept
6
+
7
+ The value of fastapi-rtk is convention. Define a SQLAlchemy model, register a `ModelRestApi` with `resource_name="items"`, and the backend exposes full CRUD at `/api/v1/items`. Wrap a component in `<ApiProvider resource_name="items">`, call `useApi()`, and you get `data`, `loading`, `addEntry`, `updateEntry`, `deleteEntry` with no fetch code and no manual types.
8
+
9
+ **The `resource_name` string is the contract, and a typo breaks it silently.**
10
+
11
+ So most features have a backend half and a frontend half that must line up. Work out which side(s) a request touches before writing code:
12
+
13
+ | Feature | Backend piece | Frontend piece |
14
+ | ------------------- | ---------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
15
+ | CRUD on a model | `ModelRestApi` | `ApiProvider` + `useApi` |
16
+ | List/detail views | `list_columns`, `show_columns` | `NextGenDataGrid` auto-columns + `columnProps` |
17
+ | Add/edit forms | `add_columns`, `edit_columns`, hooks | `useForms(kind)` → `setItem` + `setOpened`, `AddDialog`/`EditDialog` |
18
+ | Filtering | `base_filters`, `FilterXxx` | `initialQueryParams`, `quickFilters` (`{name, value:[{col,opr,value}]}`) |
19
+ | File / image upload | `FileColumn`, `ImageColumn`, file manager | form auto-detects and renders file input |
20
+ | Bulk actions | method `bulk_<handler>(body, query, session)` on the Api class | `useBulkActions` → `dispatchBulkActions(handler, ids)` |
21
+ | Permissions | `ROLES` config + `@protect` | `useAuth`, route guards |
22
+ | Custom endpoint | `BaseApi` + `@expose` | `rq` wrapper / `useCustomFetch` (last resort) |
23
+ | Platform adapters | auth endpoints (cookie `auth/login`; JWT needs `AUTH_LOGIN_JWT`) | `adapters` prop on `Provider` (web defaults; RN via `fastapi-rtk/react-native-adapters`) |
24
+
25
+ ## Cross-cutting rules
26
+
27
+ 1. **Never bypass the toolkit.** Backend: prefer `ModelRestApi` over hand-rolled routes and add behavior through `pre_*` / `post_*` hooks. `@expose` is for an endpoint whose logic is NOT CRUD on a model (an RPC-style call, an external-API proxy, a job with no model row); "do something extra when a row is added, edited or fetched" is a hook, not a new route. Frontend: never `fetch` or `axios` a standard resource, and render resource rows with `NextGenDataGrid` configured through its props, not a hand-built `<Table>` or a `.map`. Dropping to raw APIs suits genuinely custom behavior; doing it out of habit fights the framework. An ad-hoc route registered directly on the FastAPI app needs `tags=["<RegisteredApiClass>"]` to appear in `export api-docs` output.
28
+ 2. **Align `resource_name` exactly** on both sides.
29
+ 3. **Expose a different column projection per operation** rather than one model-wide set, and let the grid render from `/_info`. Do not overfetch on the model when the view already filters.
30
+ 4. **Async-first.** Async drivers throughout, hooks are `async def`, and a sync portion mixed into async gets wrapped in `smart_run()`.
31
+ 5. **Relationship filtering happens on the backend**, never by filtering dropdown options in JS. The form picks the backend's answer up automatically.
32
+ 6. **Permissions are declarative**, configured rather than checked inline. Keep ad-hoc role checks out of business logic.
33
+
34
+ ## Which reference to read
35
+
36
+ Keep this file in context and load a reference only for the detail.
37
+
38
+ | Work | Read |
39
+ | --------------------------------------------------------------------------- | -------------------------------------- |
40
+ | Models, APIs, hooks, migrations, users, filters, file managers, bulk, OAuth | `docs/agents/fastapi-rtk/backend.md` |
41
+ | Provider setup, ApiProvider, hooks, DataGrid, forms, dialogs, i18n | `docs/agents/fastapi-rtk/frontend.md` |
42
+ | Nested or conditional schemas, a custom form input, the `customizer` slot | `docs/agents/fastapi-rtk/jsonforms.md` |
43
+ | Full-stack | the table above, then both |
44
+ | Docs look wrong AND the project pins an OLDER release than the stamp below | `docs/agents/fastapi-rtk/legacy.md` |
45
+
46
+ `legacy.md` is keyed by symptom and never loads on a current release.
47
+
48
+ Bootstrapping a new project starts with `fastapi-rtk create-app`, which scaffolds the backend from the fastapi-rtk-skeleton template; the backend reference covers the pieces it produces. In a pre-existing project, read a few of its real files (`app/apis.py`, `src/main.jsx`) rather than assuming a fresh layout, since the toolkit is flexible.
49
+
50
+ **Staleness rule.** The three references were verified claim by claim against fastapi-rtk source at v2.9.0 (2026-07-09). At v2.18.3 (2026-09-16) a narrower re-check re-resolved every `file:line` citation, every config key, every backend symbol named here, the published JS export list and the declared peer dependencies, and fixed the seven that had drifted; prose claims outside those classes still date from v2.9.0. On a NEWER release, treat any claim contradicting observed behavior as possibly stale: read the installed package's source before fighting the code, and fix the reference and its version stamp in the same session when it drifted.