adminizer 5.0.0-build.8 → 5.1.0-build.24

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 (232) hide show
  1. package/assets/abnfDiagram-VCTEODGH-DFY3Pztx.js +1 -0
  2. package/assets/add-31tS-g4V.js +1 -0
  3. package/assets/add-group-Cf3zT7WE.js +1 -0
  4. package/assets/add-user-C62wOna-.js +1 -0
  5. package/assets/ai-assistant/agent.es.js +28464 -0
  6. package/assets/app-DPmI-MvN.css +1 -0
  7. package/assets/app-vdHbjOQX.js +138 -0
  8. package/assets/arc-DP4o1zz3.js +1 -0
  9. package/assets/architectureDiagram-5GKGNRK7-BjXW5Zu0.js +36 -0
  10. package/assets/asmcrypto-DZeQH0LC.js +1 -0
  11. package/assets/blockDiagram-I7D4REHJ-BUPnDwN0.js +129 -0
  12. package/assets/c4Diagram-7LVT6UL2-Cmb_dAhd.js +38 -0
  13. package/assets/{catalog-BEvJZYJP.js → catalog-C-y5rMaG.js} +3 -3
  14. package/assets/channel-CkJRUsq_.js +1 -0
  15. package/assets/chunk-2Q5K7J3B-CmS-dWwQ.js +1 -0
  16. package/assets/chunk-5VM5RSS4-Cqs2Ow64.js +15 -0
  17. package/assets/chunk-F27PBJKO-BoMCpJA1.js +1 -0
  18. package/assets/chunk-IMKFNOWR-DlDcPk1x.js +231 -0
  19. package/assets/chunk-JWPE2WC7-DzL_SU8s.js +1 -0
  20. package/assets/chunk-POPQ4Y6H-Cwq2-Vb8.js +1 -0
  21. package/assets/chunk-SVP7TREG-BfRHY5FU.js +88 -0
  22. package/assets/chunk-TICWLB2K-CRP7rHkW.js +206 -0
  23. package/assets/chunk-XXDRQBXY-Bg2jg-98.js +1 -0
  24. package/assets/classDiagram-ZZMXUADV-D4EF3HGj.js +1 -0
  25. package/assets/classDiagram-v2-VYDZK3BY-D4EF3HGj.js +1 -0
  26. package/assets/controls/ckeditor.es.js +9382 -9320
  27. package/assets/controls/handsontable.es.js +11971 -11938
  28. package/assets/controls/jsoneditor.es.js +10442 -10233
  29. package/assets/controls/toast-ui.es.js +1308 -1279
  30. package/assets/cose-bilkent-JH36ORCC-CCJ0SGyO.js +1 -0
  31. package/assets/cynefin-OW5HDTMX-B2Jx5fw3.js +166 -0
  32. package/assets/cynefinDiagram-5FMLGOSQ-DuK0Lb62.js +62 -0
  33. package/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  34. package/assets/dagre-GXQ25YYZ-DnOYexET.js +4 -0
  35. package/assets/dashboard-X8k4X6Sw.js +3 -0
  36. package/assets/debounce-B3mXI_sx.js +1 -0
  37. package/assets/defaultLocale-DX6XiGOO.js +1 -0
  38. package/assets/diagram-S7CK7UJ4-DDgPBHsK.js +30 -0
  39. package/assets/diagram-UQ7AKVKN-qlL0GZs8.js +41 -0
  40. package/assets/diagram-VSXAHHWV-CQbiU1_c.js +3 -0
  41. package/assets/diagram-VX7I27RA-DFDI9_au.js +24 -0
  42. package/assets/diagram-Z3DM3KII-B5C9dtmJ.js +24 -0
  43. package/assets/docs-BiZOD7PP.js +1 -0
  44. package/assets/ebnfDiagram-PWID7BFC-CLMUsXEI.js +1 -0
  45. package/assets/erDiagram-RLTQ6QDP-ax9Xol5-.js +99 -0
  46. package/assets/flowDiagram-HODETNUW-CvaH7l3S.js +168 -0
  47. package/assets/ganttDiagram-EL5Y4UJY-Cga5U19j.js +292 -0
  48. package/assets/gitGraphDiagram-WWUBYQGX-B6o1T3-d.js +106 -0
  49. package/assets/history-CVBJUNSQ.js +1 -0
  50. package/assets/infoDiagram-27XIBGKW-BpWxgBCE.js +2 -0
  51. package/assets/init-Gi6I4Gst.js +1 -0
  52. package/assets/init-user-xSvnFTF0.js +1 -0
  53. package/assets/isObject-DL-BuaBl.js +1 -0
  54. package/assets/ishikawaDiagram-5VMMS53U-zE83db4H.js +70 -0
  55. package/assets/journeyDiagram-3NMN7TZE-DZNVFqrX.js +139 -0
  56. package/assets/kanban-definition-UXKFOSKX-CqxNgk27.js +89 -0
  57. package/assets/katex-HP8lGamR.js +257 -0
  58. package/assets/layout-CN3kt_jt.js +1 -0
  59. package/assets/linear-nvkaQ6BA.js +1 -0
  60. package/assets/{list-DhK0McVn.js → list-CrIU2eZz.js} +2 -2
  61. package/assets/login-CX9xVjCn.js +1 -0
  62. package/assets/manifest.json +834 -58
  63. package/assets/media-manager-BfYftLIH.js +6 -0
  64. package/assets/mermaid.core-C0V1NaVJ.js +310 -0
  65. package/assets/mindmap-definition-YA3MSWOX-Bh7_d9f1.js +96 -0
  66. package/assets/module-DPEvnad3.js +1 -0
  67. package/assets/notification-Dqpt5AIZ.js +20 -0
  68. package/assets/ordinal-Cboi1Yqb.js +1 -0
  69. package/assets/pegDiagram-XKGWAZYB-DMnjm6Z1.js +1 -0
  70. package/assets/pieDiagram-E7YTZNPT-D-RWCgYb.js +39 -0
  71. package/assets/quadrantDiagram-AXDQQJYC-BNBGP7RM.js +7 -0
  72. package/assets/railroadDiagram-O6MQD6OU-BIjVPT01.js +1 -0
  73. package/assets/register-CF4KQ97e.js +1 -0
  74. package/assets/requirementDiagram-BXWQKSXE-VPrtGdjM.js +84 -0
  75. package/assets/ru-CFJ4fysR.js +1 -0
  76. package/assets/sankeyDiagram-P5KCCOFB-ct9mLF3U.js +40 -0
  77. package/assets/sequenceDiagram-WJ2MYXX4-TC06Mbmw.js +162 -0
  78. package/assets/sizeCapture-INFHLROL-BMMwriCf.js +1 -0
  79. package/assets/stateDiagram-D77RDMKH-DtwYRPCX.js +1 -0
  80. package/assets/stateDiagram-v2-MP3YSRHH-C1yB0O5U.js +1 -0
  81. package/assets/swimlanes-42K2YHIH-DE3hRbbr.js +1 -0
  82. package/assets/swimlanesDiagram-VR7AAH4N-Ce4l1sQD.js +8 -0
  83. package/assets/timeline-definition-24CTP7MA-N7s_hfjj.js +120 -0
  84. package/assets/toNumber-DZb-r9sy.js +1 -0
  85. package/assets/use-appearance-DYTIKpT2.js +1 -0
  86. package/assets/user-filters-list-fR0YNJ3l.js +1 -0
  87. package/assets/vennDiagram-4TSXK5OY-Dj5bFQkR.js +34 -0
  88. package/assets/wardleyDiagram-VM6X3IG4-83ax25Z6.js +78 -0
  89. package/assets/welcome-v11MTsRK.js +1 -0
  90. package/assets/with-app-layout-CH6RIib1.js +31 -0
  91. package/assets/xychartDiagram-S5SC5T6Z-CW9Br5Xp.js +7 -0
  92. package/controllers/access-rights/permissionOptions.d.ts +2 -0
  93. package/controllers/access-rights/permissionOptions.js +23 -0
  94. package/controllers/add.js +2 -2
  95. package/controllers/addGroup.js +15 -6
  96. package/controllers/ai/AiAgentController.d.ts +66 -0
  97. package/controllers/ai/AiAgentController.js +435 -0
  98. package/controllers/ai/AiAssistantController.d.ts +0 -4
  99. package/controllers/ai/AiAssistantController.js +4 -78
  100. package/controllers/catalog/FrontendCatalogAdapter.js +7 -8
  101. package/controllers/docs/index.d.ts +22 -0
  102. package/controllers/docs/index.js +126 -0
  103. package/controllers/edit.js +2 -2
  104. package/controllers/editGroup.js +15 -6
  105. package/controllers/filter-fields/savedFilters.js +1 -1
  106. package/controllers/history-actions/HistoryController.js +7 -7
  107. package/controllers/inlineUpdate.js +10 -3
  108. package/controllers/list.js +1 -1
  109. package/controllers/login.js +1 -1
  110. package/controllers/notifications/NotificationController.js +14 -8
  111. package/controllers/view.js +3 -3
  112. package/controllers/widgets/filterInfo.js +1 -1
  113. package/helpers/accessRightsHelper.d.ts +30 -3
  114. package/helpers/accessRightsHelper.js +140 -12
  115. package/helpers/assetVersionHelper.d.ts +14 -0
  116. package/helpers/assetVersionHelper.js +42 -0
  117. package/helpers/configHelper.js +19 -18
  118. package/helpers/controllerHelper.d.ts +1 -0
  119. package/helpers/controllerHelper.js +25 -15
  120. package/helpers/fieldsHelper.d.ts +2 -1
  121. package/helpers/fieldsHelper.js +11 -5
  122. package/helpers/inertiaActionsHelper.d.ts +1 -1
  123. package/helpers/inertiaActionsHelper.js +4 -5
  124. package/helpers/inertiaAddHelper.d.ts +1 -1
  125. package/helpers/inertiaAddHelper.js +7 -6
  126. package/helpers/inertiaGroupHelper.js +10 -2
  127. package/helpers/inertiaListHelper.d.ts +1 -1
  128. package/helpers/inertiaListHelper.js +7 -8
  129. package/helpers/inertiaMenuHelper.d.ts +7 -2
  130. package/helpers/inertiaMenuHelper.js +12 -27
  131. package/helpers/menuHelper.d.ts +11 -1
  132. package/helpers/menuHelper.js +12 -0
  133. package/helpers/modelResourceHelper.d.ts +9 -0
  134. package/helpers/modelResourceHelper.js +43 -0
  135. package/helpers/navigationAccessHelper.d.ts +13 -0
  136. package/helpers/navigationAccessHelper.js +45 -0
  137. package/index.d.ts +11 -0
  138. package/index.js +11 -0
  139. package/interfaces/adminpanelConfig.d.ts +160 -2
  140. package/interfaces/queryCriteria.d.ts +13 -0
  141. package/interfaces/types.d.ts +169 -0
  142. package/lib/Adminizer.d.ts +8 -0
  143. package/lib/Adminizer.js +29 -2
  144. package/lib/DataAccessor.d.ts +100 -3
  145. package/lib/DataAccessor.js +454 -245
  146. package/lib/access-graph/AccessGraphResolver.d.ts +136 -0
  147. package/lib/access-graph/AccessGraphResolver.js +605 -0
  148. package/lib/access-graph/RecordAccessCache.d.ts +23 -0
  149. package/lib/access-graph/RecordAccessCache.js +30 -0
  150. package/lib/access-graph/RecordAccessResolver.d.ts +83 -0
  151. package/lib/access-graph/RecordAccessResolver.js +295 -0
  152. package/lib/access-graph/shared.d.ts +60 -0
  153. package/lib/access-graph/shared.js +121 -0
  154. package/lib/admin-links/AdminLinkHandler.d.ts +72 -0
  155. package/lib/admin-links/AdminLinkHandler.js +183 -0
  156. package/lib/ai-assistant/AbstractAiConversationHistoryService.d.ts +39 -0
  157. package/lib/ai-assistant/AbstractAiConversationHistoryService.js +98 -0
  158. package/lib/ai-assistant/AbstractAiModelService.d.ts +139 -2
  159. package/lib/ai-assistant/AbstractAiModelService.js +154 -1
  160. package/lib/ai-assistant/AiAgentRunManager.d.ts +35 -0
  161. package/lib/ai-assistant/AiAgentRunManager.js +109 -0
  162. package/lib/ai-assistant/AiAssistantAgentSkillHandler.d.ts +61 -0
  163. package/lib/ai-assistant/AiAssistantAgentSkillHandler.js +90 -0
  164. package/lib/ai-assistant/AiAssistantHandler.d.ts +1 -0
  165. package/lib/ai-assistant/AiAssistantHandler.js +4 -0
  166. package/lib/ai-assistant/AiAssistantUiMethodHandler.d.ts +60 -0
  167. package/lib/ai-assistant/AiAssistantUiMethodHandler.js +144 -0
  168. package/lib/ai-assistant/builtinAgentSkills.d.ts +9 -0
  169. package/lib/ai-assistant/builtinAgentSkills.js +259 -0
  170. package/lib/ai-assistant/jsonSafe.d.ts +13 -0
  171. package/lib/ai-assistant/jsonSafe.js +81 -0
  172. package/lib/app-manager/AdminizerApp.d.ts +31 -2
  173. package/lib/app-manager/AppManager.js +43 -42
  174. package/lib/app-manager/ConfigLayerHandler.js +4 -0
  175. package/lib/docs/AbstractDocumentation.d.ts +100 -0
  176. package/lib/docs/AbstractDocumentation.js +108 -0
  177. package/lib/docs/DocumentationHandler.d.ts +100 -0
  178. package/lib/docs/DocumentationHandler.js +198 -0
  179. package/lib/docs/FileDocumentation.d.ts +47 -0
  180. package/lib/docs/FileDocumentation.js +189 -0
  181. package/lib/docs/docsAgentSkills.d.ts +10 -0
  182. package/lib/docs/docsAgentSkills.js +93 -0
  183. package/lib/filters/FilterService.js +4 -3
  184. package/lib/history-actions/AbstractHistoryAdapter.d.ts +26 -1
  185. package/lib/history-actions/AbstractHistoryAdapter.js +155 -25
  186. package/lib/media-manager/helpers/MediaManagerHelper.js +9 -7
  187. package/lib/model/AbstractModel.d.ts +48 -0
  188. package/lib/model/AbstractModel.js +14 -4
  189. package/lib/model/ModelHandler.d.ts +50 -2
  190. package/lib/model/ModelHandler.js +117 -8
  191. package/lib/model/adapter/sequelize.d.ts +43 -3
  192. package/lib/model/adapter/sequelize.js +317 -138
  193. package/lib/model/adapter/typeorm.d.ts +16 -0
  194. package/lib/model/adapter/typeorm.js +136 -9
  195. package/lib/notifications/GeneralNotificationService.js +6 -5
  196. package/lib/notifications/SystemNotificationService.js +1 -1
  197. package/lib/ui-i18n/uiTranslationKeys.d.ts +2 -1
  198. package/lib/ui-i18n/uiTranslationKeys.js +42 -1
  199. package/lib/widgets/widgetHandler.js +10 -6
  200. package/models/Group.d.ts +2 -1
  201. package/package.json +35 -23
  202. package/policies/authPolicies.js +6 -6
  203. package/policies/permissionResolvers.d.ts +1 -0
  204. package/policies/permissionResolvers.js +3 -0
  205. package/system/Router.js +21 -3
  206. package/system/bindAccessRights.js +13 -3
  207. package/system/bindDocs.d.ts +13 -0
  208. package/system/bindDocs.js +77 -0
  209. package/system/bindInertia.js +30 -7
  210. package/system/buildInternalModelAccess.d.ts +8 -0
  211. package/system/buildInternalModelAccess.js +36 -4
  212. package/system/defaults.js +7 -1
  213. package/system/validateSystemModels.js +29 -9
  214. package/assets/add-DED_wpga.js +0 -1
  215. package/assets/add-group-CL80RdYb.js +0 -1
  216. package/assets/add-user-BRmwvQB8.js +0 -1
  217. package/assets/app-DNXiApCk.css +0 -1
  218. package/assets/app-layout-86yxiZOy.js +0 -1
  219. package/assets/app.js +0 -138
  220. package/assets/dashboard-ZP6K2s_8.js +0 -3
  221. package/assets/debounce-BMmXVQ06.js +0 -1
  222. package/assets/history-M61Bcv02.js +0 -1
  223. package/assets/init-user-8aUohVDq.js +0 -1
  224. package/assets/login-CO4laf6m.js +0 -1
  225. package/assets/media-manager-BDbpSSjH.js +0 -6
  226. package/assets/module-DfBneaUl.js +0 -1
  227. package/assets/notification-CVnQf3ef.js +0 -20
  228. package/assets/register-DBYIMXGQ.js +0 -1
  229. package/assets/ru-rYKdQbWF.js +0 -1
  230. package/assets/use-appearance-_EbbjHTh.js +0 -1
  231. package/assets/user-filters-list-DXhGLFTn.js +0 -1
  232. package/assets/welcome-CLxvXkcg.js +0 -1
package/index.d.ts CHANGED
@@ -27,6 +27,9 @@ export * from "./lib/media-manager/AbstractMediaManager.js";
27
27
  export * from "./lib/controls/Control.js";
28
28
  export * from "./lib/controls/ControlsHandler.js";
29
29
  export * from "./lib/Adminizer.js";
30
+ export * from "./lib/docs/AbstractDocumentation.js";
31
+ export * from "./lib/docs/DocumentationHandler.js";
32
+ export * from "./lib/docs/FileDocumentation.js";
30
33
  export * from "./lib/I18n.js";
31
34
  export * from "./models/Group.js";
32
35
  export * from "./models/User.js";
@@ -43,7 +46,15 @@ export * from "./lib/helper/jwt.js";
43
46
  export * from "./lib/notifications/AbstractNotificationService.js";
44
47
  export * from "./lib/POWCaptcha.js";
45
48
  export * from "./lib/ai-assistant/AbstractAiModelService.js";
49
+ export * from "./lib/ai-assistant/AbstractAiConversationHistoryService.js";
46
50
  export * from "./lib/ai-assistant/AiAssistantHandler.js";
51
+ export * from "./lib/ai-assistant/AiAgentRunManager.js";
52
+ export * from "./lib/ai-assistant/jsonSafe.js";
53
+ export * from "./lib/ai-assistant/AiAssistantUiMethodHandler.js";
54
+ export * from "./lib/ai-assistant/AiAssistantAgentSkillHandler.js";
55
+ export * from "./lib/admin-links/AdminLinkHandler.js";
56
+ export * from "./helpers/modelResourceHelper.js";
57
+ export * from "./helpers/navigationAccessHelper.js";
47
58
  export * from "./lib/DataAccessor.js";
48
59
  export * from "./lib/history-actions/AbstractHistoryAdapter.js";
49
60
  export * from "./lib/filters/CustomFilterHandler.js";
package/index.js CHANGED
@@ -27,6 +27,9 @@ export * from "./lib/media-manager/AbstractMediaManager.js";
27
27
  export * from "./lib/controls/Control.js";
28
28
  export * from "./lib/controls/ControlsHandler.js";
29
29
  export * from "./lib/Adminizer.js";
30
+ export * from "./lib/docs/AbstractDocumentation.js";
31
+ export * from "./lib/docs/DocumentationHandler.js";
32
+ export * from "./lib/docs/FileDocumentation.js";
30
33
  export * from "./lib/I18n.js";
31
34
  export * from "./models/Group.js";
32
35
  export * from "./models/User.js";
@@ -43,7 +46,15 @@ export * from "./lib/helper/jwt.js";
43
46
  export * from "./lib/notifications/AbstractNotificationService.js";
44
47
  export * from "./lib/POWCaptcha.js";
45
48
  export * from "./lib/ai-assistant/AbstractAiModelService.js";
49
+ export * from "./lib/ai-assistant/AbstractAiConversationHistoryService.js";
46
50
  export * from "./lib/ai-assistant/AiAssistantHandler.js";
51
+ export * from "./lib/ai-assistant/AiAgentRunManager.js";
52
+ export * from "./lib/ai-assistant/jsonSafe.js";
53
+ export * from "./lib/ai-assistant/AiAssistantUiMethodHandler.js";
54
+ export * from "./lib/ai-assistant/AiAssistantAgentSkillHandler.js";
55
+ export * from "./lib/admin-links/AdminLinkHandler.js";
56
+ export * from "./helpers/modelResourceHelper.js";
57
+ export * from "./helpers/navigationAccessHelper.js";
47
58
  export * from "./lib/DataAccessor.js";
48
59
  export * from "./lib/history-actions/AbstractHistoryAdapter.js";
49
60
  export * from "./lib/filters/CustomFilterHandler.js";
@@ -75,6 +75,26 @@ export interface AdminpanelConfig {
75
75
  models: {
76
76
  [key: string]: ModelConfig;
77
77
  };
78
+ /**
79
+ * Relationship-based record access (ReBAC): each key declares one access graph —
80
+ * models whose records inherit visibility from a root model down parent edges.
81
+ * Membership is granted once, on root records (same `through` form as the membership
82
+ * variant of `userAccessRelation`), and every model of the graph is filtered by it
83
+ * transitively. The graph wins over a covered model's own `userAccessRelation`: once a
84
+ * graph covers a model, that declaration stops applying. Declaring both is a configuration
85
+ * error and is reported as one when the graph compiles.
86
+ * Global CRUD tokens remain the upper bound: the graph narrows visibility, it never
87
+ * grants access the token gates denied.
88
+ *
89
+ * EXPERIMENTAL: the configuration format and access semantics may change in a minor release.
90
+ * Declaring this section makes Adminizer log a warning at boot naming the graphs in use.
91
+ *
92
+ * @beta
93
+ *
94
+ */
95
+ accessGraph?: {
96
+ [graphKey: string]: AccessGraphConfig;
97
+ };
78
98
  /**
79
99
  * For custom adminpanel sections, displays inside header
80
100
  * */
@@ -107,6 +127,14 @@ export interface AdminpanelConfig {
107
127
  sectionHandlers?: {
108
128
  [section: string]: (user: User, links: HrefConfig[]) => HrefConfig[];
109
129
  };
130
+ /**
131
+ * Presentation metadata for navbar sections, keyed by section name.
132
+ * The sections themselves are declared by the `section` field of individual
133
+ * links; this map only controls how a section header is drawn.
134
+ */
135
+ sections?: {
136
+ [section: string]: NavbarSectionConfig;
137
+ };
110
138
  };
111
139
  /**
112
140
  * Policies that will be executed before going to every page
@@ -223,6 +251,14 @@ export interface AdminpanelConfig {
223
251
  defaultModel?: string;
224
252
  models?: string[];
225
253
  };
254
+ /**
255
+ * Knowledge base (built-in documentation). The implementation is
256
+ * registered separately: `adminizer.documentationHandler.register(service)`.
257
+ */
258
+ documentation?: {
259
+ /** Master switch of the subsystem; nothing is brought up unless `true` */
260
+ enabled?: boolean;
261
+ };
226
262
  cors?: {
227
263
  enabled: boolean;
228
264
  origin: string[] | string;
@@ -299,9 +335,14 @@ export interface ModelConfig {
299
335
  adapter?: string;
300
336
  title: string;
301
337
  /**
302
- * Model name
338
+ * Physical host ORM model name.
303
339
  * */
304
340
  model: string;
341
+ /**
342
+ * Required only when two or more Adminizer resources use the same host model.
343
+ * The primary resource is selected for associations that reference the host model.
344
+ */
345
+ primary?: boolean;
305
346
  /**
306
347
  * Optional display name for history. Should be a attribute of the model, or a function.
307
348
  */
@@ -392,16 +433,117 @@ export interface ModelConfig {
392
433
  * */
393
434
  identifierField?: string;
394
435
  /** In this field we can set model field, for which we want to check user access right.
395
- * May be association or association-many to User or Group */
436
+ * May be association or association-many to User or Group
437
+ *
438
+ * @deprecated Declare record access with {@link AdminpanelConfig.accessGraph} instead.
439
+ * The membership form (`{field, through, via, group}`) is a single-edge access graph
440
+ * written in a second syntax — same `membership` declaration, `field` becomes the
441
+ * `include` edge's `parent`. The string and `{field, via}` forms have no graph
442
+ * equivalent yet (the graph seeds root ids from membership rows, not from an owner
443
+ * foreign key), so this option is removed in stages rather than at once.
444
+ * Note that this option is already inert wherever an `accessGraph` covers the model:
445
+ * the graph wins and the declaration below is ignored.
446
+ *
447
+ * TODO: removal plan, its prerequisites and the migration recipe live in
448
+ * `.ai-notes/todo/user-access-relation-removal.md`; the reasoning behind the
449
+ * deprecation — including the one behaviour the graph does not reproduce (its root
450
+ * model becomes record-restricted too) — in `.ai-notes/user-access-relation-deprecation.md`.
451
+ */
396
452
  userAccessRelation?: {
397
453
  field: string;
398
454
  via?: string;
455
+ /** Membership form: a join model holding exactly one relation to the model `field` points at,
456
+ * a `via` relation to User and, optionally, a `group` relation to Group. Records are then
457
+ * filtered to the targets the user has a membership row for. */
458
+ through?: string;
459
+ /** Field in `through` that associates with Group. When set, a membership only counts for the
460
+ * actions whose CRUD token (`<verb>-<model>-model`) that group carries, so the same user may
461
+ * hold different rights in different targets. Global tokens remain the upper bound: this
462
+ * narrows rows, it never grants access the token gates denied. */
463
+ group?: string;
399
464
  } | string;
400
465
  userAccessRelationCallback?: (userWithGroups: UserWithGroups, record: any) => boolean;
401
466
  }
402
467
  type UserWithGroups = User & {
403
468
  groups: Group[];
404
469
  };
470
+ /** CRUD verb of the action being performed, as used in `<verb>-<model>-model` tokens. */
471
+ export type AccessGraphActionVerb = "create" | "read" | "update" | "delete";
472
+ /** Ids of the root records the user may see, `"all"` for an explicit bypass, or
473
+ * `undefined` to fall back to the declared `membership`. */
474
+ export type AccessGraphRootIds = unknown[] | "all" | undefined;
475
+ /**
476
+ * One access graph: a root model carrying memberships plus the models that inherit
477
+ * its visibility down explicit parent edges.
478
+ */
479
+ export interface AccessGraphConfig {
480
+ /** Root model of the graph; memberships are granted on its records. */
481
+ root: string;
482
+ /**
483
+ * Membership declaration at the root: `through` is a join model holding exactly one
484
+ * relation to the root model, a `via` relation to User and, optionally, a `group`
485
+ * relation to Group. With `group` declared, a membership only counts for the actions
486
+ * whose CRUD token (`<verb>-<model>-model`, checked against the model being accessed)
487
+ * that group carries.
488
+ */
489
+ membership?: {
490
+ through: string;
491
+ via: string;
492
+ group?: string;
493
+ };
494
+ /**
495
+ * Models covered by the graph. Each entry names the association alias the model uses
496
+ * to look at its parent in the graph; the parent model is derived from that alias and
497
+ * must itself belong to the graph (the root or another `include` entry). Exactly one
498
+ * edge per model — the parent is never guessed from foreign keys.
499
+ */
500
+ include?: {
501
+ [modelName: string]: {
502
+ /** Association alias on this model pointing at its parent in the graph. */
503
+ parent: string;
504
+ };
505
+ };
506
+ /**
507
+ * Stage 2: denormalized root-id columns. For a listed model the read filter collapses
508
+ * to one step — `{<column>: {in: graphRootIds}}` — instead of walking the parent chain.
509
+ * The column is maintained by the application; Adminizer only filters by it. The model
510
+ * still declares its `include` edge — writes keep validating the chosen parent.
511
+ */
512
+ graphRootField?: {
513
+ [modelName: string]: string;
514
+ };
515
+ /**
516
+ * Stage 3: compile the read filter into one nested-subquery SQL emitted by the adapter
517
+ * instead of materializing intermediate id lists in the application. Applies when every
518
+ * model on the path (and the membership model) lives on the same adapter connection;
519
+ * otherwise the resolver falls back to per-level materialization automatically. The SQL
520
+ * is generated from this declaration — no hand-written queries.
521
+ *
522
+ * Why it exists: the default walk costs one query per level of the chain, and the last of
523
+ * them carries every intermediate id the user can reach as a literal `IN (...)` list. The
524
+ * price of the filter therefore grows with the size of the user's data, not with the size
525
+ * of the page — a member of 500 projects ships ~25 000 task ids to render 25 rows, while
526
+ * the pushed-down form emits one statement of constant size whatever the account holds.
527
+ * The accounts it slows down first are the biggest ones.
528
+ *
529
+ * Setting it is a permission, not a guarantee: a chain that cannot be compiled goes back to
530
+ * materialization, and the write path never uses pushdown at all. The fallback is invisible in
531
+ * the result, so it is logged — one warning per model, naming the graph and the reason. Silence
532
+ * means the chain compiled.
533
+ */
534
+ pushdown?: boolean;
535
+ /**
536
+ * Token id that lets its holders see the whole graph unfiltered. Matched against the
537
+ * token grants of the user's global groups; it does not need to be a registered token.
538
+ */
539
+ bypassToken?: string;
540
+ /**
541
+ * Replaces the membership source at the root without touching the graph below it.
542
+ * Returning `undefined` falls back to the declared `membership`; `"all"` is the
543
+ * explicit bypass. The result is still bounded by global tokens.
544
+ */
545
+ resolveGraphRootIds?: (user: User, actionVerb: AccessGraphActionVerb) => Promise<AccessGraphRootIds> | AccessGraphRootIds;
546
+ }
405
547
  export type ModelFieldConfig = (BaseFieldConfig | TuiEditorFieldConfig) & {
406
548
  groupsAccessRights?: string[];
407
549
  };
@@ -583,6 +725,22 @@ export interface HrefConfig {
583
725
  */
584
726
  section?: string;
585
727
  }
728
+ /**
729
+ * Presentation of a single navbar section header.
730
+ */
731
+ export interface NavbarSectionConfig {
732
+ /**
733
+ * Icon of the section header. Also used as the section's trigger in the
734
+ * collapsed (icon-only) sidebar.
735
+ */
736
+ icon?: MaterialIcon;
737
+ /**
738
+ * Explicit position of the section. Sections carrying an `order` are sorted
739
+ * ascending and placed before the ones without it, which keep the default
740
+ * ordering (`Platform` first, `System` last, the rest alphabetically).
741
+ */
742
+ order?: number;
743
+ }
586
744
  export interface MediaManagerConfig {
587
745
  fileStoragePath: string;
588
746
  allowMIME?: string[];
@@ -47,6 +47,19 @@ export interface QueryCriteria<TModel = any> {
47
47
  where?: CriteriaWhere<TModel>;
48
48
  select?: CriteriaSelect;
49
49
  populate?: CriteriaPopulate<TModel>;
50
+ /**
51
+ * Record-access confinement of single populated associations, keyed by association
52
+ * name and ANDed into the populate JOIN's ON clause: a populated record failing the
53
+ * condition arrives as its bare foreign-key value instead of an object, while the
54
+ * owning row itself stays in the result — unlike `populate.<field>.where`, which
55
+ * confines the owning rows.
56
+ *
57
+ * Set exclusively by `DataAccessor.pushDownPopulateAccess` for the associations the
58
+ * adapter declared pushdown-capable (`AbstractModel.canPushdownPopulateAccess`);
59
+ * `sanitizeUserRelationAccess` discards an incoming value, so hand-built criteria
60
+ * cannot suppress the association verification that trusts this key.
61
+ */
62
+ populateOn?: Record<string, CriteriaWhere>;
50
63
  sort?: CriteriaSort;
51
64
  limit?: number;
52
65
  skip?: number;
@@ -1,5 +1,6 @@
1
1
  import { AbstractModel } from "../lib/model/AbstractModel.js";
2
2
  import { ModelConfig } from "./adminpanelConfig.js";
3
+ import type { User } from "../models/User.js";
3
4
  export interface ModelResource {
4
5
  name: string;
5
6
  config?: ModelConfig;
@@ -11,7 +12,24 @@ export interface AccessRightsToken {
11
12
  description: string;
12
13
  department: string;
13
14
  id: string;
15
+ getOptions?: (user: User) => Promise<PermissionOption[]>;
16
+ check?: (user: User, context?: PermissionContext) => Promise<boolean>;
14
17
  }
18
+ export interface PermissionOption {
19
+ id: string;
20
+ name: string;
21
+ description?: string;
22
+ }
23
+ export interface PermissionContext {
24
+ rights?: string[];
25
+ [key: string]: unknown;
26
+ }
27
+ /** Serialized as a JSON string in Group.tokens for a contextual token. */
28
+ export interface GroupPermissionGrant {
29
+ tokenId: string;
30
+ rights: string[];
31
+ }
32
+ export type PermissionGrant = string | GroupPermissionGrant;
15
33
  export interface PropsField {
16
34
  label: string;
17
35
  type: string;
@@ -70,3 +88,154 @@ export interface AiAssistantModelInfo {
70
88
  name: string;
71
89
  description?: string;
72
90
  }
91
+ /**
92
+ * Streaming agent contract.
93
+ *
94
+ * A model service that only implements `generateReply` stays a plain chat
95
+ * model; services that also implement the optional agent members below are
96
+ * driven by the streaming UI (tool calls, reasoning, attachments, per-user
97
+ * server-side session). See AbstractAiModelService.
98
+ */
99
+ /** One SSE frame of a run. `type` drives how the UI folds it into the thread. */
100
+ export type AiAgentStreamEvent = {
101
+ type: 'text.delta' | 'text.done' | 'reasoning.delta' | 'reasoning.done' | 'tool.start' | 'tool.done' | 'tool.error' | 'step.done' | 'turn.done' | 'done' | 'error' | string;
102
+ [key: string]: unknown;
103
+ };
104
+ export type AiAgentPublish = (event: AiAgentStreamEvent) => void;
105
+ export interface AiAgentTokenUsage {
106
+ inputTokens?: number;
107
+ outputTokens?: number;
108
+ totalTokens?: number;
109
+ }
110
+ /** One selectable LLM inside an agent service. */
111
+ export interface AiAgentModelOption {
112
+ id: string;
113
+ contextWindow: number | null;
114
+ maxOutputTokens: number | null;
115
+ vision: boolean;
116
+ /** Relative spend multiplier (×1 = median-priced model); null if unknown. */
117
+ costCoefficient: number | null;
118
+ [key: string]: unknown;
119
+ }
120
+ /** State of the user's server-side agent session. */
121
+ export interface AiAgentSessionMeta {
122
+ model: string;
123
+ contextWindow: number;
124
+ vision: boolean;
125
+ turns: number;
126
+ contextTokens: number;
127
+ totalUsage: AiAgentTokenUsage;
128
+ [key: string]: unknown;
129
+ }
130
+ /**
131
+ * Whether the service can talk to its LLM yet. Services that need no
132
+ * provisioning simply omit `getConnectionStatus()` and are treated as ready.
133
+ */
134
+ export interface AiAgentConnectionStatus {
135
+ state: 'ready' | 'registering' | 'waiting_retry' | 'setup_required' | 'error';
136
+ provider?: string;
137
+ baseUrl?: string;
138
+ source?: string | null;
139
+ nextAttemptAt?: string | null;
140
+ lastError?: string | null;
141
+ expiresAt?: string | null;
142
+ [key: string]: unknown;
143
+ }
144
+ /** States for which an agent can describe its connection screen. */
145
+ export type AiAgentConnectionState = Exclude<AiAgentConnectionStatus['state'], 'ready'>;
146
+ /**
147
+ * Declarative connection screen supplied by an agent service. The shared panel
148
+ * owns rendering, while the agent owns its copy, CTA and which status details
149
+ * are useful to expose.
150
+ */
151
+ export interface AiAgentConnectionScreen {
152
+ title?: string;
153
+ description?: string;
154
+ icon?: 'spinner' | 'bot' | 'error';
155
+ action?: {
156
+ label: string;
157
+ href: string;
158
+ };
159
+ /** Status values the agent wants to render on this screen. */
160
+ details?: Array<{
161
+ field: string;
162
+ label?: string;
163
+ format?: 'text' | 'local-time';
164
+ tone?: 'muted' | 'error';
165
+ }>;
166
+ }
167
+ /** Normalized budget / rate limits of the provider key, when it exposes them. */
168
+ export interface AiAgentLimits {
169
+ provider: string;
170
+ supported: boolean;
171
+ overBudget?: boolean;
172
+ message?: string;
173
+ maxBudgetUsd?: number | null;
174
+ spentUsd?: number | null;
175
+ remainingBudgetUsd?: number | null;
176
+ budgetResetAt?: string | null;
177
+ burstMaxBudgetUsd?: number | null;
178
+ burstSpentUsd?: number | null;
179
+ burstRemainingBudgetUsd?: number | null;
180
+ burstResetAt?: string | null;
181
+ burstDurationLabel?: string | null;
182
+ rpmLimit?: number | null;
183
+ tpmLimit?: number | null;
184
+ fetchedAt?: string;
185
+ [key: string]: unknown;
186
+ }
187
+ /**
188
+ * Copy the panel shows instead of its neutral defaults. `getUiHints(locale)`
189
+ * receives the admin user's locale, so agents can return localized copy.
190
+ */
191
+ export interface AiAgentUiHints {
192
+ title?: string;
193
+ welcomeHint?: string;
194
+ composerPlaceholder?: string;
195
+ suggestions?: string[];
196
+ /** Setting an operator must fill in before the service can connect. */
197
+ setupSetting?: string;
198
+ /** Where to fill it in; rendered as a button on the connection loader. */
199
+ setupUrl?: string;
200
+ /**
201
+ * Per-state pages rendered while this agent is not ready. `default` is
202
+ * used for any state without its own screen.
203
+ */
204
+ connectionScreens?: Partial<Record<AiAgentConnectionState | 'default', AiAgentConnectionScreen>>;
205
+ [key: string]: unknown;
206
+ }
207
+ /** A browser-side capability which an agent may call through `ui.method`. */
208
+ export interface AiAgentUiMethod {
209
+ id: string;
210
+ title: string;
211
+ description: string;
212
+ inputSchema: Record<string, unknown>;
213
+ action: string;
214
+ accessRightsToken?: string;
215
+ }
216
+ /** Declarative UI contract consumed by the shared assistant panel. */
217
+ export interface AiAgentUiSchema extends AiAgentUiHints {
218
+ commands: Array<{
219
+ id: string;
220
+ description?: string;
221
+ }>;
222
+ uiMethods?: AiAgentUiMethod[];
223
+ panels: {
224
+ history: boolean;
225
+ models: boolean;
226
+ limits: boolean;
227
+ };
228
+ }
229
+ /** An uploaded file as handed to the agent service (multer memory storage). */
230
+ export interface AiAgentUploadedFile {
231
+ originalname: string;
232
+ mimetype: string;
233
+ size: number;
234
+ buffer: Buffer;
235
+ }
236
+ /** What the service reports back about a stored upload, if it stores them. */
237
+ export interface AiAgentSavedFile {
238
+ id?: string;
239
+ name?: string;
240
+ [key: string]: unknown;
241
+ }
@@ -27,6 +27,10 @@ import { ControllerHandler } from "./app-manager/ControllerHandler.js";
27
27
  import { AssetHandler } from "./app-manager/AssetHandler.js";
28
28
  import { ConfigLayerHandler } from "./app-manager/ConfigLayerHandler.js";
29
29
  import { AccessRightsHandler } from "./access-rights/AccessRightsHandler.js";
30
+ import { AiAssistantUiMethodHandler } from './ai-assistant/AiAssistantUiMethodHandler.js';
31
+ import { AdminLinkHandler } from './admin-links/AdminLinkHandler.js';
32
+ import { AiAssistantAgentSkillHandler } from './ai-assistant/AiAssistantAgentSkillHandler.js';
33
+ import { DocumentationHandler } from './docs/DocumentationHandler.js';
30
34
  export declare function createAdminizerLogger(): winston.Logger;
31
35
  export interface EmitAsyncOptions {
32
36
  /**
@@ -54,6 +58,10 @@ export declare class Adminizer {
54
58
  notificationHandler: NotificationHandler;
55
59
  historyHandler: HistoryHandler;
56
60
  private _aiAssistantHandler?;
61
+ aiAssistantUiMethodHandler: AiAssistantUiMethodHandler;
62
+ adminLinkHandler: AdminLinkHandler;
63
+ aiAssistantAgentSkillHandler: AiAssistantAgentSkillHandler;
64
+ documentationHandler: DocumentationHandler;
57
65
  modelHandler: ModelHandler;
58
66
  widgetHandler: WidgetHandler;
59
67
  customFilterHandler: CustomFilterHandler;
package/lib/Adminizer.js CHANGED
@@ -37,12 +37,18 @@ import { bindCors } from "../system/bindCors.js";
37
37
  import bindHistory from "../system/bindHistory.js";
38
38
  import bindCustomFilterHandlers from "../system/bindCustomFilterHandlers.js";
39
39
  import { FeedbackHandler } from "./feedback/FeedbackHandler.js";
40
- import { buildInternalModelAccess } from "../system/buildInternalModelAccess.js";
40
+ import { refreshInternalModelAccess } from "../system/buildInternalModelAccess.js";
41
+ import { validateAccessGraph } from "./access-graph/AccessGraphResolver.js";
41
42
  import { AppManager } from "./app-manager/AppManager.js";
42
43
  import { ControllerHandler } from "./app-manager/ControllerHandler.js";
43
44
  import { AssetHandler } from "./app-manager/AssetHandler.js";
44
45
  import { ConfigLayerHandler } from "./app-manager/ConfigLayerHandler.js";
45
46
  import { AccessRightsHandler } from "./access-rights/AccessRightsHandler.js";
47
+ import { AiAssistantUiMethodHandler } from './ai-assistant/AiAssistantUiMethodHandler.js';
48
+ import { AdminLinkHandler } from './admin-links/AdminLinkHandler.js';
49
+ import { AiAssistantAgentSkillHandler } from './ai-assistant/AiAssistantAgentSkillHandler.js';
50
+ import { DocumentationHandler } from './docs/DocumentationHandler.js';
51
+ import bindDocs from '../system/bindDocs.js';
46
52
  const logFilePath = "logs/app.log";
47
53
  function ensureLogDirectory(filePath) {
48
54
  const logDirectory = path.dirname(filePath);
@@ -109,6 +115,10 @@ export class Adminizer {
109
115
  notificationHandler;
110
116
  historyHandler;
111
117
  _aiAssistantHandler;
118
+ aiAssistantUiMethodHandler;
119
+ adminLinkHandler;
120
+ aiAssistantAgentSkillHandler;
121
+ documentationHandler;
112
122
  modelHandler;
113
123
  widgetHandler;
114
124
  customFilterHandler;
@@ -141,10 +151,19 @@ export class Adminizer {
141
151
  this.catalogTemplateComponentHandler = new CatalogTemplateComponentHandler();
142
152
  this.mediaManagerHandler = new MediaManagerHandler();
143
153
  this.feedbackHandler = new FeedbackHandler(this);
154
+ this.aiAssistantUiMethodHandler = new AiAssistantUiMethodHandler(this);
155
+ this.adminLinkHandler = new AdminLinkHandler(this);
156
+ this.aiAssistantAgentSkillHandler = new AiAssistantAgentSkillHandler(this);
157
+ this.documentationHandler = new DocumentationHandler(this, () => bindDocs(this));
144
158
  this.controllerHandler = new ControllerHandler(this);
145
159
  this.assetHandler = new AssetHandler(this);
146
160
  this.configLayerHandler = new ConfigLayerHandler(this);
147
161
  this.appManager = new AppManager(this);
162
+ // App model (un)registration changes what the record-access resolvers may query,
163
+ // so the internal allowlists must follow; config-layer rebuilds refresh them
164
+ // synchronously inside rebuildConfig itself.
165
+ this.emitter.on("app:model:registered", () => refreshInternalModelAccess(this));
166
+ this.emitter.on("app:model:unregistered", () => refreshInternalModelAccess(this));
148
167
  }
149
168
  /**
150
169
  * Shared AI assistant model registry.
@@ -257,7 +276,10 @@ export class Adminizer {
257
276
  this.emitter.emit('adminizer:init');
258
277
  // validate models
259
278
  validateSystemModels(this);
260
- this.modelHandler.configureInternalAccess(buildInternalModelAccess(this.config, this.modelHandler));
279
+ refreshInternalModelAccess(this);
280
+ // Fail loud on structural accessGraph errors at boot; later config rebuilds only
281
+ // log them and the affected models fail closed at access time.
282
+ validateAccessGraph(this);
261
283
  bindCustomFilterHandlers(this);
262
284
  this.config.rootPath = path.resolve(import.meta.dirname + "/..");
263
285
  await this.middlewareManager.loadMiddlewares();
@@ -293,6 +315,11 @@ export class Adminizer {
293
315
  bindHistory(this);
294
316
  this.router = new Router(this);
295
317
  await this.router.bind(); // must be after binding policies and req/res functions
318
+ // Documentation (knowledge base) wires itself on register(); this call
319
+ // covers a service registered before the config was read. It runs after
320
+ // the router so those routes sit behind the same middleware stack a
321
+ // service registered after init() gets.
322
+ this.documentationHandler.activate();
296
323
  /**
297
324
  * Adminizer loaded
298
325
  * This call is used so that other apps can know that the admin panel is present in the panel and has been loaded, and can activate their logic.