adminizer 5.0.0-build.9 → 5.1.0-build.25

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-BTgLIXrb.js +1 -0
  2. package/assets/add-C8V8qjvm.js +1 -0
  3. package/assets/add-group-Ezq_Ocot.js +1 -0
  4. package/assets/add-user-CluNf2QG.js +1 -0
  5. package/assets/ai-assistant/agent.es.js +28464 -0
  6. package/assets/app-C96pcs-J.js +138 -0
  7. package/assets/app-DPmI-MvN.css +1 -0
  8. package/assets/arc-G6n0TtRf.js +1 -0
  9. package/assets/architectureDiagram-5GKGNRK7-TQ4_iSAG.js +36 -0
  10. package/assets/asmcrypto-DZeQH0LC.js +1 -0
  11. package/assets/blockDiagram-I7D4REHJ-1q050zQd.js +129 -0
  12. package/assets/c4Diagram-7LVT6UL2-BXn0hW62.js +38 -0
  13. package/assets/{catalog-dk6a0I1u.js → catalog-Cpx4OH_r.js} +3 -3
  14. package/assets/channel-JZzXP_E5.js +1 -0
  15. package/assets/chunk-2Q5K7J3B-BSnocdBz.js +1 -0
  16. package/assets/chunk-5VM5RSS4-BvfoMSpq.js +15 -0
  17. package/assets/chunk-F27PBJKO-CMyFcBlX.js +1 -0
  18. package/assets/chunk-IMKFNOWR--8Y02hwt.js +231 -0
  19. package/assets/chunk-JWPE2WC7-C18m6v8d.js +1 -0
  20. package/assets/chunk-POPQ4Y6H-DzoTZe0a.js +1 -0
  21. package/assets/chunk-SVP7TREG-Ce98IXh7.js +88 -0
  22. package/assets/chunk-TICWLB2K-BeLoUlaL.js +206 -0
  23. package/assets/chunk-XXDRQBXY-DCZSZ1aW.js +1 -0
  24. package/assets/classDiagram-ZZMXUADV-jA4zeKxs.js +1 -0
  25. package/assets/classDiagram-v2-VYDZK3BY-jA4zeKxs.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-Cy9np608.js +1 -0
  31. package/assets/cynefin-OW5HDTMX-Ba6uP79R.js +166 -0
  32. package/assets/cynefinDiagram-5FMLGOSQ-DhMBBq7T.js +62 -0
  33. package/assets/cytoscape.esm-DzSz-X2X.js +321 -0
  34. package/assets/dagre-GXQ25YYZ-BdAnQUCV.js +4 -0
  35. package/assets/dashboard-BFVFBIio.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-BDYUGbNl.js +30 -0
  39. package/assets/diagram-UQ7AKVKN-PURK0-X-.js +41 -0
  40. package/assets/diagram-VSXAHHWV-DxHdFxl9.js +3 -0
  41. package/assets/diagram-VX7I27RA-Vz8xmeYc.js +24 -0
  42. package/assets/diagram-Z3DM3KII-CFXUpBMy.js +24 -0
  43. package/assets/docs-DU3h-HtS.js +1 -0
  44. package/assets/ebnfDiagram-PWID7BFC-BXNvxMGP.js +1 -0
  45. package/assets/erDiagram-RLTQ6QDP-DN0GgZEI.js +99 -0
  46. package/assets/flowDiagram-HODETNUW-DXdnwgBv.js +168 -0
  47. package/assets/ganttDiagram-EL5Y4UJY-Cv8_dxX3.js +292 -0
  48. package/assets/gitGraphDiagram-WWUBYQGX-BO8T858E.js +106 -0
  49. package/assets/history-BmwWpckq.js +1 -0
  50. package/assets/infoDiagram-27XIBGKW-DMSJ1RNc.js +2 -0
  51. package/assets/init-Gi6I4Gst.js +1 -0
  52. package/assets/init-user-BIdB8U_4.js +1 -0
  53. package/assets/isObject-DL-BuaBl.js +1 -0
  54. package/assets/ishikawaDiagram-5VMMS53U-_tLJESbl.js +70 -0
  55. package/assets/journeyDiagram-3NMN7TZE-DMMhdo-_.js +139 -0
  56. package/assets/kanban-definition-UXKFOSKX-CxQrB4t6.js +89 -0
  57. package/assets/katex-HP8lGamR.js +257 -0
  58. package/assets/layout-_oS_xxCJ.js +1 -0
  59. package/assets/linear-VLYuNK3x.js +1 -0
  60. package/assets/{list-CcMtS4hD.js → list-YTyJQtiR.js} +2 -2
  61. package/assets/login-B9GlhjZh.js +1 -0
  62. package/assets/manifest.json +834 -58
  63. package/assets/media-manager-Bb1VWlzd.js +6 -0
  64. package/assets/mermaid.core-BknV2vft.js +310 -0
  65. package/assets/mindmap-definition-YA3MSWOX-DBjjjDq4.js +96 -0
  66. package/assets/module-DQ1NK8Xg.js +1 -0
  67. package/assets/notification-CgNOsXvn.js +20 -0
  68. package/assets/ordinal-Cboi1Yqb.js +1 -0
  69. package/assets/pegDiagram-XKGWAZYB-uRznTaO4.js +1 -0
  70. package/assets/pieDiagram-E7YTZNPT-D1TkCH8I.js +39 -0
  71. package/assets/quadrantDiagram-AXDQQJYC-BofJ36Jz.js +7 -0
  72. package/assets/railroadDiagram-O6MQD6OU-e1ZM2IT1.js +1 -0
  73. package/assets/register-DjTtMayF.js +1 -0
  74. package/assets/requirementDiagram-BXWQKSXE-DQWArRbb.js +84 -0
  75. package/assets/ru-CqAtka69.js +1 -0
  76. package/assets/sankeyDiagram-P5KCCOFB-fVOu2jzi.js +40 -0
  77. package/assets/sequenceDiagram-WJ2MYXX4-Dj24cqpu.js +162 -0
  78. package/assets/sizeCapture-INFHLROL-CsApVrVi.js +1 -0
  79. package/assets/stateDiagram-D77RDMKH-IU2Szlid.js +1 -0
  80. package/assets/stateDiagram-v2-MP3YSRHH-B-EW3cIC.js +1 -0
  81. package/assets/swimlanes-42K2YHIH-97rBqTR-.js +1 -0
  82. package/assets/swimlanesDiagram-VR7AAH4N-CqG2iydK.js +8 -0
  83. package/assets/timeline-definition-24CTP7MA-DSgRzx1V.js +120 -0
  84. package/assets/toNumber-DZb-r9sy.js +1 -0
  85. package/assets/use-appearance-CXz2xWgT.js +1 -0
  86. package/assets/user-filters-list-BAR4CBN_.js +1 -0
  87. package/assets/vennDiagram-4TSXK5OY-BqMksFxE.js +34 -0
  88. package/assets/wardleyDiagram-VM6X3IG4-BMmxRODC.js +78 -0
  89. package/assets/welcome-CPXT7_4b.js +1 -0
  90. package/assets/with-app-layout-C6dqetfx.js +31 -0
  91. package/assets/xychartDiagram-S5SC5T6Z-3xRuF0l4.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 +63 -2
  114. package/helpers/accessRightsHelper.js +202 -13
  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 +30 -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 +40 -1
  173. package/lib/app-manager/AppManager.js +48 -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 +61 -2
  190. package/lib/model/ModelHandler.js +136 -8
  191. package/lib/model/adapter/sequelize.d.ts +40 -1
  192. package/lib/model/adapter/sequelize.js +243 -77
  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 +9 -0
  211. package/system/buildInternalModelAccess.js +37 -4
  212. package/system/defaults.js +7 -1
  213. package/system/validateSystemModels.js +29 -9
  214. package/assets/add-CBlLpLr0.js +0 -1
  215. package/assets/add-group-H0IKIsNm.js +0 -1
  216. package/assets/add-user-CoTLmxtF.js +0 -1
  217. package/assets/app-DNXiApCk.css +0 -1
  218. package/assets/app-layout-xxJpLAvb.js +0 -1
  219. package/assets/app.js +0 -138
  220. package/assets/dashboard-DVJS5EPP.js +0 -3
  221. package/assets/debounce-BMmXVQ06.js +0 -1
  222. package/assets/history-bWRuzbye.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-CqO_BKsH.js +0 -1
  227. package/assets/notification-B5l2wwmi.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-C7kMKN5f.js +0 -1
  232. package/assets/welcome-6z-7sXnQ.js +0 -1
@@ -1,5 +1,5 @@
1
1
  import inertiaActionsHelper from "./inertiaActionsHelper.js";
2
- export function inertiaListHelper(modelResource, req, fields, activeFilterName) {
2
+ export async function inertiaListHelper(modelResource, req, fields, activeFilterName) {
3
3
  const actionType = 'list';
4
4
  // Check if filters are enabled for this model
5
5
  // Preferred: model-level `models.<Model>.filters`; fallback: legacy top-level `modelFilters.<Model>`
@@ -31,23 +31,22 @@ export function inertiaListHelper(modelResource, req, fields, activeFilterName)
31
31
  defaultPageSize: req.adminizer.config.list?.defaultPageSize ?? 50,
32
32
  pageSizeOptions: [5, 20, 50],
33
33
  };
34
- if (modelResource.config.add && req.adminizer.accessRightsHelper.hasPermission(`create-${modelResource.name}-model`, req.user, `CRUD create on model "${modelResource.name}"`)) {
34
+ if (modelResource.config.add && await req.adminizer.accessRightsHelper.checkPermission(`create-${modelResource.name}-model`, req.user)) {
35
35
  props.crudActions.createTitle = req.i18n.__('create');
36
36
  }
37
- if (modelResource.config.edit && req.adminizer.accessRightsHelper.hasPermission(`update-${modelResource.name}-model`, req.user, `CRUD update on model "${modelResource.name}"`)) {
37
+ if (modelResource.config.edit && await req.adminizer.accessRightsHelper.checkPermission(`update-${modelResource.name}-model`, req.user)) {
38
38
  props.crudActions.editTitle = req.i18n.__('Edit');
39
39
  }
40
- if (modelResource.config.view && req.adminizer.accessRightsHelper.hasPermission(`read-${modelResource.name}-model`, req.user, `CRUD read on model "${modelResource.name}"`)) {
40
+ if (modelResource.config.view && await req.adminizer.accessRightsHelper.checkPermission(`read-${modelResource.name}-model`, req.user)) {
41
41
  props.crudActions.viewsTitle = req.i18n.__('View');
42
42
  }
43
- if (modelResource.config.remove && req.adminizer.accessRightsHelper.hasPermission(`delete-${modelResource.name}-model`, req.user, `CRUD delete on model "${modelResource.name}"`)) {
43
+ if (modelResource.config.remove && await req.adminizer.accessRightsHelper.checkPermission(`delete-${modelResource.name}-model`, req.user)) {
44
44
  props.crudActions.deleteTitle = req.i18n.__('Delete');
45
45
  }
46
- props.actions = inertiaActionsHelper(actionType, modelResource, req);
46
+ props.actions = await inertiaActionsHelper(actionType, modelResource, req);
47
47
  if (req.adminizer.menuHelper.hasInlineActions(modelResource.config, 'list')) {
48
48
  for (const inlineAction of req.adminizer.menuHelper.getInlineActions(modelResource.config, 'list')) {
49
- const context = `inline action "${inlineAction.title}" (${inlineAction.id}) on model "${modelResource.name}"`;
50
- if (req.adminizer.accessRightsHelper.hasPermission(inlineAction.accessRightsToken, req.user, context)) {
49
+ if (await req.adminizer.accessRightsHelper.checkPermission(inlineAction.accessRightsToken, req.user)) {
51
50
  props.inlineActions.push({
52
51
  icon: inlineAction.icon,
53
52
  id: inlineAction.id,
@@ -1,12 +1,17 @@
1
1
  import { Adminizer } from "../lib/Adminizer.js";
2
+ import { NavbarSectionConfig } from "../interfaces/adminpanelConfig.js";
2
3
  import { MenuItem } from "./menuHelper.js";
3
4
  export declare class InertiaMenuHelper {
4
5
  private adminizer;
5
6
  constructor(adminizer: Adminizer);
6
- getMenuItems(req: ReqType): MenuItem[];
7
- private filterAccessibleItems;
7
+ getMenuItems(req: ReqType): Promise<MenuItem[]>;
8
8
  private translateMenuItem;
9
9
  private translateHrefItem;
10
+ /**
11
+ * Navbar section metadata keyed by the *translated* section name, so the UI
12
+ * can look it up with the already-translated `section` of a menu item.
13
+ */
14
+ getSections(req: ReqType): Record<string, NavbarSectionConfig>;
10
15
  getBrandTitle(): string;
11
16
  getLogoutUrl(): string;
12
17
  }
@@ -1,35 +1,12 @@
1
+ import { listAccessibleMenuItems } from "./navigationAccessHelper.js";
1
2
  export class InertiaMenuHelper {
2
3
  adminizer;
3
4
  constructor(adminizer) {
4
5
  this.adminizer = adminizer;
5
6
  }
6
- getMenuItems(req) {
7
- const menu = [];
8
- for (const menuItem of this.adminizer.menuHelper.getMenuItems(req.user)) {
9
- const filteredActions = this.filterAccessibleItems(menuItem.actions ?? [], req);
10
- const menuItemTokens = filteredActions.map(item => {
11
- return item.accessRightsToken;
12
- }).filter(item => {
13
- return item;
14
- });
15
- if (menuItem.accessRightsToken)
16
- menuItemTokens.push(menuItem.accessRightsToken);
17
- if (this.adminizer.accessRightsHelper.enoughPermissions(menuItemTokens, req.user)) {
18
- menu.push(this.translateMenuItem(req, {
19
- ...menuItem,
20
- actions: filteredActions
21
- }));
22
- }
23
- }
24
- return menu;
25
- }
26
- filterAccessibleItems(items, req) {
27
- return items
28
- .filter((item) => !item.accessRightsToken || this.adminizer.accessRightsHelper.hasPermission(item.accessRightsToken, req.user))
29
- .map((item) => ({
30
- ...item,
31
- subItems: item.subItems ? this.filterAccessibleItems(item.subItems, req) : item.subItems
32
- }));
7
+ async getMenuItems(req) {
8
+ return (await listAccessibleMenuItems(this.adminizer, req.user))
9
+ .map((menuItem) => this.translateMenuItem(req, menuItem));
33
10
  }
34
11
  translateMenuItem(req, menuItem) {
35
12
  return {
@@ -47,6 +24,14 @@ export class InertiaMenuHelper {
47
24
  subItems: item.subItems?.map((subItem) => this.translateHrefItem(req, subItem)),
48
25
  };
49
26
  }
27
+ /**
28
+ * Navbar section metadata keyed by the *translated* section name, so the UI
29
+ * can look it up with the already-translated `section` of a menu item.
30
+ */
31
+ getSections(req) {
32
+ const sections = this.adminizer.menuHelper.getSections();
33
+ return Object.fromEntries(Object.entries(sections).map(([name, section]) => [req.i18n.__(name), section]));
34
+ }
50
35
  getBrandTitle() {
51
36
  return this.adminizer.menuHelper.getBrandTitle();
52
37
  }
@@ -4,7 +4,7 @@
4
4
  * @constructor
5
5
  */
6
6
  import { User } from "../models/User.js";
7
- import { ActionType, AdminpanelConfig, HrefConfig, ModelConfig } from "../interfaces/adminpanelConfig.js";
7
+ import { ActionType, AdminpanelConfig, HrefConfig, ModelConfig, NavbarSectionConfig } from "../interfaces/adminpanelConfig.js";
8
8
  export type MenuItem = {
9
9
  link: string;
10
10
  title: string;
@@ -28,6 +28,16 @@ export declare class MenuHelper {
28
28
  * @returns {string}
29
29
  */
30
30
  getBrandTitle(): string;
31
+ /**
32
+ * Presentation metadata of navbar sections, keyed by section name.
33
+ *
34
+ * Only sections declared under `navbar.sections` are present: a section
35
+ * used by links but never configured simply has no entry, and the UI falls
36
+ * back to its default rendering.
37
+ *
38
+ * @returns {Record<string, NavbarSectionConfig>}
39
+ */
40
+ getSections(): Record<string, NavbarSectionConfig>;
31
41
  /**
32
42
  * Check if global actions buttons added to action
33
43
  *
@@ -63,6 +63,18 @@ export class MenuHelper {
63
63
  }
64
64
  return 'Adminizer';
65
65
  }
66
+ /**
67
+ * Presentation metadata of navbar sections, keyed by section name.
68
+ *
69
+ * Only sections declared under `navbar.sections` are present: a section
70
+ * used by links but never configured simply has no entry, and the UI falls
71
+ * back to its default rendering.
72
+ *
73
+ * @returns {Record<string, NavbarSectionConfig>}
74
+ */
75
+ getSections() {
76
+ return { ...(this.config.navbar?.sections ?? {}) };
77
+ }
66
78
  /**
67
79
  * Check if global actions buttons added to action
68
80
  *
@@ -0,0 +1,9 @@
1
+ import type { ModelConfig } from '../interfaces/adminpanelConfig.js';
2
+ import type { ModelResource } from '../interfaces/types.js';
3
+ import type { Adminizer } from '../lib/Adminizer.js';
4
+ /** Config defaults of a model resource declared as `true` in the panel config. */
5
+ export declare function normalizeModelConfig(name: string, config: ModelConfig | boolean): ModelConfig;
6
+ /** Every configured model resource whose model is actually registered. */
7
+ export declare function listModelResources(adminizer: Adminizer): ModelResource[];
8
+ /** Resolves a resource by its Adminizer name, then by a uniquely registered host model name. */
9
+ export declare function resolveModelResource(adminizer: Adminizer, modelName: string): ModelResource | undefined;
@@ -0,0 +1,43 @@
1
+ /** Config defaults of a model resource declared as `true` in the panel config. */
2
+ export function normalizeModelConfig(name, config) {
3
+ const baseConfig = {
4
+ model: name,
5
+ icon: "description",
6
+ title: name,
7
+ list: true,
8
+ add: true,
9
+ edit: true,
10
+ remove: true,
11
+ view: true,
12
+ };
13
+ if (typeof config === "boolean") {
14
+ return config ? baseConfig : { ...baseConfig, list: false, add: false, edit: false, remove: false, view: false };
15
+ }
16
+ return { ...baseConfig, ...config };
17
+ }
18
+ /** Every configured model resource whose model is actually registered. */
19
+ export function listModelResources(adminizer) {
20
+ const resources = [];
21
+ for (const [configName, configValue] of Object.entries(adminizer.config.models ?? {})) {
22
+ const config = normalizeModelConfig(configName, configValue);
23
+ const model = adminizer.modelHandler.getResource(configName);
24
+ if (!model)
25
+ continue;
26
+ resources.push({
27
+ name: configName,
28
+ uri: `${adminizer.config.routePrefix}/model/${configName}`,
29
+ config,
30
+ model,
31
+ });
32
+ }
33
+ return resources;
34
+ }
35
+ /** Resolves a resource by its Adminizer name, then by a uniquely registered host model name. */
36
+ export function resolveModelResource(adminizer, modelName) {
37
+ const requestedName = modelName.trim();
38
+ const resourceName = adminizer.modelHandler.getResourceRecord(requestedName)?.name
39
+ ?? adminizer.modelHandler.resolveResourceByHostModel(requestedName);
40
+ return resourceName
41
+ ? listModelResources(adminizer).find((resource) => resource.name === resourceName)
42
+ : undefined;
43
+ }
@@ -0,0 +1,13 @@
1
+ import type { HrefConfig } from '../interfaces/adminpanelConfig.js';
2
+ import type { Adminizer } from '../lib/Adminizer.js';
3
+ import type { User } from '../models/User.js';
4
+ import type { MenuItem } from './menuHelper.js';
5
+ /** Drops the sub-items this user may not open, recursively. */
6
+ export declare function filterAccessibleHrefItems(adminizer: Adminizer, user: User, items: HrefConfig[]): Promise<HrefConfig[]>;
7
+ /**
8
+ * The navigation menu a user may actually open: configured models, additional
9
+ * links and app-contributed pages, filtered with the very same rules the
10
+ * sidebar uses. Titles are left untranslated, so callers that need i18n (the
11
+ * Inertia page props) translate on top of this.
12
+ */
13
+ export declare function listAccessibleMenuItems(adminizer: Adminizer, user: User): Promise<MenuItem[]>;
@@ -0,0 +1,45 @@
1
+ /** Drops the sub-items this user may not open, recursively. */
2
+ export async function filterAccessibleHrefItems(adminizer, user, items) {
3
+ const accessible = [];
4
+ for (const item of items) {
5
+ if (item.accessRightsToken && !await adminizer.accessRightsHelper.checkPermission(item.accessRightsToken, user)) {
6
+ continue;
7
+ }
8
+ accessible.push({
9
+ ...item,
10
+ subItems: item.subItems ? await filterAccessibleHrefItems(adminizer, user, item.subItems) : item.subItems,
11
+ });
12
+ }
13
+ return accessible;
14
+ }
15
+ /**
16
+ * The navigation menu a user may actually open: configured models, additional
17
+ * links and app-contributed pages, filtered with the very same rules the
18
+ * sidebar uses. Titles are left untranslated, so callers that need i18n (the
19
+ * Inertia page props) translate on top of this.
20
+ */
21
+ export async function listAccessibleMenuItems(adminizer, user) {
22
+ const menu = [];
23
+ for (const menuItem of adminizer.menuHelper.getMenuItems(user)) {
24
+ const actions = await filterAccessibleHrefItems(adminizer, user, menuItem.actions ?? []);
25
+ const tokens = actions.map((item) => item.accessRightsToken).filter(Boolean);
26
+ if (menuItem.accessRightsToken)
27
+ tokens.push(menuItem.accessRightsToken);
28
+ if (await adminizer.accessRightsHelper.checkAnyPermission(tokens, user)) {
29
+ menu.push({ ...menuItem, actions });
30
+ }
31
+ }
32
+ for (const link of await adminizer.adminLinkHandler.list(user)) {
33
+ menu.push({
34
+ id: link.id,
35
+ link: link.link,
36
+ title: link.title,
37
+ type: link.type === 'blank' ? 'blank' : 'self',
38
+ actions: null,
39
+ icon: null,
40
+ accessRightsToken: link.accessRightsToken ?? null,
41
+ section: link.section || 'Platform',
42
+ });
43
+ }
44
+ return menu;
45
+ }
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;