@antelopejs/interface-dms 0.10.4 → 0.13.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 (384) hide show
  1. package/CHANGELOG.md +126 -0
  2. package/README.md +21 -10
  3. package/dist/attachments.d.ts +15 -2
  4. package/dist/attachments.js +14 -1
  5. package/dist/attachments.js.map +1 -1
  6. package/dist/auth/db/models/sessions.model.d.ts +7 -5
  7. package/dist/auth/db/models/sessions.model.js +2 -9
  8. package/dist/auth/db/models/sessions.model.js.map +1 -1
  9. package/dist/auth/db/models/userExternalIdentities.model.d.ts +7 -5
  10. package/dist/auth/db/models/users.model.d.ts +7 -5
  11. package/dist/auth/db/tables/users.table.d.ts +2 -2
  12. package/dist/auth/db/tables/users.table.js.map +1 -1
  13. package/dist/auth/index.d.ts +4 -4
  14. package/dist/auth/internal/jwt-auth-decorator.d.ts +1 -1
  15. package/dist/auth/internal/jwt-auth-decorator.js +6 -3
  16. package/dist/auth/internal/jwt-auth-decorator.js.map +1 -1
  17. package/dist/auth/internal/request-authenticators.d.ts +12 -0
  18. package/dist/auth/internal/request-authenticators.js +53 -10
  19. package/dist/auth/internal/request-authenticators.js.map +1 -1
  20. package/dist/base/activity-feed.d.ts +34 -32
  21. package/dist/base/activity-feed.js +2 -0
  22. package/dist/base/activity-feed.js.map +1 -1
  23. package/dist/base/banner.d.ts +12 -10
  24. package/dist/base/banner.js +2 -0
  25. package/dist/base/banner.js.map +1 -1
  26. package/dist/base/block-registry/internal/describe.js +4 -12
  27. package/dist/base/block-registry/internal/describe.js.map +1 -1
  28. package/dist/base/block-registry/internal/ui.js +1 -1
  29. package/dist/base/block-registry/internal/ui.js.map +1 -1
  30. package/dist/base/card.d.ts +11 -9
  31. package/dist/base/card.js +2 -0
  32. package/dist/base/card.js.map +1 -1
  33. package/dist/base/chart-card.d.ts +4 -2
  34. package/dist/base/chart-card.js +2 -0
  35. package/dist/base/chart-card.js.map +1 -1
  36. package/dist/base/chart-schemas.d.ts +148 -148
  37. package/dist/base/chart.d.ts +65 -0
  38. package/dist/base/chart.js +65 -0
  39. package/dist/base/chart.js.map +1 -1
  40. package/dist/base/code-block.d.ts +5 -3
  41. package/dist/base/code-block.js +2 -0
  42. package/dist/base/code-block.js.map +1 -1
  43. package/dist/base/custom.d.ts +5 -0
  44. package/dist/base/custom.js +5 -0
  45. package/dist/base/custom.js.map +1 -1
  46. package/dist/base/data-types/default/address.d.ts +5 -0
  47. package/dist/base/data-types/default/address.js +5 -0
  48. package/dist/base/data-types/default/address.js.map +1 -1
  49. package/dist/base/data-types/default/choices.d.ts +20 -0
  50. package/dist/base/data-types/default/choices.js +20 -0
  51. package/dist/base/data-types/default/choices.js.map +1 -1
  52. package/dist/base/data-types/default/files.d.ts +22 -5
  53. package/dist/base/data-types/default/files.js +20 -0
  54. package/dist/base/data-types/default/files.js.map +1 -1
  55. package/dist/base/data-types/default/numbers.d.ts +15 -0
  56. package/dist/base/data-types/default/numbers.js +25 -9
  57. package/dist/base/data-types/default/numbers.js.map +1 -1
  58. package/dist/base/data-types/default/relations.d.ts +10 -2
  59. package/dist/base/data-types/default/relations.js +9 -6
  60. package/dist/base/data-types/default/relations.js.map +1 -1
  61. package/dist/base/data-types/default/strings.d.ts +25 -0
  62. package/dist/base/data-types/default/strings.js +25 -0
  63. package/dist/base/data-types/default/strings.js.map +1 -1
  64. package/dist/base/data-types/default-types.d.ts +1 -1
  65. package/dist/base/data-types/field-types.d.ts +10 -0
  66. package/dist/base/data-types/field-types.js +10 -0
  67. package/dist/base/data-types/field-types.js.map +1 -1
  68. package/dist/base/data-types/internal/relation-endpoints.js +1 -0
  69. package/dist/base/data-types/internal/relation-endpoints.js.map +1 -1
  70. package/dist/base/data-types/status-type.d.ts +5 -0
  71. package/dist/base/data-types/status-type.js +5 -0
  72. package/dist/base/data-types/status-type.js.map +1 -1
  73. package/dist/base/display.d.ts +6 -6
  74. package/dist/base/empty-state.d.ts +15 -13
  75. package/dist/base/empty-state.js +2 -0
  76. package/dist/base/empty-state.js.map +1 -1
  77. package/dist/base/form-block-schema.d.ts +50 -50
  78. package/dist/base/form-schema.d.ts +2 -0
  79. package/dist/base/form-schema.js +2 -0
  80. package/dist/base/form-schema.js.map +1 -1
  81. package/dist/base/grid.d.ts +4 -0
  82. package/dist/base/grid.js +4 -0
  83. package/dist/base/grid.js.map +1 -1
  84. package/dist/base/guard.d.ts +30 -28
  85. package/dist/base/guard.js +2 -0
  86. package/dist/base/guard.js.map +1 -1
  87. package/dist/base/helpers/archive-mode-helpers.d.ts +1 -1
  88. package/dist/base/helpers/file-refs.js +15 -15
  89. package/dist/base/helpers/file-refs.js.map +1 -1
  90. package/dist/base/internal/form-schema-validation.js +7 -1
  91. package/dist/base/internal/form-schema-validation.js.map +1 -1
  92. package/dist/base/internal/realtime-topics.js.map +1 -1
  93. package/dist/base/internal/searchable.d.ts +2 -1
  94. package/dist/base/internal/searchable.js +1 -3
  95. package/dist/base/internal/searchable.js.map +1 -1
  96. package/dist/base/key-value-list.d.ts +21 -19
  97. package/dist/base/key-value-list.js +2 -0
  98. package/dist/base/key-value-list.js.map +1 -1
  99. package/dist/base/kpi-card.d.ts +4 -2
  100. package/dist/base/kpi-card.js +2 -0
  101. package/dist/base/kpi-card.js.map +1 -1
  102. package/dist/base/live-toggle.d.ts +2 -0
  103. package/dist/base/live-toggle.js +2 -0
  104. package/dist/base/live-toggle.js.map +1 -1
  105. package/dist/base/meter.d.ts +23 -21
  106. package/dist/base/meter.js +2 -0
  107. package/dist/base/meter.js.map +1 -1
  108. package/dist/base/nav-card-grid.d.ts +24 -19
  109. package/dist/base/nav-card-grid.js +5 -0
  110. package/dist/base/nav-card-grid.js.map +1 -1
  111. package/dist/base/page-toolbar.d.ts +2 -0
  112. package/dist/base/page-toolbar.js +2 -0
  113. package/dist/base/page-toolbar.js.map +1 -1
  114. package/dist/base/period-selector.d.ts +3 -1
  115. package/dist/base/period-selector.js +2 -0
  116. package/dist/base/period-selector.js.map +1 -1
  117. package/dist/base/placeholder.d.ts +2 -0
  118. package/dist/base/placeholder.js +2 -0
  119. package/dist/base/placeholder.js.map +1 -1
  120. package/dist/base/resource-form-schema.d.ts +2 -2
  121. package/dist/base/searchable.d.ts +3 -2
  122. package/dist/base/searchable.js +2 -2
  123. package/dist/base/searchable.js.map +1 -1
  124. package/dist/base/section.d.ts +6 -2
  125. package/dist/base/section.js +4 -0
  126. package/dist/base/section.js.map +1 -1
  127. package/dist/base/stack.d.ts +6 -0
  128. package/dist/base/stack.js +6 -0
  129. package/dist/base/stack.js.map +1 -1
  130. package/dist/base/stat-group.d.ts +16 -14
  131. package/dist/base/stat-group.js +2 -0
  132. package/dist/base/stat-group.js.map +1 -1
  133. package/dist/base/tab.d.ts +10 -8
  134. package/dist/base/tab.js +2 -0
  135. package/dist/base/tab.js.map +1 -1
  136. package/dist/base/table-view/column-display.d.ts +1 -1
  137. package/dist/base/table-view/column-display.js.map +1 -1
  138. package/dist/base/table-view/data-functions.d.ts +10 -4
  139. package/dist/base/table-view/data-functions.js.map +1 -1
  140. package/dist/base/table-view/factory.d.ts +5 -0
  141. package/dist/base/table-view/factory.js +8 -4
  142. package/dist/base/table-view/factory.js.map +1 -1
  143. package/dist/base/table-view/internal/auth.d.ts +17 -3
  144. package/dist/base/table-view/internal/auth.js +26 -5
  145. package/dist/base/table-view/internal/auth.js.map +1 -1
  146. package/dist/base/table-view/internal/column-conditions.d.ts +8 -1
  147. package/dist/base/table-view/internal/column-conditions.js +25 -13
  148. package/dist/base/table-view/internal/column-conditions.js.map +1 -1
  149. package/dist/base/table-view/internal/column-display.d.ts +1 -2
  150. package/dist/base/table-view/internal/column-display.js +3 -9
  151. package/dist/base/table-view/internal/column-display.js.map +1 -1
  152. package/dist/base/table-view/internal/factory-helpers.d.ts +1 -1
  153. package/dist/base/table-view/internal/factory-helpers.js +17 -14
  154. package/dist/base/table-view/internal/factory-helpers.js.map +1 -1
  155. package/dist/base/table-view/internal/factory-lifecycle-created.d.ts +2 -3
  156. package/dist/base/table-view/internal/factory-lifecycle-created.js +5 -5
  157. package/dist/base/table-view/internal/factory-lifecycle-created.js.map +1 -1
  158. package/dist/base/table-view/internal/factory-lifecycle-filter.d.ts +2 -2
  159. package/dist/base/table-view/internal/factory-private-actions.d.ts +2 -2
  160. package/dist/base/table-view/internal/files.d.ts +2 -1
  161. package/dist/base/table-view/internal/files.js +14 -5
  162. package/dist/base/table-view/internal/files.js.map +1 -1
  163. package/dist/base/table-view/internal/footer.d.ts +3 -3
  164. package/dist/base/table-view/internal/footer.js +16 -15
  165. package/dist/base/table-view/internal/footer.js.map +1 -1
  166. package/dist/base/table-view/internal/guards.d.ts +27 -5
  167. package/dist/base/table-view/internal/guards.js +133 -49
  168. package/dist/base/table-view/internal/guards.js.map +1 -1
  169. package/dist/base/table-view/internal/meta-helpers.d.ts +1 -1
  170. package/dist/base/table-view/internal/meta-helpers.js +3 -4
  171. package/dist/base/table-view/internal/meta-helpers.js.map +1 -1
  172. package/dist/base/table-view/internal/meta.js +2 -3
  173. package/dist/base/table-view/internal/meta.js.map +1 -1
  174. package/dist/base/table-view/internal/option-contract.d.ts +9 -1
  175. package/dist/base/table-view/internal/option-footer.d.ts +2 -2
  176. package/dist/base/table-view/internal/option-row-actions.d.ts +1 -1
  177. package/dist/base/table-view/internal/realtime.d.ts +52 -11
  178. package/dist/base/table-view/internal/realtime.js +23 -16
  179. package/dist/base/table-view/internal/realtime.js.map +1 -1
  180. package/dist/base/table-view/internal/resource-form.d.ts +1 -1
  181. package/dist/base/table-view/internal/resource-form.js +1 -1
  182. package/dist/base/table-view/internal/resource-form.js.map +1 -1
  183. package/dist/base/table-view/internal/row-actions-schema.d.ts +12 -12
  184. package/dist/base/table-view/internal/row-rule-shape.d.ts +11 -5
  185. package/dist/base/table-view/internal/row-rule-shape.js +60 -30
  186. package/dist/base/table-view/internal/row-rule-shape.js.map +1 -1
  187. package/dist/base/table-view/internal/row-rules.d.ts +28 -1
  188. package/dist/base/table-view/internal/row-rules.js +8 -19
  189. package/dist/base/table-view/internal/row-rules.js.map +1 -1
  190. package/dist/base/table-view/internal/tabs.d.ts +1 -1
  191. package/dist/base/table-view/internal/tabs.js +1 -4
  192. package/dist/base/table-view/internal/tabs.js.map +1 -1
  193. package/dist/base/table-view/internal/validation.d.ts +2 -2
  194. package/dist/base/table-view/internal/validation.js +7 -7
  195. package/dist/base/table-view/internal/validation.js.map +1 -1
  196. package/dist/base/table-view/meta.d.ts +7 -6
  197. package/dist/base/table-view/meta.js +12 -7
  198. package/dist/base/table-view/meta.js.map +1 -1
  199. package/dist/base/table-view/resource-form.d.ts +3 -1
  200. package/dist/base/table-view/resource-form.js +2 -0
  201. package/dist/base/table-view/resource-form.js.map +1 -1
  202. package/dist/base/table-view/routes.d.ts +73 -21
  203. package/dist/base/table-view/routes.js +13 -10
  204. package/dist/base/table-view/routes.js.map +1 -1
  205. package/dist/base/table-view/schema.d.ts +157 -152
  206. package/dist/base/table-view/schema.js +2 -1
  207. package/dist/base/table-view/schema.js.map +1 -1
  208. package/dist/base/table-view/source.js +4 -1
  209. package/dist/base/table-view/source.js.map +1 -1
  210. package/dist/base/table-view.d.ts +1 -1
  211. package/dist/base/table-view.js.map +1 -1
  212. package/dist/base/top-list-card.d.ts +35 -33
  213. package/dist/base/top-list-card.js +2 -0
  214. package/dist/base/top-list-card.js.map +1 -1
  215. package/dist/base/types/error.js +5 -2
  216. package/dist/base/types/error.js.map +1 -1
  217. package/dist/base/types/guards.d.ts +4 -4
  218. package/dist/base/types/json.d.ts +1 -10
  219. package/dist/base/types/row-action.d.ts +7 -7
  220. package/dist/component-options/confirm-dialog.d.ts +2 -0
  221. package/dist/component-options/confirm-dialog.js.map +1 -1
  222. package/dist/component-options/data-type.d.ts +2 -2
  223. package/dist/component-options/data-type.js +7 -7
  224. package/dist/component-options/data-type.js.map +1 -1
  225. package/dist/component-options/page-layout.d.ts +3 -2
  226. package/dist/component-options/page-layout.js.map +1 -1
  227. package/dist/component-options/row-action.d.ts +14 -9
  228. package/dist/component-slots.d.ts +11 -2
  229. package/dist/component-slots.js +1 -0
  230. package/dist/component-slots.js.map +1 -1
  231. package/dist/component.d.ts +7 -8
  232. package/dist/component.js +17 -25
  233. package/dist/component.js.map +1 -1
  234. package/dist/data-controllers/members.d.ts +11 -7
  235. package/dist/data-controllers/members.js +1 -1
  236. package/dist/data-controllers/members.js.map +1 -1
  237. package/dist/data-controllers/roles.d.ts +32 -10
  238. package/dist/db/internal/exportJobs.model.d.ts +7 -5
  239. package/dist/db/internal/inviteResolutions.model.d.ts +7 -5
  240. package/dist/db/internal/tenantLifecycle.model.d.ts +15 -7
  241. package/dist/db/internal/tenantLifecycle.model.js +16 -7
  242. package/dist/db/internal/tenantLifecycle.model.js.map +1 -1
  243. package/dist/db/models/roles.model.d.ts +7 -5
  244. package/dist/db/models/tenantMembers.model.d.ts +7 -5
  245. package/dist/db/models/tenants.model.d.ts +7 -5
  246. package/dist/db/models/user_invites.model.d.ts +7 -5
  247. package/dist/guards.d.ts +1 -1
  248. package/dist/hooks.d.ts +1 -2
  249. package/dist/hooks.js +33 -26
  250. package/dist/hooks.js.map +1 -1
  251. package/dist/internal/attachments.d.ts +2 -2
  252. package/dist/internal/component-builder.js +2 -1
  253. package/dist/internal/component-builder.js.map +1 -1
  254. package/dist/internal/component-serialization.d.ts +2 -4
  255. package/dist/internal/component-serialization.js +4 -3
  256. package/dist/internal/component-serialization.js.map +1 -1
  257. package/dist/internal/component-slots.d.ts +19 -2
  258. package/dist/internal/component-slots.js +35 -12
  259. package/dist/internal/component-slots.js.map +1 -1
  260. package/dist/internal/component-watch-filter.d.ts +11 -0
  261. package/dist/internal/component-watch-filter.js +22 -0
  262. package/dist/internal/component-watch-filter.js.map +1 -0
  263. package/dist/internal/component-watches.js +0 -1
  264. package/dist/internal/component-watches.js.map +1 -1
  265. package/dist/internal/own-entry.d.ts +7 -0
  266. package/dist/internal/own-entry.js +13 -0
  267. package/dist/internal/own-entry.js.map +1 -0
  268. package/dist/internal/uploads.d.ts +30 -0
  269. package/dist/internal/uploads.js +19 -0
  270. package/dist/internal/uploads.js.map +1 -1
  271. package/dist/invite-extension-payloads.d.ts +14 -2
  272. package/dist/invite-extensions/internal/delivery.js +1 -2
  273. package/dist/invite-extensions/internal/delivery.js.map +1 -1
  274. package/dist/invite-extensions/internal/edit.d.ts +2 -1
  275. package/dist/invite-extensions/internal/edit.js +8 -9
  276. package/dist/invite-extensions/internal/edit.js.map +1 -1
  277. package/dist/invite-extensions/internal/form-slot.js +8 -15
  278. package/dist/invite-extensions/internal/form-slot.js.map +1 -1
  279. package/dist/invite-extensions/internal/payload-slices.d.ts +33 -3
  280. package/dist/invite-extensions/internal/payload-slices.js +84 -19
  281. package/dist/invite-extensions/internal/payload-slices.js.map +1 -1
  282. package/dist/invite-extensions/internal/types.d.ts +10 -1
  283. package/dist/invite-resolution.js +6 -4
  284. package/dist/invite-resolution.js.map +1 -1
  285. package/dist/json.d.ts +10 -0
  286. package/dist/json.js +3 -0
  287. package/dist/json.js.map +1 -0
  288. package/dist/layout-banners.d.ts +5 -2
  289. package/dist/layout-banners.js.map +1 -1
  290. package/dist/page/controllers.d.ts +2 -2
  291. package/dist/page/internal/extension-validation.js +3 -2
  292. package/dist/page/internal/extension-validation.js.map +1 -1
  293. package/dist/page/internal/metadata-layout.js +1 -1
  294. package/dist/page/internal/metadata-layout.js.map +1 -1
  295. package/dist/page/internal/metadata-publish.js +3 -2
  296. package/dist/page/internal/metadata-publish.js.map +1 -1
  297. package/dist/page/internal/metadata-registration.js +5 -4
  298. package/dist/page/internal/metadata-registration.js.map +1 -1
  299. package/dist/page/internal/metadata-upload-registration.d.ts +4 -2
  300. package/dist/page/internal/metadata-upload-registration.js +13 -4
  301. package/dist/page/internal/metadata-upload-registration.js.map +1 -1
  302. package/dist/page/internal/metadata-uploads.d.ts +5 -5
  303. package/dist/page/internal/metadata-uploads.js +4 -8
  304. package/dist/page/internal/metadata-uploads.js.map +1 -1
  305. package/dist/page/internal/page-record.d.ts +2 -3
  306. package/dist/page/internal/page-record.js +23 -16
  307. package/dist/page/internal/page-record.js.map +1 -1
  308. package/dist/page/layout-filter.js +17 -6
  309. package/dist/page/layout-filter.js.map +1 -1
  310. package/dist/page/metadata.js +5 -7
  311. package/dist/page/metadata.js.map +1 -1
  312. package/dist/request-tenant.d.ts +10 -4
  313. package/dist/request-tenant.js +16 -34
  314. package/dist/request-tenant.js.map +1 -1
  315. package/dist/tenant-export.d.ts +2 -1
  316. package/dist/tenant-lifecycle.d.ts +43 -4
  317. package/dist/tenant-lifecycle.js +97 -18
  318. package/dist/tenant-lifecycle.js.map +1 -1
  319. package/dist/types.d.ts +20 -0
  320. package/dist/uploads.d.ts +1 -1
  321. package/dist/uploads.js +39 -60
  322. package/dist/uploads.js.map +1 -1
  323. package/dist/utils/fire-and-forget.js.map +1 -1
  324. package/dist/utils/internal/decorated-class.d.ts +14 -0
  325. package/dist/utils/internal/decorated-class.js +37 -0
  326. package/dist/utils/internal/decorated-class.js.map +1 -0
  327. package/dist/utils/internal/type-check.d.ts +9 -0
  328. package/dist/utils/internal/type-check.js +13 -5
  329. package/dist/utils/internal/type-check.js.map +1 -1
  330. package/dist/utils/internal/value-parser.d.ts +1 -1
  331. package/dist/utils/internal/value-parser.js +22 -9
  332. package/dist/utils/internal/value-parser.js.map +1 -1
  333. package/dist/watch-actions.d.ts +1 -1
  334. package/docs/1.core/1.introduction.md +82 -77
  335. package/docs/1.core/10.layout-banners.md +57 -45
  336. package/docs/1.core/11.modules-and-dynamic-menus.md +178 -0
  337. package/docs/1.core/12.frontend-modules-and-request-helpers.md +155 -0
  338. package/docs/1.core/13.tenant-data-export.md +120 -0
  339. package/docs/1.core/2.pages-and-modules.md +140 -367
  340. package/docs/1.core/3.components.md +21 -22
  341. package/docs/1.core/4.permissions.md +92 -71
  342. package/docs/1.core/5.tenancy.md +150 -86
  343. package/docs/1.core/6.invites.md +90 -65
  344. package/docs/1.core/7.hooks.md +96 -166
  345. package/docs/1.core/8.realtime.md +44 -45
  346. package/docs/1.core/9.quick-actions.md +37 -30
  347. package/docs/2.auth/1.introduction.md +33 -35
  348. package/docs/2.auth/2.authentication.md +60 -38
  349. package/docs/2.auth/3.tokens-and-sessions.md +56 -55
  350. package/docs/2.auth/4.users.md +79 -51
  351. package/docs/2.auth/5.external-identities.md +25 -26
  352. package/docs/3.base/1.introduction.md +64 -60
  353. package/docs/3.base/10.display-blocks.md +15 -16
  354. package/docs/3.base/11.page-layouts.md +270 -0
  355. package/docs/3.base/12.table-row-actions.md +122 -0
  356. package/docs/3.base/13.table-filters-and-forms.md +104 -0
  357. package/docs/3.base/14.table-events-and-helpers.md +114 -0
  358. package/docs/3.base/15.table-sources-and-gates.md +24 -0
  359. package/docs/3.base/2.types.md +44 -41
  360. package/docs/3.base/3.forms.md +27 -28
  361. package/docs/3.base/4.tables.md +35 -368
  362. package/docs/3.base/5.charts-and-widgets.md +28 -29
  363. package/docs/3.base/6.layout.md +21 -270
  364. package/docs/3.base/8.data-types.md +39 -40
  365. package/docs/3.base/9.helpers.md +36 -38
  366. package/docs/4.notifications/1.introduction.md +22 -26
  367. package/docs/4.notifications/2.categories-and-subjects.md +58 -55
  368. package/docs/4.notifications/3.sending.md +72 -68
  369. package/docs/5.html-render/1.introduction.md +119 -81
  370. package/docs/5.html-render/2.built-in-emails.md +71 -51
  371. package/docs/index.md +18 -29
  372. package/docs/tsconfig.json +4 -0
  373. package/package.json +11 -11
  374. package/docs/images/emails-admin-invite.png +0 -0
  375. package/docs/images/emails-change-notice.png +0 -0
  376. package/docs/images/emails-change-verification.png +0 -0
  377. package/docs/images/emails-digest-logo-dark.png +0 -0
  378. package/docs/images/emails-digest-logo-light.png +0 -0
  379. package/docs/images/emails-digest.png +0 -0
  380. package/docs/images/emails-export-partial.png +0 -0
  381. package/docs/images/emails-export-ready.png +0 -0
  382. package/docs/images/emails-reset-password.png +0 -0
  383. package/docs/images/emails-two-factor.png +0 -0
  384. package/docs/images/emails-user-validation.png +0 -0
@@ -1,13 +1,12 @@
1
1
  ---
2
2
  title: "Users"
3
- description: "The `User` table is the identity record every other part of this interface points at: the authentication decorators resolve a request to one, the token generators sign for one, and `Session` rows reference one."
3
+ description: "The `User` table and `UserModel`, password hashing and policy, `sanitizeUser`, the account mail functions, and `announceRegistration`."
4
+ mode: reference
4
5
  ---
5
6
 
6
- # Users
7
-
8
7
  ## Overview
9
8
 
10
- The `User` table is the identity record every other part of this interface points at: the authentication decorators resolve a request to one, the token generators sign for one, and `Session` rows reference one. The table, its constant and its model live on the `/db` subpath; the sanitizer and the mail entry points live on the root subpath.
9
+ The `User` table is the identity record the rest of this interface points at. The authentication decorators resolve a request to a user, the token generators sign for one, and `Session` rows reference one. The table, its constant and its model live in `@antelopejs/interface-dms/auth/db`. The sanitizer and the mail functions live in `@antelopejs/interface-dms/auth`.
11
10
 
12
11
  ```typescript
13
12
  import { sanitizeUser } from "@antelopejs/interface-dms/auth";
@@ -18,9 +17,9 @@ import {
18
17
  } from "@antelopejs/interface-dms/auth/db";
19
18
  ```
20
19
 
21
- ## The User Table
20
+ ## The user table
22
21
 
23
- `User` extends `Table.with(HashModifier)` and is registered as the `users` table under the DMS core schema, so its rows are shared across tenants and its model is retrieved without a tenant instance. `USERS_TABLE_NAME` holds the table name, `"users"`.
22
+ `User` extends `Table.with(HashModifier)` and is registered as the `users` table in the DMS core schema. Its rows are shared across tenants, and you obtain its model without a tenant instance. `USERS_TABLE_NAME` holds the table name, `"users"`.
24
23
 
25
24
  | Field | Type | Description |
26
25
  | ----- | ---- | ----------- |
@@ -29,32 +28,45 @@ import {
29
28
  | `updatedAt` | `Date` | Set on write by the update-time decorator. Indexed. |
30
29
  | `email` | `string` | Login identifier. Indexed. |
31
30
  | `name` | `string` | Display name. |
32
- | `avatar` | `DefaultDataTypes.ImageValue \| null` | Profile picture as an image value (`{ key, alt? }`) in the default storage, downscaled client-side to a square of at most 100×100 pixels. |
31
+ | `avatar` | `ImageValue \| null` | Profile picture as an image value (`{ key, alt? }`) in the default storage, downscaled in the browser to a square of at most 100 by 100 pixels. |
33
32
  | `language` | `string` | Preferred language or locale, for example `"en"` or `"fr"`. Indexed. |
34
- | `password` | `string \| null` | Hashed by the `HashModifier`; reads back as the hash, not the plaintext. `null` for accounts with no password. |
35
- | `authKey` | `string` | Per-user secret that takes part in JWT signing. Rotating it invalidates every token already issued to the user. |
33
+ | `timeZone` | `string \| null`, optional | IANA time zone that dates and times are shown in, for example `"Europe/Brussels"`. |
34
+ | `weekStart` | `UserWeekStart \| null`, optional | First day of the week in calendars and date pickers: `0` Sunday, `1` Monday, `6` Saturday. |
35
+ | `timeFormat` | `UserTimeFormat \| null`, optional | Clock used for times: `"h23"` for a 24-hour clock, `"h12"` for a 12-hour clock. |
36
+ | `dateFormat` | `UserDateFormat \| null`, optional | How full dates are written: `"numeric"` (01/10/2026) or `"text"` (1 Oct 2026). |
37
+ | `password` | `string \| null` | Hashed by `HashModifier`. Reads back as the hash, not the plaintext. `null` for accounts with no password. |
38
+ | `passwordChangedAt` | `Date \| null` | When the user last set the password, by a change or a recovery. `null` for an account that never changed it since the field was added. |
39
+ | `authKey` | `string` | Per-user secret that is part of the JWT signing secret. Rotating it invalidates every token already issued to the user. |
36
40
  | `isValidated` | `boolean` | Whether the email address has been validated. Indexed. |
37
41
  | `validationToken` | `string \| null` | Current email-validation code. Indexed. |
38
42
  | `validationRequestedAt` | `Date \| null` | When the validation code was issued, for expiry and rate limiting. |
39
43
  | `forgotPasswordToken` | `string \| null` | Current password-reset code. Indexed. |
40
44
  | `forgotPasswordRequestedAt` | `Date \| null` | When the reset code was issued, for expiry and rate limiting. |
41
- | `owner` | `boolean` | Platform owner flag. This is what `AuthOwnerOnly` checks — see [Authentication](/docs/interfaces/auth/authentication). |
45
+ | `owner` | `boolean` | Platform owner flag. `AuthOwnerOnly` checks it. See [Authentication](/docs/interfaces/auth/authentication). |
46
+ | `lastActiveAt` | `Date \| null` | The last time the user made a request with a valid access token. The DMS writes it at most once a minute, so it is accurate to that interval. `null` until the first authenticated request. |
42
47
  | `twoFactorMethods` | `string[]` | Second factors enabled on the account. |
43
48
  | `twoFactorSecret` | `string \| null` | Active TOTP secret. |
44
- | `twoFactorPendingSecret` | `string \| null` | TOTP secret awaiting confirmation during enrolment. |
45
- | `twoFactorTotpLastStep` | `number \| null` | 30-second time step of the last TOTP code accepted; a code from that step or an earlier one is refused, so each code works once. Unset until one is accepted. |
49
+ | `twoFactorPendingSecret` | `string \| null` | TOTP secret that awaits confirmation during enrollment. |
50
+ | `twoFactorTotpLastStep` | `number \| null`, optional | The 30-second time step of the last TOTP code accepted. A code from that step or an earlier one is refused, so each code works once. Unset until a code is accepted. |
46
51
  | `twoFactorBackupCodes` | `string[]` | Remaining single-use backup codes. |
47
- | `twoFactorEmailCode` | `string \| null` | Hash of the current emailed second-factor code — hex SHA-256 of `JSON.stringify([userId, code])` — never the code itself. |
52
+ | `twoFactorBackupCodesGeneratedAt` | `Date \| null` | When the current set of backup codes was generated. |
53
+ | `twoFactorBackupCodesSavedAt` | `Date \| null` | When the user confirmed they kept the current backup codes, by download, copy or acknowledgment. `null` until then. |
54
+ | `twoFactorEmailCode` | `string \| null` | Hash of the current emailed second-factor code, never the code itself. The hash is the hex SHA-256 of `JSON.stringify([userId, code])`. |
48
55
  | `twoFactorEmailCodeRequestedAt` | `Date \| null` | When the emailed code was issued, for expiry and rate limiting. |
49
56
 
50
- The indexed fields — `createdAt`, `updatedAt`, `email`, `language`, `isValidated`, `validationToken`, `forgotPasswordToken` — are the ones you can look up through `getAll` on the underlying table or `getBy` on the model, which is how `UserModel.getByEmail` works.
57
+ `UserWeekStart`, `UserTimeFormat` and `UserDateFormat` are exported next to `User`. `ImageValue` comes from `@antelopejs/interface-dms/types`.
58
+
59
+ The indexed fields are `createdAt`, `updatedAt`, `email`, `language`, `isValidated`, `validationToken` and `forgotPasswordToken`. You can look a user up by these fields through `getAll` on the underlying table or `getBy` on the model. `UserModel.getByEmail` works this way.
51
60
 
52
- ## Store and Check a Password
61
+ ## Store and check a password
53
62
 
54
- `password` carries the `@Hashed` decorator, and the table composes `HashModifier`. Assign a plaintext value on write and it is hashed on the way into the database; read it back and you get the hash, not the original.
63
+ `password` carries the `@Hashed` decorator, and the table composes `HashModifier`. Assign a plaintext value on write, and the modifier hashes it on the way into the database. Read it back, and you get the hash.
55
64
 
56
65
  ```typescript
57
- import type { UserModel } from "@antelopejs/interface-dms/auth/db";
66
+ import {
67
+ normalizeEmail,
68
+ type UserModel,
69
+ } from "@antelopejs/interface-dms/auth/db";
58
70
  import randomstring from "randomstring";
59
71
 
60
72
  const AUTH_KEY_LENGTH = 64;
@@ -69,7 +81,7 @@ export async function createUser(
69
81
  createdAt: new Date(),
70
82
  updatedAt: new Date(),
71
83
  name,
72
- email: email.toLowerCase(),
84
+ email: normalizeEmail(email),
73
85
  password, // hashed by the modifier on insert
74
86
  authKey: randomstring.generate({ length: AUTH_KEY_LENGTH }),
75
87
  owner: false,
@@ -79,7 +91,7 @@ export async function createUser(
79
91
  }
80
92
  ```
81
93
 
82
- To check a candidate password, call `testHashAsync` on the loaded row. The mixin hashes the candidate with the salt and the cost in the stored hash, on the thread pool, and compares the results.
94
+ To check a candidate password, call `testHashAsync` on the loaded row. The mixin hashes the candidate on the thread pool, with the salt and the cost that the stored hash holds, and compares the result with the stored hash.
83
95
 
84
96
  ```typescript
85
97
  import { HTTPResult } from "@antelopejs/interface-api";
@@ -92,7 +104,7 @@ export async function checkCredentials(
92
104
  email: string,
93
105
  password: string,
94
106
  ) {
95
- const user = await userModel.getByEmail(email.toLowerCase());
107
+ const user = await userModel.getByEmail(email);
96
108
  if (!user || !(await user.testHashAsync("password", password))) {
97
109
  throw new HTTPResult(UNAUTHORIZED, "error.invalid_credentials");
98
110
  }
@@ -102,19 +114,19 @@ export async function checkCredentials(
102
114
 
103
115
  A successful sign-in through `/auth/login` also writes the password again when `isHashOutdated("password")` reports an older hash on the row.
104
116
 
105
- Emails are stored trimmed and lowercased. `getByEmail` normalizes its argument itself; normalize with `normalizeEmail` (exported alongside `UserModel`) before writing one.
117
+ Emails are stored trimmed and lowercased. `getByEmail` normalizes its argument itself. Before you write an email, normalize it with `normalizeEmail`, exported next to `UserModel`.
106
118
 
107
- ## Query Users with UserModel
119
+ ## Query users with `UserModel`
108
120
 
109
- `UserModel` extends `BasicDataModel(User, USERS_TABLE_NAME)`, so it has the standard `get`, `getBy`, `getAll`, `insert`, `update` and `delete` operations, plus three lookups of its own.
121
+ `UserModel` extends `BasicDataModel(User, USERS_TABLE_NAME)`. It has the standard `get`, `getBy`, `getAll`, `insert`, `update` and `delete` operations, plus three lookups of its own.
110
122
 
111
123
  | Method | Signature | Description |
112
124
  | ------ | --------- | ----------- |
113
- | `getByEmail` | `(email: string) => Promise<User \| undefined>` | The user with this email, trimmed and lowercased before the lookup through the `email` index, or `undefined`. |
125
+ | `getByEmail` | `(email: string) => Promise<User \| undefined>` | The user with this email, or `undefined`. The email is trimmed and lowercased, then looked up through the `email` index. |
114
126
  | `getOwners` | `() => Promise<User[]>` | Every user with `owner === true`. |
115
127
  | `countOwnersExcluding` | `(excludedIds: string[]) => Promise<number>` | How many owners remain once the given user ids are set aside. Pass an empty array to count all owners. |
116
128
 
117
- `countOwnersExcluding` exists for the last-owner check: before removing the owner flag from a set of users, count who would be left and refuse the operation if the answer is zero.
129
+ `countOwnersExcluding` serves the last-owner check. Before you remove the owner flag from a set of users, count who would remain, and refuse the operation if the answer is zero.
118
130
 
119
131
  ```typescript
120
132
  import { HTTPResult } from "@antelopejs/interface-api";
@@ -134,9 +146,9 @@ export async function demoteOwners(userModel: UserModel, userIds: string[]) {
134
146
  }
135
147
  ```
136
148
 
137
- ## Invalidate Every Token for a User
149
+ ## Invalidate every token for a user
138
150
 
139
- `authKey` takes part in the signing secret of every token issued for the user, so replacing it makes all outstanding access, refresh, two-factor and tenant-assignment tokens fail validation at once. Combine it with `SessionModel.deleteByUserId` from [Tokens and Sessions](/docs/interfaces/auth/tokens-and-sessions) to sign a user out everywhere.
151
+ `authKey` is part of the signing secret of every token issued for the user. Replacing it makes all outstanding access, refresh, two-factor and tenant-assignment tokens fail validation at once. Combine it with `SessionModel.deleteByUserId` from [Tokens and sessions](/docs/interfaces/auth/tokens-and-sessions) to sign a user out everywhere.
140
152
 
141
153
  ```typescript
142
154
  import type {
@@ -159,17 +171,25 @@ export async function signOutEverywhere(
159
171
  }
160
172
  ```
161
173
 
162
- `userModel.update(user)` takes the primary key from the object; the two-argument form `update(id, partial)` updates a subset of fields instead.
174
+ `userModel.update(user)` takes the primary key from the object. The two-argument form `update(id, partial)` updates a subset of fields.
163
175
 
164
- ## Strip Secrets Before Responding
176
+ ## Strip secrets before responding
165
177
 
166
- A `User` loaded from the database carries authentication secrets. `sanitizeUser` returns a copy without them, and is what you return over HTTP.
178
+ A `User` loaded from the database carries authentication secrets. `sanitizeUser` returns a copy without them. Return that copy over HTTP.
167
179
 
168
- ```typescript
180
+ ```typescript fragment
169
181
  sanitizeUser(user: User): Promise<Partial<User>>
170
182
  ```
171
183
 
172
- It removes the account's secret material — the `authKey`, the email-validation and password-reset tokens with their timestamps, and the two-factor secret, the secret of an enrolment in progress, backup codes and email code with its timestamp — along with any additional keys the deployment lists in its authentication configuration. Because the removed set is configurable, the return type is `Partial<User>`: treat every field as possibly absent rather than assuming a fixed shape.
184
+ It removes these fields:
185
+
186
+ - `authKey`;
187
+ - `validationToken` and `validationRequestedAt`;
188
+ - `forgotPasswordToken` and `forgotPasswordRequestedAt`;
189
+ - `twoFactorSecret`, `twoFactorPendingSecret`, `twoFactorTotpLastStep` and `twoFactorBackupCodes`;
190
+ - `twoFactorEmailCode` and `twoFactorEmailCodeRequestedAt`.
191
+
192
+ It also removes the keys the deployment lists in the `userSensitiveKeys` setting of its authentication configuration. A dotted key such as `profile.secret` removes a nested property. Because the removed set is configurable, the return type is `Partial<User>`. Treat every field as possibly absent.
173
193
 
174
194
  ```typescript
175
195
  import { Controller, Get } from "@antelopejs/interface-api";
@@ -187,18 +207,26 @@ export class MeController extends Controller("/api/me") {
187
207
  }
188
208
  ```
189
209
 
190
- ## Send Account Emails
210
+ ## Send account emails
191
211
 
192
- Four entry points send the account emails the authentication flows depend on. Each returns a promise that resolves once the message has been accepted and rejects when the mail provider reports a failure, so decide per call site whether to await it or dispatch it in the background.
212
+ Four functions send the account emails the authentication flows depend on. Each returns a promise that resolves once the mail provider accepts the message, and rejects when the provider reports a failure. Decide at each call site whether to await it or send it in the background.
193
213
 
194
214
  | Function | Signature | Sends |
195
215
  | -------- | --------- | ----- |
196
216
  | `send2FAEmail` | `(user: User, code: string) => Promise<void>` | The emailed second-factor code, to `user.email`. |
197
- | `sendEmailValidationEmail` | `(user: User) => Promise<void>` | The email-validation message, using the `validationToken` currently stored on the user. |
198
- | `sendEmailForgotEmail` | `(user: User) => Promise<void>` | The password-reset message, using the `forgotPasswordToken` currently stored on the user. |
199
- | `sendAdminInviteEmail` | `(email: string, token: string, inviteeName?: string) => Promise<void>` | An invite carrying a signup link built from `token`, to an address that has no user row yet. `inviteeName` addresses the invitee by name when the invite carried one. |
217
+ | `sendEmailValidationEmail` | `(user: User) => Promise<void>` | The email-validation message, with the `validationToken` currently stored on the user. |
218
+ | `sendEmailForgotEmail` | `(user: User) => Promise<void>` | The password-reset message, with the `forgotPasswordToken` currently stored on the user. |
219
+ | `sendAdminInviteEmail` | `(email: string, token: string, inviteeName?: string, context?: AdminInviteEmailContext) => Promise<void>` | An invite with a signup link built from `token`, to an address that has no user row yet. `inviteeName` addresses the invitee by name when the invite carried one. |
220
+
221
+ `AdminInviteEmailContext` has three optional fields. Without them, the invite email keeps its generic wording.
200
222
 
201
- `sendEmailValidationEmail` and `sendEmailForgotEmail` read the code off the user, so generate it, persist it, and only then send.
223
+ | Field | Type | Description |
224
+ | ----- | ---- | ----------- |
225
+ | `workspaceName` | `string` | The workspace the invitee joins. |
226
+ | `inviterName` | `string` | Who sent the invitation. |
227
+ | `language` | `string` | The invitee's language (`en`, `fr`, and others). English when the language is not supported. |
228
+
229
+ `sendEmailValidationEmail` and `sendEmailForgotEmail` read the code from the user. Generate the code, persist it, and only then send.
202
230
 
203
231
  ```typescript
204
232
  import { sendEmailForgotEmail } from "@antelopejs/interface-dms/auth";
@@ -211,7 +239,7 @@ export async function requestPasswordReset(
211
239
  userModel: UserModel,
212
240
  email: string,
213
241
  ) {
214
- const user = await userModel.getByEmail(email.toLowerCase());
242
+ const user = await userModel.getByEmail(email);
215
243
  if (!user) return; // answer identically whether or not the address exists
216
244
 
217
245
  user.forgotPasswordToken = randomstring.generate({
@@ -225,23 +253,23 @@ export async function requestPasswordReset(
225
253
  }
226
254
  ```
227
255
 
228
- `sendAdminInviteEmail` takes a bare address and a token instead of a `User`, because an invitee has no user row until they sign up. The invite record itself, its token and its expiry belong to the [Core](/docs/interfaces/core/introduction) domain, whose invite helper dispatches this mail for you.
256
+ `sendAdminInviteEmail` takes a bare address and a token instead of a `User`, because an invitee has no user row until they sign up. The invite record, its token and its expiry belong to the [Core](/docs/interfaces/core/invites) domain, whose `inviteUserToTenant` sends this email for you.
229
257
 
230
- ## Password Policy
258
+ ## Password policy
231
259
 
232
- `@antelopejs/interface-dms/auth/password` exports the policy every DMS flow applies when a password is set: at least `PASSWORD_MIN_LENGTH` (8) characters, with at least one uppercase letter, one digit and one of `@$!%*?&`, and no character outside ASCII letters, digits and those special characters. `PASSWORD_PATTERN` holds the character rules without the length.
260
+ `@antelopejs/interface-dms/auth/password` exports the policy every DMS flow applies when a password is set. A password needs at least `PASSWORD_MIN_LENGTH` (8) characters, with at least one uppercase letter, one digit, and one of `@$!%*?&`. It may contain only ASCII letters, digits and those special characters. `PASSWORD_PATTERN` holds the character rules without the length.
233
261
 
234
- ```typescript
262
+ ```typescript fragment
235
263
  isPasswordCompliant(password: string): boolean
236
264
  ```
237
265
 
238
- It is a pure function, not an interface call, so it works without the DMS loaded. Call it from any flow you own that stores a password — a public signup, for instance — before hashing it, so your module accepts exactly the passwords the DMS accepts.
266
+ `isPasswordCompliant` is a pure function, not an interface call, so it works without the DMS loaded. Call it from any flow you own that stores a password, such as a public signup, before you hash the password. Your module then accepts exactly the passwords the DMS accepts.
239
267
 
240
- ## Announce a Registration
268
+ ## Announce a registration
241
269
 
242
- `announceRegistration` runs everything that must happen once an account has really joined a tenant: the welcome notification, the collaborator notification to the platform owners, and then the `USER_REGISTERED` hook.
270
+ `announceRegistration` runs what must happen once an account has joined a tenant. It sends the welcome notification and the collaborator notification to the platform owners, then fires the `USER_REGISTERED` hook with `tenantId`, `userId`, `email` and `name`.
243
271
 
244
- ```typescript
272
+ ```typescript fragment
245
273
  announceRegistration(
246
274
  userModel: UserModel,
247
275
  user: User,
@@ -249,10 +277,10 @@ announceRegistration(
249
277
  ): Promise<void>
250
278
  ```
251
279
 
252
- Call it from any registration flow you own — a module with its own signup, a SaaS transactional flow calling it once provisioning has committed. The DMS password signup and a provider login consuming an invitation both go through it, which is what keeps notifications and automations independent of which door the account came through.
280
+ Call it from any registration flow you own: a module with its own signup, or a SaaS transactional flow once provisioning has committed. The DMS password signup and a provider login that consumes an invitation both go through it. Notifications and automations therefore run the same way, whichever flow created the account.
253
281
 
254
- The hook is observational: registration is already complete when it runs, so a failing subscriber is logged rather than allowed to fail the request and strand a user whose account exists but whose retry token is gone. The same is true of the notifications — they are dispatched in the background and a failure there is logged, not propagated.
282
+ The hook only observes. Registration is already complete when it runs, so a failing subscriber is logged and does not fail the request. Failing the request would leave a user whose account exists but whose retry token is gone. The notifications are sent in the background, and a failure there is logged, not propagated.
255
283
 
256
- ## Next Steps
284
+ ## Next steps
257
285
 
258
- [External Identities](/docs/interfaces/auth/external-identities) covers binding a user to the accounts they own on a login provider.
286
+ [External identities](/docs/interfaces/auth/external-identities) covers binding a user to the accounts they own on a login provider.
@@ -1,15 +1,14 @@
1
1
  ---
2
- title: "External Identities"
3
- description: "An external identity binds an account a user owns on a login provider — GitHub, Google, whichever the deployment configures — to a `User` row, so that signing in through that provider resolves to the same account…"
2
+ title: "External identities"
3
+ description: "Bind the accounts a user owns on external login providers, list them, and read the `UserExternalIdentity` table and model."
4
+ mode: reference
4
5
  ---
5
6
 
6
- # External Identities
7
-
8
7
  ## Overview
9
8
 
10
- An external identity binds an account a user owns on a login provider — GitHub, Google, whichever the deployment configures — to a `User` row, so that signing in through that provider resolves to the same account every time. The binding is what the provider login flow reads; the provider account id, not the email, is what identifies it.
9
+ An external identity binds an account a user owns on a login provider, such as GitHub, Google, or any provider the deployment configures, to a `User` row. Signing in through that provider then resolves to the same account every time. The provider login flow reads the binding. The provider account id identifies it, not the email.
11
10
 
12
- The two functions live on the root subpath, the table and its model on `/db`.
11
+ The two functions live in `@antelopejs/interface-dms/auth`. The table and its model live in `@antelopejs/interface-dms/auth/db`.
13
12
 
14
13
  ```typescript
15
14
  import {
@@ -24,11 +23,11 @@ import {
24
23
  } from "@antelopejs/interface-dms/auth/db";
25
24
  ```
26
25
 
27
- This surface is scoped to authentication. A provider connection that powers a product feature — repository access, deployments — owns its own storage and must never be derived from these rows: they say who the user is, not what the deployment may do on their behalf.
26
+ These rows serve authentication only. A provider connection behind a product feature, such as repository access or deployments, owns its own storage and must never be derived from these rows. They say who the user is, not what the deployment may do on the user's behalf.
28
27
 
29
- ## Bind a Provider Account
28
+ ## Bind a provider account
30
29
 
31
- ```typescript
30
+ ```typescript fragment
32
31
  linkExternalIdentity(
33
32
  userId: string,
34
33
  identity: ExternalIdentityInput,
@@ -49,13 +48,13 @@ interface ExternalIdentityLinkResult {
49
48
 
50
49
  | Field | Description |
51
50
  | ----- | ----------- |
52
- | `provider` | Provider identifier, as declared in the instance's OAuth configuration. |
53
- | `providerAccountId` | Immutable account identifier on the provider side. Never the email: users change their provider email without changing account. |
51
+ | `provider` | Provider identifier, as declared in the OAuth configuration of the instance. |
52
+ | `providerAccountId` | Immutable account identifier on the provider side. Never the email, because users change their provider email without changing account. |
54
53
  | `email` | Email the provider reported, stored lowercased for support and audit only. |
55
54
 
56
- `wasCreated` distinguishes a first binding from a refresh, which is what a login flow needs to decide whether the user just connected a new provider or simply signed in again.
55
+ `wasCreated` is `true` for a first binding and `false` for a refresh. A login flow reads it to tell whether the user just connected a new provider or signed in again.
57
56
 
58
- ```typescript
57
+ ```typescript fragment
59
58
  import { linkExternalIdentity } from "@antelopejs/interface-dms/auth";
60
59
 
61
60
  const { wasCreated } = await linkExternalIdentity(user._id, {
@@ -65,25 +64,25 @@ const { wasCreated } = await linkExternalIdentity(user._id, {
65
64
  });
66
65
  ```
67
66
 
68
- The call is idempotent for the owning user: re-linking the same provider account only refreshes its `email` and `lastLoginAt`. Binding an account already owned by another user throws `409` with the message `error.oauth.identity_already_linked` — a provider account is a single-user credential.
67
+ The call is idempotent for the owning user. Linking the same provider account again only refreshes its `email` and `lastLoginAt`. Binding an account that another user already owns throws `409` with the message `error.oauth.identity_already_linked`, because a provider account is a single-user credential.
69
68
 
70
- Uniqueness is enforced by the row's deterministic primary key rather than by a secondary index, so two concurrent bindings of the same provider account collide instead of duplicating; the loser re-reads the row and either refreshes it or gets the `409`.
69
+ The deterministic primary key of the row enforces uniqueness, not a secondary index. Two concurrent bindings of the same provider account therefore collide instead of creating a duplicate. The call that loses reads the row again, and either refreshes it or throws the `409`.
71
70
 
72
- ## List a User's Identities
71
+ ## List a user's identities
73
72
 
74
- ```typescript
73
+ ```typescript fragment
75
74
  getExternalIdentities(userId: string): Promise<UserExternalIdentity[]>
76
75
  ```
77
76
 
78
- Returns an empty array for a user who only signs in with a password, which is how an account-settings screen decides whether to offer disconnecting a provider.
77
+ It returns an empty array for a user who only signs in with a password. An account settings screen reads it to decide whether to offer disconnecting a provider.
79
78
 
80
- ## The `UserExternalIdentity` Table
79
+ ## The `UserExternalIdentity` table
81
80
 
82
- Table `user_external_identities` in the DMS core schema, so its rows are shared across tenants like `users`. `USER_EXTERNAL_IDENTITIES_TABLE_NAME` holds the name.
81
+ Table `user_external_identities` in the DMS core schema. Like `users`, its rows are shared across tenants. `USER_EXTERNAL_IDENTITIES_TABLE_NAME` holds the name.
83
82
 
84
83
  | Field | Type | Description |
85
84
  | ----- | ---- | ----------- |
86
- | `_id` | `string` | `<provider>:<providerAccountId>`. Being the primary key is what makes the binding unique. |
85
+ | `_id` | `string` | `<provider>:<providerAccountId>`. As the primary key, it makes the binding unique. |
87
86
  | `userId` | `string` | Owning user. Indexed, related to `User`. |
88
87
  | `provider` | `string` | Provider identifier. |
89
88
  | `providerAccountId` | `string` | Account identifier on the provider side. |
@@ -91,7 +90,7 @@ Table `user_external_identities` in the DMS core schema, so its rows are shared
91
90
  | `createdAt` | `Date` | Set on insert. Indexed. |
92
91
  | `lastLoginAt` | `Date` | Refreshed on every successful login through this identity. |
93
92
 
94
- `buildExternalIdentityId(provider, providerAccountId)` composes the id. Use it rather than formatting the string yourself, so a lookup by id keeps matching what `linkExternalIdentity` writes.
93
+ `buildExternalIdentityId(provider, providerAccountId)` composes the id. Use it instead of formatting the string yourself, so a lookup by id keeps matching what `linkExternalIdentity` writes.
95
94
 
96
95
  ## `UserExternalIdentityModel`
97
96
 
@@ -101,11 +100,11 @@ Table `user_external_identities` in the DMS core schema, so its rows are shared
101
100
  | ------ | --------- | ----------- |
102
101
  | `getByProviderAccount` | `(provider: string, providerAccountId: string) => Promise<UserExternalIdentity \| undefined>` | The identity a provider account is bound to, or `undefined` when the account is unknown. |
103
102
  | `getByUserId` | `(userId: string) => Promise<UserExternalIdentity[]>` | Every identity bound to a user, read through the `userId` index. |
104
- | `deleteByUserId` | `(userId: string) => Promise<void>` | Drop every identity of a user. |
103
+ | `deleteByUserId` | `(userId: string) => Promise<void>` | Deletes every identity of a user. |
105
104
 
106
- `getByProviderAccount` is the login-side lookup: resolve the provider profile to an identity, and its `userId` to the account to sign in.
105
+ `getByProviderAccount` is the lookup on the login side. Resolve the provider profile to an identity, then its `userId` to the account to sign in.
107
106
 
108
- ```typescript
107
+ ```typescript fragment
109
108
  import { GetModel } from "@antelopejs/interface-database-decorators";
110
109
  import {
111
110
  UserExternalIdentityModel,
@@ -119,4 +118,4 @@ const user = identity
119
118
  : undefined;
120
119
  ```
121
120
 
122
- An unknown provider account means no account exists for it yet — hand the flow to signup or to invite redemption rather than creating one implicitly. `deleteByUserId` belongs to account deletion: leaving identities behind would let a recycled provider account resolve to a user row that is gone.
121
+ An unknown provider account means no account exists for it yet. Hand the flow to signup or to invite redemption, and do not create an account implicitly. `deleteByUserId` belongs to account deletion. Identities left behind would let a recycled provider account resolve to a user row that no longer exists.
@@ -1,52 +1,51 @@
1
1
  ---
2
2
  title: "Introduction"
3
3
  description: "The base domain is the component vocabulary of the DMS: forms, tables, charts and cards, layout containers, data types and shared prop types."
4
+ mode: explanation
4
5
  ---
5
6
 
6
- # Introduction
7
-
8
7
  ## Overview
9
8
 
10
9
  The `base` domain of `@antelopejs/interface-dms` is the component vocabulary of the DMS: forms, tables, charts and cards, layout containers, trees, the data types behind every field, and the shared prop types they all draw from.
11
10
 
12
- Every component here is a builder. `Form({...})`, `TableView(controller, {...})` and `ChartLine({...})` do not render anything — they return a `ComponentBuilder` from `@antelopejs/interface-dms/component`, which is a serializable description of a component: its name, its options, and its children. You assign that description to a static field of a page class, and the frontend reads the page's tree and draws it. The options you set are therefore data, and the components that need data are given a `fetchUrl` and a `submitUrl` pointing back at routes you declare on the same page or on a data controller.
11
+ Every component here is a builder. `Form({...})`, `TableView(controller, {...})` and `ChartLine({...})` do not render anything. They return a `ComponentBuilder` from `@antelopejs/interface-dms/component`, which is a serializable description of a component: its name, its options, and its children. You assign that description to a static field of a page class, and the frontend reads the page's tree and draws it. The options you set are therefore data, and the components that need data are given a `fetchUrl` and a `submitUrl` pointing back at routes you declare on the same page or on a data controller.
13
12
 
14
- Two consequences shape the whole domain. First, a component's contract is its props interface: what you can express is exactly what the props allow, which is why this reference documents them field by field. Second, anything the frontend must receive has to survive serialization, so a builder that accepts a nested `Component` — a modal target, a kanban card, a chart inside a card — stores its serialized form instead.
13
+ Two consequences shape the whole domain. First, a component's contract is its props interface: what you can express is exactly what the props allow, which is why this reference documents them field by field. Second, anything the frontend must receive has to survive serialization, so a builder that accepts a nested `Component`, such as a modal target, a kanban card or a chart inside a card, stores its serialized form instead.
15
14
 
16
15
  The DMS documentation carries task-shaped chapters on the same components, organised around what you are building. This folder is the contract behind them: the exact symbol names, option fields and return types.
17
16
 
18
- ### Subpath Map
17
+ ### Subpath map
19
18
 
20
19
  `@antelopejs/interface-dms/base` is the barrel over the modules below, and re-exports most of them. The ones marked as not in the barrel are imported from their own subpath. Every row is a subpath of `@antelopejs/interface-dms/base`.
21
20
 
22
21
  | Subpath | Holds | In the root barrel | Documented in |
23
22
  | --- | --- | --- | --- |
24
- | `types` | Base props, colours, tones (`TONES`, `Tone`, `ICON_TONES`, `IconTone`), sizes, buttons, action targets, row-action rules, guards, watch actions, enums | yes | [Shared Types](/docs/interfaces/base/types) |
23
+ | `types` | Base props, colours, tones (`TONES`, `Tone`, `ICON_TONES`, `IconTone`), sizes, buttons, action targets, row-action rules, guards, watch actions, enums | yes | [Shared types](/docs/interfaces/base/types) |
25
24
  | `form` | `Form`, the field and group shapes, `FormComponents`, `FormEvents`, the schema helpers | yes | [Forms](/docs/interfaces/base/forms) |
26
25
  | `table-view` | `TableView`, `@Column`, `TableViewRoutes`, guards, row actions | yes | [Tables](/docs/interfaces/base/tables) |
27
- | `chart` | Chart builders, series and point shapes, chart props | yes | [Charts and Widgets](/docs/interfaces/base/charts-and-widgets) |
28
- | `chart-card` | `ChartCard` | yes | [Charts and Widgets](/docs/interfaces/base/charts-and-widgets) |
29
- | `kpi-card` | `KpiCard` | yes | [Charts and Widgets](/docs/interfaces/base/charts-and-widgets) |
30
- | `top-list-card` | `TopListCard` | yes | [Charts and Widgets](/docs/interfaces/base/charts-and-widgets) |
31
- | `period-selector` | `PeriodSelector` | yes | [Charts and Widgets](/docs/interfaces/base/charts-and-widgets) |
32
- | `live-toggle` | `LiveToggle` | yes | [Display Blocks](/docs/interfaces/base/display-blocks#live-toggle) |
33
- | `page-toolbar` | `PageToolbar`, `pageToolbar` | yes | [Display Blocks](/docs/interfaces/base/display-blocks#page-toolbar) |
34
- | `grid` | `Grid`, `GridRow` | yes | [Layout and Containers](/docs/interfaces/base/layout) |
35
- | `stack` | `HStack`, `VStack`, `Spacer` | yes | [Layout and Containers](/docs/interfaces/base/layout) |
36
- | `tab` | `Tab` | yes | [Layout and Containers](/docs/interfaces/base/layout) |
37
- | `guard` | `Guard` | yes | [Layout and Containers](/docs/interfaces/base/layout#guard) |
38
- | `layouts` | `DefaultLayout`, `FormPageLayout`, `SettingsLayout`, `EmptyLayout` | yes | [Layout and Containers](/docs/interfaces/base/layout) |
39
- | `placeholder` | `Placeholder` | yes | [Layout and Containers](/docs/interfaces/base/layout) |
40
- | `display` | `BlockLinkAction`, `BlockItemsSource`, `BlockEmptyText` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
41
- | `stat-group` | `StatGroup` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
42
- | `key-value-list` | `KeyValueList` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
43
- | `nav-card-grid` | `NavCardGrid` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
44
- | `empty-state` | `EmptyState` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
45
- | `banner` | `Banner` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
46
- | `card` | `Card` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
47
- | `code-block` | `CodeBlock` | yes | [Display Blocks](/docs/interfaces/base/display-blocks) |
48
- | `custom` | `CustomComponent` | no | [Layout and Containers](/docs/interfaces/base/layout) |
49
- | `data-types` | `DataType`, the registration decorators, `DefaultDataTypes`, `DefaultDataCompareTypes`, `StatusType` | no | [Data Types](/docs/interfaces/base/data-types) |
26
+ | `chart` | Chart builders, series and point shapes, chart props | yes | [Charts and widgets](/docs/interfaces/base/charts-and-widgets) |
27
+ | `chart-card` | `ChartCard` | yes | [Charts and widgets](/docs/interfaces/base/charts-and-widgets) |
28
+ | `kpi-card` | `KpiCard` | yes | [Charts and widgets](/docs/interfaces/base/charts-and-widgets) |
29
+ | `top-list-card` | `TopListCard` | yes | [Charts and widgets](/docs/interfaces/base/charts-and-widgets) |
30
+ | `period-selector` | `PeriodSelector` | yes | [Charts and widgets](/docs/interfaces/base/charts-and-widgets) |
31
+ | `live-toggle` | `LiveToggle` | yes | [Display blocks](/docs/interfaces/base/display-blocks#live-toggle) |
32
+ | `page-toolbar` | `PageToolbar`, `pageToolbar` | yes | [Display blocks](/docs/interfaces/base/display-blocks#page-toolbar) |
33
+ | `grid` | `Grid`, `GridRow` | yes | [Layout and containers](/docs/interfaces/base/layout) |
34
+ | `stack` | `HStack`, `VStack`, `Spacer` | yes | [Layout and containers](/docs/interfaces/base/layout) |
35
+ | `tab` | `Tab` | yes | [Layout and containers](/docs/interfaces/base/layout) |
36
+ | `guard` | `Guard` | yes | [Layout and containers](/docs/interfaces/base/layout#guard) |
37
+ | `layouts` | `DefaultLayout`, `FormPageLayout`, `SettingsLayout`, `EmptyLayout` | yes | [Layout and containers](/docs/interfaces/base/layout) |
38
+ | `placeholder` | `Placeholder` | yes | [Layout and containers](/docs/interfaces/base/layout) |
39
+ | `display` | `BlockLinkAction`, `BlockItemsSource`, `BlockEmptyText` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
40
+ | `stat-group` | `StatGroup` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
41
+ | `key-value-list` | `KeyValueList` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
42
+ | `nav-card-grid` | `NavCardGrid` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
43
+ | `empty-state` | `EmptyState` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
44
+ | `banner` | `Banner` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
45
+ | `card` | `Card` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
46
+ | `code-block` | `CodeBlock` | yes | [Display blocks](/docs/interfaces/base/display-blocks) |
47
+ | `custom` | `CustomComponent` | no | [Layout and containers](/docs/interfaces/base/layout) |
48
+ | `data-types` | `DataType`, the registration decorators, `DefaultDataTypes`, `DefaultDataCompareTypes`, `StatusType` | no | [Data types](/docs/interfaces/base/data-types) |
50
49
  | `searchable` | `Searchable`, `SearchableMeta` | yes | [Helpers](/docs/interfaces/base/helpers) |
51
50
  | `export-jobs` | `runExportJob` and the export-job lifecycle | yes | [Helpers](/docs/interfaces/base/helpers) |
52
51
  | `tenant-export-archive` | `startTenantExportJob`, `buildTenantExportArchive` | yes | [Helpers](/docs/interfaces/base/helpers) |
@@ -55,33 +54,33 @@ The DMS documentation carries task-shaped chapters on the same components, organ
55
54
 
56
55
  Within `types`, four modules are outside that subpath's barrel and are imported from `types/action-target`, `types/row-action`, `types/row-action-operators` and `types/guards`; the guard types and `RowActionRule` are also re-exported by `table-view`.
57
56
 
58
- ## Key Features
57
+ ## What the package provides
59
58
 
60
- - **Builders that produce data** — Every component function returns a `ComponentBuilder`, so a page's UI is a plain value you can factor, reuse and compose with `.child()`.
61
- - **Forms from typed fields** — A field names a `DataType`, and the type supplies both the input component and the Zod schema; `Form` compiles the field list into one schema and ships its JSON Schema form with the component.
62
- - **Tables from data controllers** — `TableView` reads a controller's data-api metadata and its `@Column` declarations to build the columns, the `new` / `edit` / `view` forms, and the permission-carrying actions in one call.
63
- - **Server-enforced row rules and guards** — Row-action rules are a small algebra evaluated on both sides, and guards run inside the mutating routes, so what the row menu offers is what the server accepts.
64
- - **A chart and widget set with a shared period scope** — Thirteen chart types plus KPI, chart and top-list cards; one `PeriodSelector` id ties them to the same range and comparison.
65
- - **Layout without a frontend build** — Grids, stacks, tabs and page layouts arrange components, and `CustomComponent` names a Vue component when the vocabulary runs out.
66
- - **An extensible data-type registry** — `@RegisterDataType` and `@RegisterDataCompareMode` add your own types and filter operators to the catalogue the forms, columns and filters draw from.
67
- - **Shared prop vocabulary** — Colours, sizes, HTTP methods, action targets, readonly behaviour and watch actions are declared once in `types` and reused by every component.
59
+ - **Builders that produce data.** Every component function returns a `ComponentBuilder`, so a page's UI is a plain value you can factor, reuse and compose with `.child()`.
60
+ - **Forms from typed fields.** A field names a `DataType`, and the type supplies both the input component and the Zod schema; `Form` compiles the field list into one schema and ships its JSON Schema form with the component.
61
+ - **Tables from data controllers.** `TableView` reads a controller's data-api metadata and its `@Column` declarations to build the columns, the `new` / `edit` / `view` forms, and the permission-carrying actions in one call.
62
+ - **Server-enforced row rules and guards.** Row-action rules are a small algebra evaluated on both sides, and guards run inside the mutating routes, so what the row menu offers is what the server accepts.
63
+ - **A chart and widget set with a shared period scope.** Thirteen chart types plus KPI, chart and top-list cards; one `PeriodSelector` id ties them to the same range and comparison.
64
+ - **Layout without a frontend build.** Grids, stacks, tabs and page layouts arrange components, and `CustomComponent` names a Vue component when the vocabulary runs out.
65
+ - **An extensible data-type registry.** `@RegisterDataType` and `@RegisterDataCompareMode` add your own types and filter operators to the catalogue the forms, columns and filters draw from.
66
+ - **Shared prop vocabulary.** Colors, sizes, HTTP methods, action targets, readonly behaviour and watch actions are declared once in `types` and reused by every component.
68
67
 
69
68
  ## Dependencies
70
69
 
71
70
  This interface relies on the following AntelopeJS packages:
72
71
 
73
- - [@antelopejs/interface-api](https://github.com/AntelopeJS/interface-api) - Request context, controller metadata and route parameters.
74
- - [@antelopejs/interface-api-util](https://github.com/AntelopeJS/interface-api-util) - `assert` for failing a route with an HTTP status.
75
- - [@antelopejs/interface-core](https://github.com/AntelopeJS/interface-core) - Metadata storage, decorator factories, interface functions and logging.
76
- - [@antelopejs/interface-data-api](https://github.com/AntelopeJS/interface-data-api) - Data controllers, default routes, list parameters and field metadata.
77
- - [@antelopejs/interface-database](https://github.com/AntelopeJS/interface-database) - The value proxies compare modes build their filters from.
78
- - [@antelopejs/interface-database-decorators](https://github.com/AntelopeJS/interface-database-decorators) - Models and the localization modifier.
79
- - [@antelopejs/interface-file-storage](https://github.com/AntelopeJS/interface-file-storage) - Upload constraints, staged-key promotion and file deletion behind the file and image types.
80
- - [zod](https://www.npmjs.com/package/zod) and [zod-to-json-schema](https://www.npmjs.com/package/zod-to-json-schema) - Field validation schemas and their JSON Schema form.
72
+ - [`@antelopejs/interface-api`](https://github.com/AntelopeJS/interface-api): Request context, controller metadata and route parameters.
73
+ - [`@antelopejs/interface-api-util`](https://github.com/AntelopeJS/interface-api-util): `assert` for failing a route with an HTTP status.
74
+ - [`@antelopejs/interface-core`](https://github.com/AntelopeJS/interface-core): Metadata storage, decorator factories, interface functions and logging.
75
+ - [`@antelopejs/interface-data-api`](https://github.com/AntelopeJS/interface-data-api): Data controllers, default routes, list parameters and field metadata.
76
+ - [`@antelopejs/interface-database`](https://github.com/AntelopeJS/interface-database): The value proxies compare modes build their filters from.
77
+ - [`@antelopejs/interface-database-decorators`](https://github.com/AntelopeJS/interface-database-decorators): Models and the localization modifier.
78
+ - [`@antelopejs/interface-file-storage`](https://github.com/AntelopeJS/interface-file-storage): Upload constraints, staged-key promotion and file deletion behind the file and image types.
79
+ - [`zod`](https://www.npmjs.com/package/zod) and [`zod-to-json-schema`](https://www.npmjs.com/package/zod-to-json-schema) - Field validation schemas and their JSON Schema form.
81
80
 
82
81
  It also builds on other domains of the same package: `/component` for `Component` and `ComponentBuilder`, `/page` for the page a component is registered on, `/permissions` for the action permissions a table view declares, and `/auth` for the authenticated user its routes receive.
83
82
 
84
- ## Quick Start
83
+ ## A first page
85
84
 
86
85
  A page declaring one component. The `Form` builder describes the fields; the two routes on the same class serve and accept the values. Define the backing model first; `SettingsModel` is application code, not a DMS export.
87
86
 
@@ -108,7 +107,7 @@ export class SettingsModel extends BasicDataModel(Settings, "settings") {}
108
107
 
109
108
  The page edits one module-wide record, so restrict it to platform owners. It returns defaults before the first save and upserts only `SETTINGS_ROW_ID`, rather than updating every row in the table.
110
109
 
111
- ```typescript [settings-page.ts]
110
+ ```typescript fragment [settings-page.ts]
112
111
  import { Get, JSONBody, Post } from "@antelopejs/interface-api";
113
112
  import { assert } from "@antelopejs/interface-api-util";
114
113
  import { GetModel } from "@antelopejs/interface-database-decorators";
@@ -180,7 +179,7 @@ export class SettingsPage extends PageController(
180
179
 
181
180
  Import the page and provision the schema from your module's entry file. Merge this into an existing `start()` if your module already has one:
182
181
 
183
- ```typescript [index.ts]
182
+ ```typescript fragment [index.ts]
184
183
  import "./settings-page";
185
184
  import { RegisterSchema } from "@antelopejs/interface-database-decorators";
186
185
  import { SETTINGS_SCHEMA } from "./db";
@@ -190,14 +189,19 @@ export async function start(): Promise<void> {
190
189
  }
191
190
  ```
192
191
 
193
- The page registers itself at import time, and its routes are mounted under the page's slug — which is why the form fetches from and submits to `/settings/`. `Form` serializes the field descriptions and JSON Schema for the frontend; the custom write route must still validate the request on the server, as the `formSchema` call does here. For per-tenant settings, use a tenant-scoped schema and model instead of this module-wide record.
194
-
195
- ## Documentation Sections
196
-
197
- - [Shared Types](/docs/interfaces/base/types) - Base props, colours, sizes, action targets, row-action rules, guards and the small enums.
198
- - [Forms](/docs/interfaces/base/forms) - The `Form` builder, fields and groups, input components and the validation schema.
199
- - [Tables](/docs/interfaces/base/tables) - Table views over a data controller: columns, routes, row actions, guards, displays and generated forms.
200
- - [Charts and Widgets](/docs/interfaces/base/charts-and-widgets) - Chart types and props, chart cards, KPI cards, top lists and the period selector.
201
- - [Layout and Containers](/docs/interfaces/base/layout) - Grids, stacks, tabs, the guard, page layouts, the placeholder and the custom-component escape hatch.
202
- - [Data Types](/docs/interfaces/base/data-types) - The `DataType` contract, the registries, the default catalogue and a custom type.
203
- - [Helpers](/docs/interfaces/base/helpers) - Searchable columns, export jobs, file references and the archive-mode defaults.
192
+ The page registers itself at import time, and its routes are mounted under the page's slug, so the form fetches from and submits to `/settings/`. `Form` serializes the field descriptions and JSON Schema for the frontend; the custom write route must still validate the request on the server, as the `formSchema` call does here. For per-tenant settings, use a tenant-scoped schema and model instead of this module-wide record.
193
+
194
+ ## Pages in this section
195
+
196
+ - [Shared types](/docs/interfaces/base/types): Base props, colours, sizes, action targets, row-action rules, guards and the small enums.
197
+ - [Forms](/docs/interfaces/base/forms): The `Form` builder, fields and groups, input components and the validation schema.
198
+ - [Tables](/docs/interfaces/base/tables): Table views over a data controller: the controller, its routes, columns and table view options.
199
+ - [Table sources and tenant gates](/docs/interfaces/base/table-sources-and-gates): Table views over a module's route, and behind a tenant gate.
200
+ - [Table row actions](/docs/interfaces/base/table-row-actions): Row actions, archive mode and guards.
201
+ - [Table filters and forms](/docs/interfaces/base/table-filters-and-forms): Filters, tabs, displays and generated forms.
202
+ - [Table events and helpers](/docs/interfaces/base/table-events-and-helpers): Exports, realtime, metadata, route helpers and events.
203
+ - [Charts and widgets](/docs/interfaces/base/charts-and-widgets): Chart types and props, chart cards, KPI cards, top lists and the period selector.
204
+ - [Layout and containers](/docs/interfaces/base/layout): Grids, stacks, tabs, the guard, the placeholder and `CustomComponent`.
205
+ - [Page layouts](/docs/interfaces/base/page-layouts): The layouts a page renders in, its header actions, and pages that fill the panel.
206
+ - [Data types](/docs/interfaces/base/data-types): The `DataType` contract, the registries, the default catalogue and a custom type.
207
+ - [Helpers](/docs/interfaces/base/helpers): Searchable columns, export jobs, file references and the archive-mode defaults.