adminizer 5.0.0-build.2 → 5.0.0-build.21
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.
- package/assets/abnfDiagram-VCTEODGH-CkmsvdDi.js +1 -0
- package/assets/add-CESPH_aI.js +1 -0
- package/assets/add-group-CQ4Xm8Ru.js +1 -0
- package/assets/add-user-B-QNjR4B.js +1 -0
- package/assets/ai-assistant/agent.es.js +28362 -0
- package/assets/app-C5Ju5Zra.css +1 -0
- package/assets/app-Ca5yUSBv.js +138 -0
- package/assets/arc-B-Dt8YNl.js +1 -0
- package/assets/architectureDiagram-5GKGNRK7-Ca58kq-l.js +36 -0
- package/assets/asmcrypto-DZeQH0LC.js +1 -0
- package/assets/blockDiagram-NRAW4CY4-CuqE52IG.js +129 -0
- package/assets/c4Diagram-UCG6FXSJ-Cx8z-0Bw.js +38 -0
- package/assets/catalog-CkSbxvx0.js +6 -0
- package/assets/channel-C9z6Pi_j.js +1 -0
- package/assets/chunk-2Q5K7J3B-CXISVKTC.js +1 -0
- package/assets/chunk-5VM5RSS4-DYGeeVYe.js +15 -0
- package/assets/chunk-F27PBJKO-BeY-dbKj.js +1 -0
- package/assets/chunk-G27WJ6UU-DeX_eH7C.js +231 -0
- package/assets/chunk-JWPE2WC7-DaxI9q0u.js +1 -0
- package/assets/chunk-LCL6LL3I-B7PkaEz_.js +206 -0
- package/assets/chunk-POPQ4Y6H-lwu6ZpsN.js +1 -0
- package/assets/chunk-SVP7TREG-CVFllgl1.js +88 -0
- package/assets/chunk-XXDRQBXY-DDLN0rcg.js +1 -0
- package/assets/classDiagram-DTDB5LWJ-BKtCIC3B.js +1 -0
- package/assets/classDiagram-v2-JRS7N3AN-BKtCIC3B.js +1 -0
- package/assets/controls/ckeditor.es.js +9370 -9306
- package/assets/controls/handsontable.es.js +11971 -11938
- package/assets/controls/jsoneditor.es.js +13104 -13008
- package/assets/controls/leaflet.es.js +1023 -1004
- package/assets/controls/toast-ui.es.js +1843 -1834
- package/assets/cose-bilkent-JH36ORCC-CWRyUkhQ.js +1 -0
- package/assets/cynefin-OW5HDTMX-B1iLDXCC.js +166 -0
- package/assets/cynefinDiagram-5FMLGOSQ-DaXQHr3l.js +62 -0
- package/assets/cytoscape.esm-yzknjiTM.js +321 -0
- package/assets/dagre-3AP2YEHR-DFORa4Hz.js +4 -0
- package/assets/dashboard-Ssa1_Of3.js +3 -0
- package/assets/debounce-B3mXI_sx.js +1 -0
- package/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/assets/diagram-S7CK7UJ4-CgP6KBo0.js +30 -0
- package/assets/diagram-UQ7AKVKN-oYfQe6w0.js +41 -0
- package/assets/diagram-VSXAHHWV-DfCU5wgt.js +3 -0
- package/assets/diagram-VX7I27RA-BITJVrNY.js +24 -0
- package/assets/diagram-Z3DM3KII-D8J9Ssjy.js +24 -0
- package/assets/docs-DHMt_Rq1.js +1 -0
- package/assets/ebnfDiagram-PWID7BFC-qpNmoYgx.js +1 -0
- package/assets/erDiagram-SSCWMZ5O-DDF-Y0zs.js +99 -0
- package/assets/flowDiagram-A5DVABFB-CjKVkR9T.js +168 -0
- package/assets/ganttDiagram-EL5Y4UJY-BXGQ53w6.js +292 -0
- package/assets/gitGraphDiagram-WWUBYQGX-BLMevdLh.js +106 -0
- package/assets/history-tBOo2gd5.js +1 -0
- package/assets/infoDiagram-RXCK75RN-BAnPdnyw.js +2 -0
- package/assets/init-Gi6I4Gst.js +1 -0
- package/assets/init-user-DrNUHa6r.js +1 -0
- package/assets/isObject-DL-BuaBl.js +1 -0
- package/assets/ishikawaDiagram-5VMMS53U-PnL0Z4Li.js +70 -0
- package/assets/journeyDiagram-EYS64GPL-CLcsCSwF.js +139 -0
- package/assets/kanban-definition-3QL26DDD-C2eU3uBE.js +89 -0
- package/assets/katex-HP8lGamR.js +257 -0
- package/assets/layout-BXoYdHdx.js +1 -0
- package/assets/linear-BSzrP0oP.js +1 -0
- package/assets/list-CLg6xeIK.js +4 -0
- package/assets/login-Ce7o3-o_.js +1 -0
- package/assets/manifest.json +834 -58
- package/assets/{media-manager-BDbpSSjH.js → media-manager-D7G1P3fz.js} +3 -3
- package/assets/mermaid.core-aKMJHeEk.js +310 -0
- package/assets/mindmap-definition-FBJOCRG2-Dzbz5ghA.js +96 -0
- package/assets/module-BsXph_pA.js +1 -0
- package/assets/notification-D-Zz1gF_.js +20 -0
- package/assets/ordinal-Cboi1Yqb.js +1 -0
- package/assets/pegDiagram-XKGWAZYB-BTdWkU0Y.js +1 -0
- package/assets/pieDiagram-E7YTZNPT-Dnw8-TB-.js +39 -0
- package/assets/quadrantDiagram-AXDQQJYC-CALzLlg9.js +7 -0
- package/assets/railroadDiagram-O6MQD6OU-CTA1vzcv.js +1 -0
- package/assets/register-CNnBkzej.js +1 -0
- package/assets/requirementDiagram-EFPCY7ZU-UX2CF5HA.js +84 -0
- package/assets/ru-CoNgAwFK.js +1 -0
- package/assets/sankeyDiagram-P5KCCOFB-4aPp3ATV.js +40 -0
- package/assets/sequenceDiagram-WJ2MYXX4--kltkwJw.js +162 -0
- package/assets/sizeCapture-X5ZJPWSS-Ca9-g5QY.js +1 -0
- package/assets/stateDiagram-HBIQ2CUA-Diaf5Q7I.js +1 -0
- package/assets/stateDiagram-v2-4QOOHH4V-C6FlRp7u.js +1 -0
- package/assets/swimlanes-XN3QIQJK-DhqX_myE.js +1 -0
- package/assets/swimlanesDiagram-VK2B7HYN-i_3LTEBk.js +8 -0
- package/assets/timeline-definition-24CTP7MA-CwkJQbhq.js +120 -0
- package/assets/toNumber-DZb-r9sy.js +1 -0
- package/assets/use-appearance-DC-DESKB.js +1 -0
- package/assets/user-filters-list-W47DTBp2.js +1 -0
- package/assets/vennDiagram-4TSXK5OY-BNM3_cVS.js +34 -0
- package/assets/wardleyDiagram-VM6X3IG4-BODb1NP8.js +78 -0
- package/assets/welcome-BeBm6NIi.js +1 -0
- package/assets/with-app-layout-B90DtzYC.js +31 -0
- package/assets/xychartDiagram-S5SC5T6Z-DthqEh3g.js +7 -0
- package/controllers/access-rights/permissionOptions.d.ts +2 -0
- package/controllers/access-rights/permissionOptions.js +23 -0
- package/controllers/add.js +1 -1
- package/controllers/addGroup.js +15 -6
- package/controllers/ai/AiAgentController.d.ts +66 -0
- package/controllers/ai/AiAgentController.js +435 -0
- package/controllers/ai/AiAssistantController.d.ts +0 -4
- package/controllers/ai/AiAssistantController.js +4 -78
- package/controllers/catalog/FrontendCatalogAdapter.d.ts +1 -1
- package/controllers/catalog/FrontentCatalogAdapter.d.ts +1 -1
- package/controllers/docs/index.d.ts +22 -0
- package/controllers/docs/index.js +126 -0
- package/controllers/edit.js +1 -1
- package/controllers/editGroup.js +15 -6
- package/controllers/feedback/feedbackController.d.ts +1 -1
- package/controllers/filter-fields/savedFilters.js +1 -1
- package/controllers/history-actions/HistoryController.d.ts +1 -1
- package/controllers/history-actions/HistoryController.js +6 -6
- package/controllers/list.js +1 -1
- package/controllers/login.js +1 -1
- package/controllers/media-manager/mediaManagerAdapter.d.ts +1 -1
- package/controllers/notifications/NotificationController.js +14 -8
- package/controllers/view.js +2 -2
- package/controllers/widgets/filterInfo.js +1 -1
- package/helpers/accessRightsHelper.d.ts +11 -4
- package/helpers/accessRightsHelper.js +82 -12
- package/helpers/assetVersionHelper.d.ts +14 -0
- package/helpers/assetVersionHelper.js +42 -0
- package/helpers/configHelper.d.ts +3 -3
- package/helpers/configHelper.js +20 -18
- package/helpers/controllerHelper.d.ts +3 -2
- package/helpers/controllerHelper.js +25 -15
- package/helpers/fieldsHelper.d.ts +2 -2
- package/helpers/fieldsHelper.js +3 -3
- package/helpers/filterDatetimeHelper.d.ts +1 -1
- package/helpers/inertiaActionsHelper.d.ts +3 -3
- package/helpers/inertiaActionsHelper.js +4 -5
- package/helpers/inertiaAddHelper.d.ts +5 -5
- package/helpers/inertiaAddHelper.js +7 -6
- package/helpers/inertiaGroupHelper.d.ts +3 -3
- package/helpers/inertiaGroupHelper.js +10 -2
- package/helpers/inertiaListHelper.d.ts +4 -4
- package/helpers/inertiaListHelper.js +7 -8
- package/helpers/inertiaMenuHelper.d.ts +2 -3
- package/helpers/inertiaMenuHelper.js +3 -26
- package/helpers/inertiaUserHelper.d.ts +3 -3
- package/helpers/menuHelper.d.ts +2 -2
- package/helpers/modelResourceHelper.d.ts +9 -0
- package/helpers/modelResourceHelper.js +43 -0
- package/helpers/navigationAccessHelper.d.ts +13 -0
- package/helpers/navigationAccessHelper.js +40 -0
- package/helpers/translationHelper.d.ts +2 -2
- package/index.d.ts +69 -58
- package/index.js +11 -0
- package/interfaces/adminpanelConfig.d.ts +18 -5
- package/interfaces/global.d.ts +6 -6
- package/interfaces/internalModelAccess.d.ts +1 -1
- package/interfaces/listQueryBuilder.d.ts +1 -1
- package/interfaces/types.d.ts +171 -2
- package/lib/Adminizer.d.ts +46 -26
- package/lib/Adminizer.js +37 -1
- package/lib/DataAccessor.d.ts +7 -6
- package/lib/DataAccessor.js +50 -48
- package/lib/MiddlewareManager.d.ts +1 -1
- package/lib/access-rights/AccessRightsHandler.d.ts +2 -2
- package/lib/admin-links/AdminLinkHandler.d.ts +72 -0
- package/lib/admin-links/AdminLinkHandler.js +175 -0
- package/lib/ai-assistant/AbstractAiConversationHistoryService.d.ts +39 -0
- package/lib/ai-assistant/AbstractAiConversationHistoryService.js +98 -0
- package/lib/ai-assistant/AbstractAiModelService.d.ts +140 -3
- package/lib/ai-assistant/AbstractAiModelService.js +154 -1
- package/lib/ai-assistant/AiAgentRunManager.d.ts +35 -0
- package/lib/ai-assistant/AiAgentRunManager.js +109 -0
- package/lib/ai-assistant/AiAssistantAgentSkillHandler.d.ts +58 -0
- package/lib/ai-assistant/AiAssistantAgentSkillHandler.js +87 -0
- package/lib/ai-assistant/AiAssistantHandler.d.ts +16 -5
- package/lib/ai-assistant/AiAssistantHandler.js +35 -1
- package/lib/ai-assistant/AiAssistantUiMethodHandler.d.ts +60 -0
- package/lib/ai-assistant/AiAssistantUiMethodHandler.js +138 -0
- package/lib/ai-assistant/builtinAgentSkills.d.ts +9 -0
- package/lib/ai-assistant/builtinAgentSkills.js +256 -0
- package/lib/ai-assistant/jsonSafe.d.ts +13 -0
- package/lib/ai-assistant/jsonSafe.js +81 -0
- package/lib/app-manager/AdminizerApp.d.ts +40 -11
- package/lib/app-manager/AppManager.d.ts +2 -2
- package/lib/app-manager/AppManager.js +54 -50
- package/lib/app-manager/AssetHandler.d.ts +2 -2
- package/lib/app-manager/ConfigLayerHandler.d.ts +2 -2
- package/lib/app-manager/ControllerHandler.d.ts +2 -2
- package/lib/catalog/AbstractCatalog.d.ts +1 -1
- package/lib/catalog/CatalogHandler.d.ts +1 -1
- package/lib/controls/ControlsHandler.d.ts +1 -1
- package/lib/controls/codeEditor/MonacoEditor.d.ts +1 -1
- package/lib/controls/geojsoneditor/GeoEditor.d.ts +1 -1
- package/lib/controls/jsoneditor/JsonEditor.d.ts +1 -1
- package/lib/controls/markdown/ToastUiEditor.d.ts +1 -1
- package/lib/controls/table/Handsontable.d.ts +1 -1
- package/lib/controls/wysiwyg/CKeditor.d.ts +1 -1
- package/lib/docs/AbstractDocumentation.d.ts +100 -0
- package/lib/docs/AbstractDocumentation.js +108 -0
- package/lib/docs/DocumentationHandler.d.ts +100 -0
- package/lib/docs/DocumentationHandler.js +198 -0
- package/lib/docs/FileDocumentation.d.ts +47 -0
- package/lib/docs/FileDocumentation.js +189 -0
- package/lib/docs/docsAgentSkills.d.ts +10 -0
- package/lib/docs/docsAgentSkills.js +93 -0
- package/lib/feedback/FeedbackHandler.d.ts +2 -2
- package/lib/filters/ConditionValidator.d.ts +1 -1
- package/lib/filters/FilterCustomFieldHandler.d.ts +2 -2
- package/lib/filters/FilterService.d.ts +4 -4
- package/lib/filters/FilterService.js +4 -3
- package/lib/filters/index.d.ts +6 -6
- package/lib/history-actions/AbstractHistoryAdapter.d.ts +6 -6
- package/lib/history-actions/AbstractHistoryAdapter.js +11 -9
- package/lib/history-actions/DefaultHistoryAdapter.d.ts +4 -4
- package/lib/history-actions/HistoryHandler.d.ts +1 -1
- package/lib/list-query-builder/ListQueryBuilder.d.ts +7 -7
- package/lib/media-manager/AbstractMediaManager.d.ts +2 -2
- package/lib/media-manager/MediaManagerHandler.d.ts +1 -1
- package/lib/media-manager/Thumb.d.ts +1 -1
- package/lib/media-manager/helpers/MediaManagerHelper.d.ts +5 -5
- package/lib/model/AbstractModel.d.ts +11 -4
- package/lib/model/InternalModelAccessFactory.d.ts +2 -2
- package/lib/model/ModelHandler.d.ts +50 -5
- package/lib/model/ModelHandler.js +107 -8
- package/lib/model/adapter/sequelize.d.ts +7 -5
- package/lib/model/adapter/sequelize.js +79 -46
- package/lib/model/adapter/typeorm.d.ts +2 -2
- package/lib/model/adapter/typeorm.js +1 -1
- package/lib/model/internalModelRepository.d.ts +2 -2
- package/lib/notifications/AbstractNotificationService.d.ts +4 -4
- package/lib/notifications/GeneralNotificationService.d.ts +2 -2
- package/lib/notifications/GeneralNotificationService.js +6 -5
- package/lib/notifications/NotificationHandler.d.ts +2 -2
- package/lib/notifications/SystemNotificationService.d.ts +2 -2
- package/lib/notifications/SystemNotificationService.js +1 -1
- package/lib/requestProcessor.d.ts +1 -1
- package/lib/ui-i18n/uiTranslationKeys.d.ts +2 -1
- package/lib/ui-i18n/uiTranslationKeys.js +42 -1
- package/lib/widgets/abstractAction.d.ts +1 -1
- package/lib/widgets/abstractCustom.d.ts +2 -2
- package/lib/widgets/abstractInfo.d.ts +1 -1
- package/lib/widgets/abstractLink.d.ts +2 -2
- package/lib/widgets/abstractSwitch.d.ts +1 -1
- package/lib/widgets/abstractWidgetBase.d.ts +1 -1
- package/lib/widgets/widgetHandler.d.ts +9 -9
- package/lib/widgets/widgetHandler.js +5 -1
- package/models/Filter.d.ts +1 -1
- package/models/FilterColumn.d.ts +1 -1
- package/models/Group.d.ts +3 -2
- package/models/HistoryActions.d.ts +1 -1
- package/models/MediaManagerAP.d.ts +2 -2
- package/models/MediaManagerAssociationsAP.d.ts +1 -1
- package/models/MediaManagerMetaAP.d.ts +1 -1
- package/models/User.d.ts +2 -2
- package/models/UserNotification.d.ts +1 -1
- package/package.json +29 -20
- package/policies/authPolicies.js +6 -6
- package/policies/permissionResolvers.d.ts +1 -0
- package/policies/permissionResolvers.js +3 -0
- package/services/FeedService.d.ts +3 -3
- package/system/Router.d.ts +1 -1
- package/system/Router.js +27 -1
- package/system/bindAccessRights.d.ts +1 -1
- package/system/bindAccessRights.js +2 -3
- package/system/bindAuthorization.d.ts +1 -1
- package/system/bindControls.d.ts +1 -1
- package/system/bindCors.d.ts +1 -1
- package/system/bindCustomFilterHandlers.d.ts +1 -1
- package/system/bindDashboardWidgets.d.ts +1 -1
- package/system/bindDev.d.ts +1 -1
- package/system/bindDocs.d.ts +13 -0
- package/system/bindDocs.js +77 -0
- package/system/bindHistory.d.ts +1 -1
- package/system/bindInertia.d.ts +1 -1
- package/system/bindInertia.js +23 -1
- package/system/bindMediaManager.d.ts +1 -1
- package/system/bindNotifications.d.ts +1 -1
- package/system/bindReqFunctions.d.ts +1 -1
- package/system/bindTranslations.d.ts +1 -1
- package/system/buildInternalModelAccess.d.ts +3 -3
- package/system/buildInternalModelAccess.js +5 -2
- package/system/defaults.d.ts +1 -1
- package/system/routePolicies.d.ts +1 -1
- package/system/systemModelContracts.d.ts +1 -1
- package/system/systemModelContracts.js +11 -7
- package/system/validateSystemModels.d.ts +1 -1
- package/system/validateSystemModels.js +29 -9
- package/assets/add-group-BdHFzKng.js +0 -1
- package/assets/add-p6yGCoZ7.js +0 -1
- package/assets/add-user-CDspCLu4.js +0 -1
- package/assets/app-DNXiApCk.css +0 -1
- package/assets/app-layout-DCr_-JY3.js +0 -1
- package/assets/app.js +0 -138
- package/assets/catalog-XN-_p9TB.js +0 -6
- package/assets/dashboard-CHZO2QFY.js +0 -3
- package/assets/debounce-BMmXVQ06.js +0 -1
- package/assets/history-CQd0FojQ.js +0 -1
- package/assets/init-user-8aUohVDq.js +0 -1
- package/assets/list-PHWgTHsg.js +0 -4
- package/assets/login-CO4laf6m.js +0 -1
- package/assets/module-DVyQtIxm.js +0 -1
- package/assets/notification-B87XG8nQ.js +0 -20
- package/assets/register-DBYIMXGQ.js +0 -1
- package/assets/ru-rYKdQbWF.js +0 -1
- package/assets/use-appearance-_EbbjHTh.js +0 -1
- package/assets/user-filters-list-BRzDbPkx.js +0 -1
- package/assets/welcome-DiXmrTgc.js +0 -1
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import type { ActionType, AdminpanelConfig, AdminpanelIcon, ModelConfig } from "../../interfaces/adminpanelConfig";
|
|
2
|
-
import type { AccessRightsToken, INotification, ModelResource } from "../../interfaces/types";
|
|
3
|
-
import type { AbstractCatalog } from "../catalog/AbstractCatalog";
|
|
4
|
-
import type { CatalogTemplateComponentResource } from "../catalog/CatalogTemplateComponentHandler";
|
|
5
|
-
import type { AppModelAccess } from "../model/ModelHandler";
|
|
6
|
-
import type { AbstractMediaManager } from "../media-manager/AbstractMediaManager";
|
|
7
|
-
import type { Config, ControlType } from "../controls/Control";
|
|
8
|
-
import type { User } from "../../models/User";
|
|
9
|
-
import type { DataAccessor } from "../DataAccessor";
|
|
10
|
-
import type { AbstractAiModelService } from "../ai-assistant/AbstractAiModelService";
|
|
1
|
+
import type { ActionType, AdminpanelConfig, AdminpanelIcon, ModelConfig } from "../../interfaces/adminpanelConfig.js";
|
|
2
|
+
import type { AccessRightsToken, INotification, ModelResource, PermissionContext } from "../../interfaces/types.js";
|
|
3
|
+
import type { AbstractCatalog } from "../catalog/AbstractCatalog.js";
|
|
4
|
+
import type { CatalogTemplateComponentResource } from "../catalog/CatalogTemplateComponentHandler.js";
|
|
5
|
+
import type { AppModelAccess } from "../model/ModelHandler.js";
|
|
6
|
+
import type { AbstractMediaManager } from "../media-manager/AbstractMediaManager.js";
|
|
7
|
+
import type { Config, ControlType } from "../controls/Control.js";
|
|
8
|
+
import type { User } from "../../models/User.js";
|
|
9
|
+
import type { DataAccessor } from "../DataAccessor.js";
|
|
10
|
+
import type { AbstractAiModelService } from "../ai-assistant/AbstractAiModelService.js";
|
|
11
|
+
import type { AiAssistantUiMethod } from '../ai-assistant/AiAssistantUiMethodHandler.js';
|
|
12
|
+
import type { AdminLink, AdminLinkTemplate } from '../admin-links/AdminLinkHandler.js';
|
|
13
|
+
import type { AiAssistantAgentSkill } from '../ai-assistant/AiAssistantAgentSkillHandler.js';
|
|
11
14
|
export type AppDisposer = () => void | Promise<void>;
|
|
12
15
|
export type AppEventName = string | symbol;
|
|
13
16
|
export type AppEventHandler<TPayload = any> = (payload: TPayload, runtime: AppRuntime) => void | Promise<void>;
|
|
@@ -127,9 +130,14 @@ export type AppWidgetResource = AppInfoWidgetResource | AppActionWidgetResource
|
|
|
127
130
|
export interface AppRuntime {
|
|
128
131
|
models: AppModelAccess;
|
|
129
132
|
config: AppRuntimeConfig;
|
|
133
|
+
accessRights: AppRuntimeAccessRights;
|
|
130
134
|
notifications: AppRuntimeNotifications;
|
|
131
135
|
apps: AppRuntimeApps;
|
|
132
136
|
}
|
|
137
|
+
export interface AppRuntimeAccessRights {
|
|
138
|
+
hasPermission(token: string, user: User, context?: PermissionContext): Promise<boolean>;
|
|
139
|
+
getPermissionRights(token: string, user: User): string[] | null;
|
|
140
|
+
}
|
|
133
141
|
export interface AppRuntimeConfig {
|
|
134
142
|
readonly routePrefix: string;
|
|
135
143
|
getModelConfig(modelName: string): ModelConfig | undefined;
|
|
@@ -171,12 +179,28 @@ export interface AppAiAssistantContext {
|
|
|
171
179
|
routePrefix: string;
|
|
172
180
|
getModelResources(): ModelResource[];
|
|
173
181
|
resolveModelResource(modelName: string): ModelResource | undefined;
|
|
174
|
-
hasPermission(token: string, user: User): boolean
|
|
182
|
+
hasPermission(token: string, user: User): boolean | Promise<boolean>;
|
|
175
183
|
createDataAccessor(modelResource: ModelResource, user: User, action: ActionType): DataAccessor;
|
|
184
|
+
/** UI tools available to this user, including methods registered by apps. */
|
|
185
|
+
getUiMethods(user: User): AiAssistantUiMethod[];
|
|
176
186
|
}
|
|
177
187
|
export interface AppAiAssistantResource {
|
|
178
188
|
models: Array<(context: AppAiAssistantContext) => AbstractAiModelService | Promise<AbstractAiModelService>>;
|
|
179
189
|
}
|
|
190
|
+
export interface AppAiAssistantUiMethodResource extends AiAssistantUiMethod {
|
|
191
|
+
id: string;
|
|
192
|
+
}
|
|
193
|
+
export interface AppAiAssistantAgentSkillResource extends AiAssistantAgentSkill {
|
|
194
|
+
}
|
|
195
|
+
/** Skills contributed by an app. UI methods are browser skills; agent skills run on the server. */
|
|
196
|
+
export interface AppSkills {
|
|
197
|
+
uiMethod(method: AppAiAssistantUiMethodResource): void;
|
|
198
|
+
agent(skill: AppAiAssistantAgentSkillResource): void;
|
|
199
|
+
}
|
|
200
|
+
export interface AppAdminLinkResource extends AdminLink {
|
|
201
|
+
}
|
|
202
|
+
export interface AppAdminLinkTemplateResource extends AdminLinkTemplate {
|
|
203
|
+
}
|
|
180
204
|
/**
|
|
181
205
|
* Resource registration API provided while an app is being enabled.
|
|
182
206
|
*
|
|
@@ -197,6 +221,11 @@ export interface AppSetupContext {
|
|
|
197
221
|
model(model: AppModelResource): void;
|
|
198
222
|
modelAccess(access: AppModelAccessResource): void;
|
|
199
223
|
aiAssistant(resource: AppAiAssistantResource): void;
|
|
224
|
+
skills: AppSkills;
|
|
225
|
+
/** Register a standalone server page in the admin navigation and agent search. */
|
|
226
|
+
adminLink(link: AppAdminLinkResource): void;
|
|
227
|
+
/** Register a parametrized page (e.g. `/admin/orders/:id/invoice`) the assistant may open. */
|
|
228
|
+
adminLinkTemplate(template: AppAdminLinkTemplateResource): void;
|
|
200
229
|
listener(event: AppEventName, handler: AppEventHandler): void;
|
|
201
230
|
}
|
|
202
231
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Adminizer } from "../Adminizer";
|
|
2
|
-
import { AbstractAdminizerApp, AppRuntime, AppRuntimeAppState } from "./AdminizerApp";
|
|
1
|
+
import { Adminizer } from "../Adminizer.js";
|
|
2
|
+
import { AbstractAdminizerApp, AppRuntime, AppRuntimeAppState } from "./AdminizerApp.js";
|
|
3
3
|
export type AppState = AppRuntimeAppState;
|
|
4
4
|
export declare class AppManager {
|
|
5
5
|
private adminizer;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Adminizer } from "../Adminizer.js";
|
|
2
|
-
import { AiAssistantHandler } from "../ai-assistant/AiAssistantHandler.js";
|
|
3
2
|
import { DataAccessor } from "../DataAccessor.js";
|
|
3
|
+
import { listModelResources, resolveModelResource } from "../../helpers/modelResourceHelper.js";
|
|
4
4
|
class RuntimeAppSetupContext {
|
|
5
5
|
adminizer;
|
|
6
6
|
appName;
|
|
@@ -8,6 +8,8 @@ class RuntimeAppSetupContext {
|
|
|
8
8
|
pendingModelRegistrations = [];
|
|
9
9
|
pendingModelAccessRegistrations = [];
|
|
10
10
|
pendingAiAssistantRegistrations = [];
|
|
11
|
+
pendingAiAssistantUiMethodRegistrations = [];
|
|
12
|
+
pendingAiAssistantAgentSkillRegistrations = [];
|
|
11
13
|
pendingMediaManagerRegistrations = [];
|
|
12
14
|
pendingCatalogRegistrations = [];
|
|
13
15
|
configLayerIndex = 0;
|
|
@@ -15,7 +17,12 @@ class RuntimeAppSetupContext {
|
|
|
15
17
|
constructor(adminizer, appName) {
|
|
16
18
|
this.adminizer = adminizer;
|
|
17
19
|
this.appName = appName;
|
|
20
|
+
this.skills = {
|
|
21
|
+
uiMethod: (method) => this.registerAiAssistantUiMethod(method),
|
|
22
|
+
agent: (skill) => this.registerAiAssistantAgentSkill(skill),
|
|
23
|
+
};
|
|
18
24
|
}
|
|
25
|
+
skills;
|
|
19
26
|
asset(asset) {
|
|
20
27
|
const resourceId = `${this.appName}:${asset.id}`;
|
|
21
28
|
const url = this.adminizer.assetHandler.register(this.appName, asset);
|
|
@@ -115,6 +122,32 @@ class RuntimeAppSetupContext {
|
|
|
115
122
|
aiAssistant(resource) {
|
|
116
123
|
this.pendingAiAssistantRegistrations.push(() => this.registerAiAssistant(resource));
|
|
117
124
|
}
|
|
125
|
+
registerAiAssistantUiMethod(method) {
|
|
126
|
+
this.pendingAiAssistantUiMethodRegistrations.push(async () => {
|
|
127
|
+
this.adminizer.aiAssistantUiMethodHandler.register(method, this.appName);
|
|
128
|
+
this.disposers.push(() => this.adminizer.aiAssistantUiMethodHandler.unregister(method.id, this.appName));
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
registerAiAssistantAgentSkill(skill) {
|
|
132
|
+
this.pendingAiAssistantAgentSkillRegistrations.push(async () => {
|
|
133
|
+
this.adminizer.aiAssistantAgentSkillHandler.add(skill, this.appName);
|
|
134
|
+
this.disposers.push(() => {
|
|
135
|
+
this.adminizer.aiAssistantAgentSkillHandler.remove(skill.id, this.appName);
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
adminLink(link) {
|
|
140
|
+
const resourceId = this.adminizer.adminLinkHandler.add(link, this.appName);
|
|
141
|
+
this.disposers.push(() => {
|
|
142
|
+
this.adminizer.adminLinkHandler.remove(resourceId, this.appName);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
adminLinkTemplate(template) {
|
|
146
|
+
const resourceId = this.adminizer.adminLinkHandler.addTemplate(template, this.appName);
|
|
147
|
+
this.disposers.push(() => {
|
|
148
|
+
this.adminizer.adminLinkHandler.removeTemplate(resourceId, this.appName);
|
|
149
|
+
});
|
|
150
|
+
}
|
|
118
151
|
listener(event, handler) {
|
|
119
152
|
const resourceId = `${this.appName}:${event.toString()}:${this.disposers.length + 1}`;
|
|
120
153
|
const listener = (payload) => handler(payload, this.adminizer.appManager.createRuntime(this.appName));
|
|
@@ -137,28 +170,34 @@ class RuntimeAppSetupContext {
|
|
|
137
170
|
await this.runRegistrationPhase(this.pendingModelRegistrations);
|
|
138
171
|
await this.runRegistrationPhase(this.pendingModelAccessRegistrations);
|
|
139
172
|
await this.runRegistrationPhase(this.pendingAiAssistantRegistrations);
|
|
173
|
+
await this.runRegistrationPhase(this.pendingAiAssistantUiMethodRegistrations);
|
|
174
|
+
await this.runRegistrationPhase(this.pendingAiAssistantAgentSkillRegistrations);
|
|
140
175
|
await this.runRegistrationPhase(this.pendingMediaManagerRegistrations);
|
|
141
176
|
await this.runRegistrationPhase(this.pendingCatalogRegistrations);
|
|
142
177
|
}
|
|
143
178
|
async registerAiAssistant(resource) {
|
|
144
|
-
|
|
145
|
-
|
|
179
|
+
// Models go into the shared handler instead of replacing it, so models
|
|
180
|
+
// registered directly by the host application keep working and several
|
|
181
|
+
// apps can contribute models at the same time.
|
|
182
|
+
const handler = this.adminizer.aiAssistantHandler;
|
|
146
183
|
const context = this.createAiAssistantContext();
|
|
184
|
+
const modelIds = [];
|
|
147
185
|
for (const modelFactory of resource.models) {
|
|
148
186
|
const model = await modelFactory(context);
|
|
149
|
-
handler.registerModel(model);
|
|
187
|
+
handler.registerModel(model, this.appName);
|
|
188
|
+
modelIds.push(model.id);
|
|
150
189
|
}
|
|
151
|
-
this.adminizer.aiAssistantHandler = handler;
|
|
152
190
|
this.adminizer.emitter.emit("app:ai-assistant:registered", {
|
|
153
191
|
appName: this.appName,
|
|
154
|
-
models:
|
|
192
|
+
models: modelIds,
|
|
155
193
|
});
|
|
156
194
|
this.disposers.push(() => {
|
|
157
|
-
|
|
158
|
-
this.adminizer.aiAssistantHandler
|
|
195
|
+
for (const modelId of modelIds) {
|
|
196
|
+
this.adminizer.aiAssistantHandler.unregisterModel(modelId, this.appName);
|
|
159
197
|
}
|
|
160
198
|
this.adminizer.emitter.emit("app:ai-assistant:unregistered", {
|
|
161
199
|
appName: this.appName,
|
|
200
|
+
models: modelIds,
|
|
162
201
|
});
|
|
163
202
|
});
|
|
164
203
|
}
|
|
@@ -347,52 +386,13 @@ class RuntimeAppSetupContext {
|
|
|
347
386
|
return {
|
|
348
387
|
runtime: this.adminizer.appManager.createRuntime(this.appName),
|
|
349
388
|
routePrefix: this.adminizer.config.routePrefix,
|
|
350
|
-
getModelResources: () => this.
|
|
351
|
-
resolveModelResource: (modelName) => this.
|
|
389
|
+
getModelResources: () => listModelResources(this.adminizer),
|
|
390
|
+
resolveModelResource: (modelName) => resolveModelResource(this.adminizer, modelName),
|
|
352
391
|
hasPermission: (token, user) => this.adminizer.accessRightsHelper.hasPermission(token, user),
|
|
353
392
|
createDataAccessor: (modelResource, user, action) => new DataAccessor(this.adminizer, user, modelResource, action),
|
|
393
|
+
getUiMethods: (user) => this.adminizer.aiAssistantUiMethodHandler.getAvailable(user),
|
|
354
394
|
};
|
|
355
395
|
}
|
|
356
|
-
getModelResources() {
|
|
357
|
-
const resources = [];
|
|
358
|
-
for (const [configName, configValue] of Object.entries(this.adminizer.config.models ?? {})) {
|
|
359
|
-
const normalizedConfig = this.normalizeModelConfig(configName, configValue);
|
|
360
|
-
const model = this.adminizer.modelHandler.model.get(normalizedConfig.model.toLowerCase());
|
|
361
|
-
if (!model) {
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
resources.push({
|
|
365
|
-
name: configName,
|
|
366
|
-
uri: `${this.adminizer.config.routePrefix}/model/${configName}`,
|
|
367
|
-
config: normalizedConfig,
|
|
368
|
-
model,
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
return resources;
|
|
372
|
-
}
|
|
373
|
-
resolveModelResource(modelName) {
|
|
374
|
-
const loweredName = modelName.toLowerCase();
|
|
375
|
-
return this.getModelResources().find((resource) => {
|
|
376
|
-
const modelId = resource.config.model?.toLowerCase();
|
|
377
|
-
return resource.name.toLowerCase() === loweredName || modelId === loweredName;
|
|
378
|
-
});
|
|
379
|
-
}
|
|
380
|
-
normalizeModelConfig(name, config) {
|
|
381
|
-
const baseConfig = {
|
|
382
|
-
model: name,
|
|
383
|
-
icon: "description",
|
|
384
|
-
title: name,
|
|
385
|
-
list: true,
|
|
386
|
-
add: true,
|
|
387
|
-
edit: true,
|
|
388
|
-
remove: true,
|
|
389
|
-
view: true,
|
|
390
|
-
};
|
|
391
|
-
if (typeof config === "boolean") {
|
|
392
|
-
return config ? baseConfig : { ...baseConfig, list: false, add: false, edit: false, remove: false, view: false };
|
|
393
|
-
}
|
|
394
|
-
return { ...baseConfig, ...config };
|
|
395
|
-
}
|
|
396
396
|
}
|
|
397
397
|
export class AppManager {
|
|
398
398
|
adminizer;
|
|
@@ -505,6 +505,10 @@ export class AppManager {
|
|
|
505
505
|
return modelKey ? models[modelKey] : undefined;
|
|
506
506
|
},
|
|
507
507
|
},
|
|
508
|
+
accessRights: {
|
|
509
|
+
hasPermission: async (token, user, context) => await this.adminizer.accessRightsHelper.hasPermission(token, user, context),
|
|
510
|
+
getPermissionRights: (token, user) => this.adminizer.accessRightsHelper.getPermissionRights(token, user),
|
|
511
|
+
},
|
|
508
512
|
notifications: {
|
|
509
513
|
send: (notification) => this.adminizer.sendNotification(notification),
|
|
510
514
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Adminizer } from "../Adminizer";
|
|
2
|
-
import type { AppConfigPatch } from "./AdminizerApp";
|
|
1
|
+
import type { Adminizer } from "../Adminizer.js";
|
|
2
|
+
import type { AppConfigPatch } from "./AdminizerApp.js";
|
|
3
3
|
export interface ConfigLayerRecord {
|
|
4
4
|
id: string;
|
|
5
5
|
appName: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Adminizer } from "../Adminizer";
|
|
2
|
-
import type { AppController, AppControllerMethod } from "./AdminizerApp";
|
|
1
|
+
import type { Adminizer } from "../Adminizer.js";
|
|
2
|
+
import type { AppController, AppControllerMethod } from "./AdminizerApp.js";
|
|
3
3
|
interface ControllerRecord {
|
|
4
4
|
id: string;
|
|
5
5
|
appName: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Control, ControlType, Path, Config } from "../Control";
|
|
1
|
+
import { Control, ControlType, Path, Config } from "../Control.js";
|
|
2
2
|
export declare class JsonEditor implements Control {
|
|
3
3
|
readonly config: Record<string, string | string[] | object | number | boolean>;
|
|
4
4
|
readonly name: string;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Control, ControlType, Path, Config } from "../Control";
|
|
1
|
+
import { Control, ControlType, Path, Config } from "../Control.js";
|
|
2
2
|
export declare class ToastUiEditor implements Control {
|
|
3
3
|
readonly config: Record<string, string | string[] | object | number | boolean>;
|
|
4
4
|
readonly name: string;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Adminizer } from '../Adminizer.js';
|
|
2
|
+
export interface DocMeta {
|
|
3
|
+
/** Stable document identifier; the only handle that ever leaves the abstraction. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Title in the requested locale when a translation exists. */
|
|
6
|
+
title: string;
|
|
7
|
+
/** Viewer grouping. */
|
|
8
|
+
section?: string;
|
|
9
|
+
keywords: string[];
|
|
10
|
+
/** Model resource names this document is about. */
|
|
11
|
+
models: string[];
|
|
12
|
+
/** Express-style admin path patterns (`:id` placeholders) the document is bound to. */
|
|
13
|
+
urls: string[];
|
|
14
|
+
/**
|
|
15
|
+
* Per-document access right, required on top of the base documentation
|
|
16
|
+
* token. References an existing token (registered by the host or an app);
|
|
17
|
+
* documentation never creates tokens itself.
|
|
18
|
+
*/
|
|
19
|
+
accessRightsToken?: string;
|
|
20
|
+
/** Locales this document is available in. */
|
|
21
|
+
locales?: string[];
|
|
22
|
+
}
|
|
23
|
+
export interface DocContent {
|
|
24
|
+
meta: DocMeta;
|
|
25
|
+
/** Content in the locale that was actually served. */
|
|
26
|
+
markdown: string;
|
|
27
|
+
locale: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DocSearchQuery {
|
|
30
|
+
/** Full-text query; matched against title, keywords and content. */
|
|
31
|
+
query?: string;
|
|
32
|
+
/** Every listed keyword must be present on the document. */
|
|
33
|
+
keywords?: string[];
|
|
34
|
+
/** Narrow the scope to documents bound to this admin path. */
|
|
35
|
+
url?: string;
|
|
36
|
+
/** Narrow the scope to documents bound to this model. */
|
|
37
|
+
model?: string;
|
|
38
|
+
}
|
|
39
|
+
export interface DocSearchResult {
|
|
40
|
+
meta: DocMeta;
|
|
41
|
+
/** Matched lines with a little surrounding context, best matches first. */
|
|
42
|
+
snippets: string[];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The single contract of the documentation subsystem. Everything adminizer
|
|
46
|
+
* does with documentation — listing, reading, searching, resolving links —
|
|
47
|
+
* goes through this class; where documents live, how translations are stored
|
|
48
|
+
* and how search works are private details of the implementation.
|
|
49
|
+
*
|
|
50
|
+
* One implementation is active per adminizer instance (a singleton registered
|
|
51
|
+
* on startup). Implementations know nothing about users or permissions: the
|
|
52
|
+
* access layer is applied by adminizer on top of every result.
|
|
53
|
+
*
|
|
54
|
+
* Only `list` and `get` are required. The base class implements search,
|
|
55
|
+
* context matching, the keyword map and link resolution on top of them; an
|
|
56
|
+
* implementation overrides any of those when it can do better (e.g. database
|
|
57
|
+
* full-text search).
|
|
58
|
+
*/
|
|
59
|
+
export declare abstract class AbstractDocumentation {
|
|
60
|
+
/** Set when the implementation is registered; not part of the contract. */
|
|
61
|
+
adminizer: Adminizer;
|
|
62
|
+
/**
|
|
63
|
+
* Metadata of every document, without content, in the requested locale.
|
|
64
|
+
* Falls back to the best available translation per document.
|
|
65
|
+
*/
|
|
66
|
+
abstract list(locale?: string): Promise<DocMeta[]>;
|
|
67
|
+
/**
|
|
68
|
+
* One document in the requested locale. How the translation is obtained
|
|
69
|
+
* is up to the implementation; when the locale is unavailable it serves
|
|
70
|
+
* the best available version and reports which locale it actually is.
|
|
71
|
+
*/
|
|
72
|
+
abstract get(id: string, locale?: string): Promise<DocContent | undefined>;
|
|
73
|
+
/**
|
|
74
|
+
* Keyword filter plus full-text scan over `list()`/`get()`. Content is
|
|
75
|
+
* only fetched for documents that survive the metadata filters.
|
|
76
|
+
*/
|
|
77
|
+
search(query: DocSearchQuery, locale?: string): Promise<DocSearchResult[]>;
|
|
78
|
+
/** Documents bound to a place in the admin panel: a URL and/or a model. */
|
|
79
|
+
forContext(ctx: {
|
|
80
|
+
url?: string;
|
|
81
|
+
model?: string;
|
|
82
|
+
}, locale?: string): Promise<DocMeta[]>;
|
|
83
|
+
/** Keyword → document ids, aggregated from metadata. */
|
|
84
|
+
keywords(locale?: string): Promise<Map<string, string[]>>;
|
|
85
|
+
/**
|
|
86
|
+
* Resolves a reference written inside `fromId` to the id of the target
|
|
87
|
+
* document. How authors spell references is implementation-specific; the
|
|
88
|
+
* default only understands plain document ids (with an optional anchor).
|
|
89
|
+
*/
|
|
90
|
+
resolveLink(_fromId: string, ref: string): Promise<string | undefined>;
|
|
91
|
+
/**
|
|
92
|
+
* Change notification: consumers subscribe to drop their caches. Optional;
|
|
93
|
+
* implementations without it are treated as static.
|
|
94
|
+
*/
|
|
95
|
+
onChange?(cb: (ids?: string[]) => void): void;
|
|
96
|
+
/** Matches an Express-style pattern (`/model/:name`) against a concrete path. */
|
|
97
|
+
protected matchesUrl(pattern: string, url: string): boolean;
|
|
98
|
+
/** Lines containing the query, trimmed to a little context around the match. */
|
|
99
|
+
protected extractSnippets(markdown: string, query: string): string[];
|
|
100
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
const SNIPPET_LIMIT = 3;
|
|
2
|
+
const SNIPPET_CONTEXT = 120;
|
|
3
|
+
/**
|
|
4
|
+
* The single contract of the documentation subsystem. Everything adminizer
|
|
5
|
+
* does with documentation — listing, reading, searching, resolving links —
|
|
6
|
+
* goes through this class; where documents live, how translations are stored
|
|
7
|
+
* and how search works are private details of the implementation.
|
|
8
|
+
*
|
|
9
|
+
* One implementation is active per adminizer instance (a singleton registered
|
|
10
|
+
* on startup). Implementations know nothing about users or permissions: the
|
|
11
|
+
* access layer is applied by adminizer on top of every result.
|
|
12
|
+
*
|
|
13
|
+
* Only `list` and `get` are required. The base class implements search,
|
|
14
|
+
* context matching, the keyword map and link resolution on top of them; an
|
|
15
|
+
* implementation overrides any of those when it can do better (e.g. database
|
|
16
|
+
* full-text search).
|
|
17
|
+
*/
|
|
18
|
+
export class AbstractDocumentation {
|
|
19
|
+
/** Set when the implementation is registered; not part of the contract. */
|
|
20
|
+
adminizer;
|
|
21
|
+
/**
|
|
22
|
+
* Keyword filter plus full-text scan over `list()`/`get()`. Content is
|
|
23
|
+
* only fetched for documents that survive the metadata filters.
|
|
24
|
+
*/
|
|
25
|
+
async search(query, locale) {
|
|
26
|
+
const wanted = (query.query ?? '').trim().toLowerCase();
|
|
27
|
+
const keywords = (query.keywords ?? []).map((keyword) => keyword.trim().toLowerCase()).filter(Boolean);
|
|
28
|
+
let candidates = await this.forContext({ url: query.url, model: query.model }, locale);
|
|
29
|
+
if (keywords.length > 0) {
|
|
30
|
+
candidates = candidates.filter((meta) => {
|
|
31
|
+
const present = meta.keywords.map((keyword) => keyword.toLowerCase());
|
|
32
|
+
return keywords.every((keyword) => present.includes(keyword));
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
if (!wanted)
|
|
36
|
+
return candidates.map((meta) => ({ meta, snippets: [] }));
|
|
37
|
+
const results = [];
|
|
38
|
+
for (const meta of candidates) {
|
|
39
|
+
const inMeta = meta.title.toLowerCase().includes(wanted)
|
|
40
|
+
|| meta.keywords.some((keyword) => keyword.toLowerCase().includes(wanted));
|
|
41
|
+
const content = await this.get(meta.id, locale);
|
|
42
|
+
const snippets = content ? this.extractSnippets(content.markdown, wanted) : [];
|
|
43
|
+
if (inMeta || snippets.length > 0)
|
|
44
|
+
results.push({ meta, snippets });
|
|
45
|
+
}
|
|
46
|
+
return results;
|
|
47
|
+
}
|
|
48
|
+
/** Documents bound to a place in the admin panel: a URL and/or a model. */
|
|
49
|
+
async forContext(ctx, locale) {
|
|
50
|
+
const documents = await this.list(locale);
|
|
51
|
+
const url = ctx.url?.trim();
|
|
52
|
+
const model = ctx.model?.trim();
|
|
53
|
+
if (!url && !model)
|
|
54
|
+
return documents;
|
|
55
|
+
return documents.filter((meta) => (!url || meta.urls.some((pattern) => this.matchesUrl(pattern, url)))
|
|
56
|
+
&& (!model || meta.models.includes(model)));
|
|
57
|
+
}
|
|
58
|
+
/** Keyword → document ids, aggregated from metadata. */
|
|
59
|
+
async keywords(locale) {
|
|
60
|
+
const map = new Map();
|
|
61
|
+
for (const meta of await this.list(locale)) {
|
|
62
|
+
for (const raw of meta.keywords) {
|
|
63
|
+
const keyword = raw.trim();
|
|
64
|
+
if (!keyword)
|
|
65
|
+
continue;
|
|
66
|
+
const ids = map.get(keyword) ?? [];
|
|
67
|
+
if (!ids.includes(meta.id))
|
|
68
|
+
ids.push(meta.id);
|
|
69
|
+
map.set(keyword, ids);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return map;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Resolves a reference written inside `fromId` to the id of the target
|
|
76
|
+
* document. How authors spell references is implementation-specific; the
|
|
77
|
+
* default only understands plain document ids (with an optional anchor).
|
|
78
|
+
*/
|
|
79
|
+
async resolveLink(_fromId, ref) {
|
|
80
|
+
const id = ref.trim().replace(/#.*$/, '');
|
|
81
|
+
if (!id)
|
|
82
|
+
return undefined;
|
|
83
|
+
const documents = await this.list();
|
|
84
|
+
return documents.find((meta) => meta.id === id)?.id;
|
|
85
|
+
}
|
|
86
|
+
/** Matches an Express-style pattern (`/model/:name`) against a concrete path. */
|
|
87
|
+
matchesUrl(pattern, url) {
|
|
88
|
+
const path = url.replace(/[?#].*$/, '').replace(/\/+$/, '') || '/';
|
|
89
|
+
const source = pattern.replace(/\/+$/, '') || '/';
|
|
90
|
+
const regex = new RegExp(`^${source.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/:[A-Za-z0-9_]+/g, '[^/]+')}$`);
|
|
91
|
+
return regex.test(path);
|
|
92
|
+
}
|
|
93
|
+
/** Lines containing the query, trimmed to a little context around the match. */
|
|
94
|
+
extractSnippets(markdown, query) {
|
|
95
|
+
const snippets = [];
|
|
96
|
+
for (const line of markdown.split('\n')) {
|
|
97
|
+
const index = line.toLowerCase().indexOf(query);
|
|
98
|
+
if (index === -1)
|
|
99
|
+
continue;
|
|
100
|
+
const start = Math.max(0, index - SNIPPET_CONTEXT / 2);
|
|
101
|
+
const snippet = line.slice(start, start + query.length + SNIPPET_CONTEXT).trim();
|
|
102
|
+
snippets.push(`${start > 0 ? '…' : ''}${snippet}${start + query.length + SNIPPET_CONTEXT < line.length ? '…' : ''}`);
|
|
103
|
+
if (snippets.length >= SNIPPET_LIMIT)
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
return snippets;
|
|
107
|
+
}
|
|
108
|
+
}
|