@antelopejs/dms 0.8.3 → 0.9.1

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 (424) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/data/module-store/catalog.json +71 -0
  3. package/dist/config.d.ts +7 -0
  4. package/dist/config.js +8 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/implementations/dms/frontend-backend-proxies.d.ts +3 -0
  7. package/dist/implementations/dms/frontend-backend-proxies.js +39 -0
  8. package/dist/implementations/dms/frontend-backend-proxies.js.map +1 -0
  9. package/dist/implementations/dms/frontend-bootstrap.d.ts +1 -0
  10. package/dist/implementations/dms/frontend-bootstrap.js +13 -0
  11. package/dist/implementations/dms/frontend-bootstrap.js.map +1 -1
  12. package/dist/implementations/dms/frontend-modules.d.ts +6 -2
  13. package/dist/implementations/dms/frontend-modules.js +37 -8
  14. package/dist/implementations/dms/frontend-modules.js.map +1 -1
  15. package/dist/implementations/dms/frontend-ready.d.ts +8 -0
  16. package/dist/implementations/dms/frontend-ready.js +34 -0
  17. package/dist/implementations/dms/frontend-ready.js.map +1 -0
  18. package/dist/implementations/dms/manifest.d.ts +7 -0
  19. package/dist/implementations/dms/manifest.js +13 -0
  20. package/dist/implementations/dms/manifest.js.map +1 -1
  21. package/dist/implementations/dms/menu-categories.d.ts +16 -0
  22. package/dist/implementations/dms/menu-categories.js +30 -0
  23. package/dist/implementations/dms/menu-categories.js.map +1 -0
  24. package/dist/implementations/dms/nav-badges.d.ts +0 -8
  25. package/dist/implementations/dms/nav-badges.js +0 -16
  26. package/dist/implementations/dms/nav-badges.js.map +1 -1
  27. package/dist/implementations/dms/page.d.ts +13 -5
  28. package/dist/implementations/dms/page.js +72 -41
  29. package/dist/implementations/dms/page.js.map +1 -1
  30. package/dist/implementations/dms-html-render/index.js +5 -2
  31. package/dist/implementations/dms-html-render/index.js.map +1 -1
  32. package/dist/implementations/dms-notifications/index.d.ts +4 -3
  33. package/dist/implementations/dms-notifications/index.js +14 -6
  34. package/dist/implementations/dms-notifications/index.js.map +1 -1
  35. package/dist/pages/settings/settings-index.js +3 -2
  36. package/dist/pages/settings/settings-index.js.map +1 -1
  37. package/dist/pages/settings/users/invites.js +2 -2
  38. package/dist/pages/settings/users/invites.js.map +1 -1
  39. package/dist/pages/settings/users/member-change-notifications.js +17 -6
  40. package/dist/pages/settings/users/member-change-notifications.js.map +1 -1
  41. package/dist/pages/settings/users/member-invite-form.d.ts +3 -2
  42. package/dist/pages/settings/users/member-invite-form.js +7 -4
  43. package/dist/pages/settings/users/member-invite-form.js.map +1 -1
  44. package/dist/pages/settings/users/member-roles-field.js +2 -1
  45. package/dist/pages/settings/users/member-roles-field.js.map +1 -1
  46. package/dist/pages/settings/users/members.js +4 -1
  47. package/dist/pages/settings/users/members.js.map +1 -1
  48. package/dist/pages/settings/users/notifications.d.ts +6 -0
  49. package/dist/pages/settings/users/notifications.js +23 -15
  50. package/dist/pages/settings/users/notifications.js.map +1 -1
  51. package/dist/pages/settings/users/profile.js +1 -1
  52. package/dist/pages/settings/users/profile.js.map +1 -1
  53. package/dist/pages/settings/users/region-form.d.ts +1 -1
  54. package/dist/pages/settings/users/region-form.js +2 -1
  55. package/dist/pages/settings/users/region-form.js.map +1 -1
  56. package/dist/pages/settings/users/role-editor-store.d.ts +3 -1
  57. package/dist/pages/settings/users/role-editor-store.js +17 -4
  58. package/dist/pages/settings/users/role-editor-store.js.map +1 -1
  59. package/dist/pages/settings/users/roles.js +1 -0
  60. package/dist/pages/settings/users/roles.js.map +1 -1
  61. package/dist/realtime/registry.d.ts +1 -0
  62. package/dist/realtime/registry.js +5 -0
  63. package/dist/realtime/registry.js.map +1 -1
  64. package/dist/routes/index.d.ts +1 -0
  65. package/dist/routes/index.js +1 -0
  66. package/dist/routes/index.js.map +1 -1
  67. package/dist/routes/realtime.d.ts +5 -1
  68. package/dist/routes/realtime.js +16 -9
  69. package/dist/routes/realtime.js.map +1 -1
  70. package/dist/routes/store.d.ts +21 -0
  71. package/dist/routes/store.js +130 -0
  72. package/dist/routes/store.js.map +1 -0
  73. package/dist/store/activity.d.ts +2 -0
  74. package/dist/store/activity.js +12 -0
  75. package/dist/store/activity.js.map +1 -0
  76. package/dist/store/cache.d.ts +8 -0
  77. package/dist/store/cache.js +29 -0
  78. package/dist/store/cache.js.map +1 -0
  79. package/dist/store/catalog.d.ts +4 -0
  80. package/dist/store/catalog.js +69 -0
  81. package/dist/store/catalog.js.map +1 -0
  82. package/dist/store/compatibility.d.ts +3 -0
  83. package/dist/store/compatibility.js +45 -0
  84. package/dist/store/compatibility.js.map +1 -0
  85. package/dist/store/declarations.d.ts +18 -0
  86. package/dist/store/declarations.js +116 -0
  87. package/dist/store/declarations.js.map +1 -0
  88. package/dist/store/frontend-compatibility.d.ts +18 -0
  89. package/dist/store/frontend-compatibility.js +93 -0
  90. package/dist/store/frontend-compatibility.js.map +1 -0
  91. package/dist/store/interfaces.d.ts +6 -0
  92. package/dist/store/interfaces.js +126 -0
  93. package/dist/store/interfaces.js.map +1 -0
  94. package/dist/store/listing.d.ts +2 -0
  95. package/dist/store/listing.js +180 -0
  96. package/dist/store/listing.js.map +1 -0
  97. package/dist/store/operations.d.ts +8 -0
  98. package/dist/store/operations.js +254 -0
  99. package/dist/store/operations.js.map +1 -0
  100. package/dist/store/registry.d.ts +4 -0
  101. package/dist/store/registry.js +61 -0
  102. package/dist/store/registry.js.map +1 -0
  103. package/dist/store/requirements.d.ts +14 -0
  104. package/dist/store/requirements.js +81 -0
  105. package/dist/store/requirements.js.map +1 -0
  106. package/dist/store/runtime.d.ts +11 -0
  107. package/dist/store/runtime.js +78 -0
  108. package/dist/store/runtime.js.map +1 -0
  109. package/dist/store/tarball.d.ts +2 -0
  110. package/dist/store/tarball.js +80 -0
  111. package/dist/store/tarball.js.map +1 -0
  112. package/dist/store/types.d.ts +98 -0
  113. package/dist/store/types.js +3 -0
  114. package/dist/store/types.js.map +1 -0
  115. package/dist/test/integration/account-deletion.test.js +2 -2
  116. package/dist/test/integration/account-deletion.test.js.map +1 -1
  117. package/dist/test/integration/auth/auth-key-rotation.test.js +2 -1
  118. package/dist/test/integration/auth/auth-key-rotation.test.js.map +1 -1
  119. package/dist/test/integration/auth/current-password.test.js +2 -1
  120. package/dist/test/integration/auth/current-password.test.js.map +1 -1
  121. package/dist/test/integration/auth/session-revocation.test.js +2 -1
  122. package/dist/test/integration/auth/session-revocation.test.js.map +1 -1
  123. package/dist/test/integration/auth/sign-in-country.test.js +2 -1
  124. package/dist/test/integration/auth/sign-in-country.test.js.map +1 -1
  125. package/dist/test/integration/auth/two-factor-codes.test.js +2 -1
  126. package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
  127. package/dist/test/integration/auth/two-factor-password.test.js +3 -2
  128. package/dist/test/integration/auth/two-factor-password.test.js.map +1 -1
  129. package/dist/test/integration/email-change.test.js +2 -1
  130. package/dist/test/integration/email-change.test.js.map +1 -1
  131. package/dist/test/integration/files/component-files.test.js +2 -2
  132. package/dist/test/integration/files/component-files.test.js.map +1 -1
  133. package/dist/test/integration/member-settings-access.test.js +8 -7
  134. package/dist/test/integration/member-settings-access.test.js.map +1 -1
  135. package/dist/test/integration/menu-access-events.test.d.ts +1 -0
  136. package/dist/test/integration/menu-access-events.test.js +148 -0
  137. package/dist/test/integration/menu-access-events.test.js.map +1 -0
  138. package/dist/test/integration/notification-inbox.test.js +4 -1
  139. package/dist/test/integration/notification-inbox.test.js.map +1 -1
  140. package/dist/test/integration/notification-locked-subjects.test.js +3 -2
  141. package/dist/test/integration/notification-locked-subjects.test.js.map +1 -1
  142. package/dist/test/integration/notification-unread-badge.test.js +51 -5
  143. package/dist/test/integration/notification-unread-badge.test.js.map +1 -1
  144. package/dist/test/integration/page-layout-route-guard.test.d.ts +1 -0
  145. package/dist/test/integration/page-layout-route-guard.test.js +56 -0
  146. package/dist/test/integration/page-layout-route-guard.test.js.map +1 -0
  147. package/dist/test/integration/permission-warnings.test.js +3 -2
  148. package/dist/test/integration/permission-warnings.test.js.map +1 -1
  149. package/dist/test/integration/removed-member-notifications.test.js +3 -2
  150. package/dist/test/integration/removed-member-notifications.test.js.map +1 -1
  151. package/dist/test/integration/role-delete-reassign.test.js +2 -1
  152. package/dist/test/integration/role-delete-reassign.test.js.map +1 -1
  153. package/dist/test/integration/role-permission-ancestors.test.js +2 -1
  154. package/dist/test/integration/role-permission-ancestors.test.js.map +1 -1
  155. package/dist/test/unit/frontend-bootstrap.test.js +13 -0
  156. package/dist/test/unit/frontend-bootstrap.test.js.map +1 -1
  157. package/dist/test/unit/implementations/dms/category-slug-registry.test.js +14 -5
  158. package/dist/test/unit/implementations/dms/category-slug-registry.test.js.map +1 -1
  159. package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.d.ts +1 -0
  160. package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js +66 -0
  161. package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js.map +1 -0
  162. package/dist/test/unit/implementations/dms/manifest-strip.test.js +26 -0
  163. package/dist/test/unit/implementations/dms/manifest-strip.test.js.map +1 -1
  164. package/dist/test/unit/implementations/dms/nav-badges.test.js +23 -9
  165. package/dist/test/unit/implementations/dms/nav-badges.test.js.map +1 -1
  166. package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +22 -4
  167. package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
  168. package/dist/test/unit/interfaces/dms/page-layout-query.test.js +12 -0
  169. package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -1
  170. package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -1
  171. package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
  172. package/dist/test/unit/pages/settings/member-roles-field.test.js +1 -1
  173. package/dist/test/unit/pages/settings/member-roles-field.test.js.map +1 -1
  174. package/dist/test/unit/pages/settings/role-delete-confirm.test.js.map +1 -1
  175. package/dist/test/unit/realtime/menu-topics.test.d.ts +1 -0
  176. package/dist/test/unit/realtime/menu-topics.test.js +26 -0
  177. package/dist/test/unit/realtime/menu-topics.test.js.map +1 -0
  178. package/dist/test/unit/store/store.test.d.ts +1 -0
  179. package/dist/test/unit/store/store.test.js +127 -0
  180. package/dist/test/unit/store/store.test.js.map +1 -0
  181. package/dist/validation/store.schema.d.ts +39 -0
  182. package/dist/validation/store.schema.js +27 -0
  183. package/dist/validation/store.schema.js.map +1 -0
  184. package/frontend-vue/deferred-plugins.ts +3 -4
  185. package/frontend-vue/dms.email.ts +5 -1
  186. package/frontend-vue/dms.frontend.ts +15 -5
  187. package/frontend-vue/dms.server.ts +74 -0
  188. package/frontend-vue/layers/dms-auth/app/composables/useLogout.ts +1 -1
  189. package/frontend-vue/layers/dms-auth/app/composables/usePostLoginRedirect.ts +16 -3
  190. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/2fa.vue +3 -1
  191. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/backup.vue +3 -1
  192. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/forgot-validation.vue +1 -1
  193. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/oauth-complete.vue +26 -3
  194. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/recover.vue +1 -1
  195. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +22 -25
  196. package/frontend-vue/layers/dms-auth/app/custom-pages/auth/validate.vue +1 -1
  197. package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -7
  198. package/frontend-vue/layers/dms-auth/app/plugins/permissions-preload.ts +17 -0
  199. package/frontend-vue/layers/dms-auth/server/accounts.ts +135 -0
  200. package/frontend-vue/layers/dms-auth/server/actions.ts +251 -0
  201. package/frontend-vue/layers/dms-auth/server/oauth.ts +260 -0
  202. package/frontend-vue/layers/dms-auth/server/session.ts +158 -0
  203. package/frontend-vue/layers/dms-core/app/build/utils/asyncDataScope.ts +47 -0
  204. package/frontend-vue/layers/dms-core/app/build/utils/permission-preview.ts +6 -2
  205. package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +148 -0
  206. package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +7 -20
  207. package/frontend-vue/layers/dms-core/app/composables/auth/useCurrentUser.ts +2 -2
  208. package/frontend-vue/layers/dms-core/app/composables/auth/useMultiAccount.ts +18 -5
  209. package/frontend-vue/layers/dms-core/app/composables/auth/usePermissions.ts +35 -6
  210. package/frontend-vue/layers/dms-core/app/composables/auth/useSessionRecovery.ts +1 -1
  211. package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +20 -14
  212. package/frontend-vue/layers/dms-core/app/composables/period/periodClock.ts +121 -0
  213. package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +42 -0
  214. package/frontend-vue/layers/dms-core/app/composables/period/periodTimeZone.ts +31 -0
  215. package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +48 -19
  216. package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +33 -4
  217. package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +78 -25
  218. package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +3 -0
  219. package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +36 -6
  220. package/frontend-vue/layers/dms-core/app/composables/user/useUserRegionalPreferences.ts +1 -1
  221. package/frontend-vue/layers/dms-core/app/plugins/async-data-scope.ts +26 -0
  222. package/frontend-vue/layers/dms-core/app/utils/pageRoutes.ts +22 -0
  223. package/frontend-vue/layers/dms-core/app/utils/regional.ts +36 -3
  224. package/frontend-vue/layers/dms-core/app/utils/trailingDeduper.ts +10 -1
  225. package/frontend-vue/layers/dms-core/shared/types/app-config.ts +48 -4
  226. package/frontend-vue/layers/dms-layout/app/app.config.ts +42 -23
  227. package/frontend-vue/layers/dms-layout/app/assets/css/main.css +72 -20
  228. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardFrame.vue +9 -0
  229. package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +0 -3
  230. package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +9 -3
  231. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreDependencyPicker.vue +136 -0
  232. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +98 -0
  233. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +247 -29
  234. package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +19 -2
  235. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +13 -12
  236. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +17 -7
  237. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationPreferencesForm.vue +0 -1
  238. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +36 -33
  239. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSecuritySummary.vue +1 -2
  240. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts +5 -4
  241. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +35 -28
  242. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityStatus.vue +0 -1
  243. package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +1 -1
  244. package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts +7 -6
  245. package/frontend-vue/layers/dms-layout/app/build/composables/layout/sidePanelState.ts +1 -1
  246. package/frontend-vue/layers/dms-layout/app/build/composables/navigation/useVisitNavBadges.ts +26 -0
  247. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts +25 -23
  248. package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationPreferences.ts +44 -23
  249. package/frontend-vue/layers/dms-layout/app/build/composables/security/useSessionHandoff.ts +1 -1
  250. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts +1 -1
  251. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +3 -2
  252. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts +1 -18
  253. package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfilePreferences.ts +19 -23
  254. package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +6 -3
  255. package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityOverview.ts +13 -18
  256. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +186 -120
  257. package/frontend-vue/layers/dms-layout/app/build/utils/module-store-install.ts +146 -0
  258. package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +8 -6
  259. package/frontend-vue/layers/dms-layout/app/build/utils/publicErrorMessage.ts +19 -0
  260. package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -11
  261. package/frontend-vue/layers/dms-layout/app/components/navigation/NavigationMenu.vue +3 -13
  262. package/frontend-vue/layers/dms-layout/app/composables/general/useSystemState.ts +7 -11
  263. package/frontend-vue/layers/dms-layout/app/composables/notification/useNotifications.ts +103 -22
  264. package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +109 -0
  265. package/frontend-vue/layers/dms-layout/app/composables/page/useModulesListing.ts +25 -102
  266. package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +0 -10
  267. package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +177 -96
  268. package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +3 -2
  269. package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +341 -62
  270. package/frontend-vue/layers/dms-layout/app/emails/components/EmailLayout.vue +31 -11
  271. package/frontend-vue/layers/dms-layout/app/error.vue +5 -2
  272. package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +1 -1
  273. package/frontend-vue/layers/dms-layout/app/middleware/page-leave-guard.global.ts +2 -2
  274. package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +12 -9
  275. package/frontend-vue/layers/dms-layout/app/plugins/dms-menu-sync.client.ts +31 -3
  276. package/frontend-vue/layers/dms-layout/app/plugins/favicon.ts +58 -0
  277. package/frontend-vue/layers/dms-layout/app/plugins/notification-stream.client.ts +15 -7
  278. package/frontend-vue/layers/dms-layout/app/plugins/profile-sync.client.ts +5 -1
  279. package/frontend-vue/layers/dms-layout/app/plugins/regional-preferences.ts +10 -4
  280. package/frontend-vue/layers/dms-layout/app/plugins/time-zone-cookie.client.ts +12 -0
  281. package/frontend-vue/layers/dms-layout/app/types/module-store.ts +134 -0
  282. package/frontend-vue/layers/dms-layout/app/types/page.ts +13 -0
  283. package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +107 -9
  284. package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +107 -9
  285. package/frontend-vue/layers/dms-layout/server/html-render.ts +58 -0
  286. package/frontend-vue/layers/dms-layout/server/render-token.ts +95 -0
  287. package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/useOnboarding.ts +25 -21
  288. package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +1 -1
  289. package/frontend-vue/layers/dms-onboarding/app/plugins/onboarding-preload.ts +9 -0
  290. package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +4 -1
  291. package/frontend-vue/layers/dms-ui/app/build/composables/files/useFileMetadata.ts +162 -0
  292. package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +23 -4
  293. package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageDataRefresh.ts +101 -0
  294. package/frontend-vue/layers/dms-ui/app/build/composables/navigation/useNavBadges.ts +33 -6
  295. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +19 -16
  296. package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +40 -20
  297. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +6 -8
  298. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +42 -14
  299. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +19 -8
  300. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +1 -1
  301. package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +5 -2
  302. package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +9 -9
  303. package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +20 -18
  304. package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -1
  305. package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +26 -132
  306. package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +40 -105
  307. package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -0
  308. package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -4
  309. package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +9 -13
  310. package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +2 -1
  311. package/frontend-vue/layers/dms-ui/app/components/table-view/FilePreview.vue +12 -24
  312. package/frontend-vue/layers/dms-ui/app/components/table-view/ImagePreview.vue +17 -29
  313. package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +7 -3
  314. package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +20 -8
  315. package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +8 -11
  316. package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +146 -182
  317. package/frontend-vue/layers/dms-ui/app/composables/live/usePageLiveRefresh.ts +8 -2
  318. package/frontend-vue/layers/dms-ui/app/composables/useFileReadUrls.ts +11 -99
  319. package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +32 -3
  320. package/frontend-vue/package.json +3 -3
  321. package/frontend-vue/pnpm-lock.yaml +16 -22
  322. package/frontend-vue/server/private-options.ts +27 -0
  323. package/frontend-vue/tests/async-data-scope.test.ts +121 -0
  324. package/frontend-vue/tests/auth-middleware-error.test.ts +3 -3
  325. package/frontend-vue/tests/banner-block-fetch.test.ts +3 -0
  326. package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +3 -0
  327. package/frontend-vue/tests/block-items-period-realtime.test.ts +3 -0
  328. package/frontend-vue/tests/block-not-found.test.ts +16 -2
  329. package/frontend-vue/tests/block-page-refresh.test.ts +3 -0
  330. package/frontend-vue/tests/block-route-params.test.ts +3 -0
  331. package/frontend-vue/tests/branding-defaults.test.ts +34 -0
  332. package/frontend-vue/tests/builtin-emails.test.ts +23 -2
  333. package/frontend-vue/tests/chart-live-pause.test.ts +4 -1
  334. package/frontend-vue/tests/chart-value-propagation.test.ts +4 -1
  335. package/frontend-vue/tests/composed-text-blocks.test.ts +3 -0
  336. package/frontend-vue/tests/email-layout.test.ts +66 -4
  337. package/frontend-vue/tests/error-page-message.test.ts +25 -2
  338. package/frontend-vue/tests/favicon.test.ts +131 -0
  339. package/frontend-vue/tests/file-fields-ssr.test.ts +145 -0
  340. package/frontend-vue/tests/file-read-urls.test.ts +145 -34
  341. package/frontend-vue/tests/file-unavailable.test.ts +5 -1
  342. package/frontend-vue/tests/fixtures/dataEngine.ts +44 -0
  343. package/frontend-vue/tests/form-load-error.test.ts +55 -1
  344. package/frontend-vue/tests/helpers/api-data-runtime.ts +77 -0
  345. package/frontend-vue/tests/image-hydration-ids.test.ts +21 -16
  346. package/frontend-vue/tests/image-reference-preservation.test.ts +59 -53
  347. package/frontend-vue/tests/input-number-adjust.test.ts +14 -0
  348. package/frontend-vue/tests/key-value-list-copy.test.ts +3 -0
  349. package/frontend-vue/tests/member-role-picker.test.ts +4 -2
  350. package/frontend-vue/tests/menu-sync-reconnect.test.ts +47 -0
  351. package/frontend-vue/tests/metadata-retry.test.ts +5 -1
  352. package/frontend-vue/tests/module-routing.test.ts +2 -5
  353. package/frontend-vue/tests/module-store-catalog.test.ts +270 -51
  354. package/frontend-vue/tests/modules-catalog.test.ts +17 -4
  355. package/frontend-vue/tests/modules-listing-transfer.test.ts +95 -0
  356. package/frontend-vue/tests/multi-account-validate.test.ts +23 -1
  357. package/frontend-vue/tests/nav-badge-tones.test.ts +66 -3
  358. package/frontend-vue/tests/notification-counts-from-writes.test.ts +223 -0
  359. package/frontend-vue/tests/notification-counts-transfer.test.ts +88 -0
  360. package/frontend-vue/tests/notification-inbox-display.test.ts +57 -5
  361. package/frontend-vue/tests/notification-popover-errors.test.ts +21 -1
  362. package/frontend-vue/tests/oauth-complete-handoff.test.ts +209 -0
  363. package/frontend-vue/tests/onboarding-middleware.test.ts +1 -1
  364. package/frontend-vue/tests/onboarding-transfer.test.ts +106 -0
  365. package/frontend-vue/tests/page-data-refresh.test.ts +141 -0
  366. package/frontend-vue/tests/page-fill-height.test.ts +16 -3
  367. package/frontend-vue/tests/page-no-visible-blocks.test.ts +12 -2
  368. package/frontend-vue/tests/page-realtime-remount.test.ts +1 -1
  369. package/frontend-vue/tests/page-record-label.test.ts +0 -7
  370. package/frontend-vue/tests/page-validation-redirect.test.ts +12 -2
  371. package/frontend-vue/tests/period-hydration-now.test.ts +185 -0
  372. package/frontend-vue/tests/period-live-pause.test.ts +3 -0
  373. package/frontend-vue/tests/period-reader-zone.test.ts +137 -0
  374. package/frontend-vue/tests/period-scope-first-fetch.test.ts +17 -1
  375. package/frontend-vue/tests/period-ssr.test.ts +195 -0
  376. package/frontend-vue/tests/permission-preview-partial.test.ts +0 -2
  377. package/frontend-vue/tests/permissions-transfer.test.ts +194 -0
  378. package/frontend-vue/tests/post-login-redirect.test.ts +10 -5
  379. package/frontend-vue/tests/profile-personal-info-ssr.test.ts +150 -0
  380. package/frontend-vue/tests/profile-preferences-summary.test.ts +15 -11
  381. package/frontend-vue/tests/realtime-reconnect.test.ts +22 -3
  382. package/frontend-vue/tests/roles-name-errors.test.ts +1 -1
  383. package/frontend-vue/tests/row-side-panel.test.ts +1 -1
  384. package/frontend-vue/tests/security-sessions-load-error.test.ts +3 -1
  385. package/frontend-vue/tests/server-routes-vs-pages.test.ts +26 -0
  386. package/frontend-vue/tests/server-routes.test.ts +582 -0
  387. package/frontend-vue/tests/session-handoff.test.ts +1 -1
  388. package/frontend-vue/tests/settings-reads-ssr.test.ts +232 -0
  389. package/frontend-vue/tests/side-panels.test.ts +8 -1
  390. package/frontend-vue/tests/sign-in-exit-history.test.ts +219 -0
  391. package/frontend-vue/tests/signup-invitation-link.test.ts +56 -8
  392. package/frontend-vue/tests/site-layout-categories.test.ts +42 -0
  393. package/frontend-vue/tests/site-layout-hydration.test.ts +3 -3
  394. package/frontend-vue/tests/site-layout-rendered.test.ts +103 -0
  395. package/frontend-vue/tests/site-layout-resync.test.ts +87 -0
  396. package/frontend-vue/tests/ssr-guard-preload.test.ts +188 -0
  397. package/frontend-vue/tests/table-cell-tones.test.ts +36 -1
  398. package/frontend-vue/tests/table-data-ssr.test.ts +195 -0
  399. package/frontend-vue/tests/table-list-data-requests.test.ts +154 -0
  400. package/frontend-vue/tests/table-retry-loading.test.ts +37 -0
  401. package/frontend-vue/tests/table-source-navigation.test.ts +0 -1
  402. package/frontend-vue/tests/table-source-period-scope.test.ts +0 -1
  403. package/frontend-vue/tests/table-view-views.test.ts +1 -1
  404. package/frontend-vue/tests/top-list-card-actions.test.ts +3 -0
  405. package/frontend-vue/tests/top-list-card-period-badge-ssr.test.ts +212 -0
  406. package/frontend-vue/tests/top-list-card-period-badge.test.ts +79 -0
  407. package/frontend-vue/tests/top-list-card-value-suffix.test.ts +3 -0
  408. package/frontend-vue/tests/trailing-deduper.test.ts +22 -0
  409. package/frontend-vue/tests/use-api-data-update.test.ts +119 -0
  410. package/frontend-vue/tests/use-api-data.test.ts +410 -0
  411. package/frontend-vue/tests/user-language.test.ts +8 -5
  412. package/frontend-vue/tests/widget-label-i18n.test.ts +4 -1
  413. package/frontend-vue/vitest.config.ts +6 -0
  414. package/package.json +2 -2
  415. package/skills/dms/SKILL.md +2 -1
  416. package/skills/dms-module/SKILL.md +1 -1
  417. package/skills/dms-pages/SKILL.md +4 -1
  418. package/frontend-vue/layers/dms-core/app/build/composables/page/usePageRecordLabel.ts +0 -9
  419. package/frontend-vue/layers/dms-layout/app/composables/page/usePrefetch.ts +0 -21
  420. package/frontend-vue/layers/dms-ui/app/build/composables/form/useControlError.ts +0 -10
  421. package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts +0 -23
  422. package/frontend-vue/tests/control-error-path.test.ts +0 -11
  423. package/frontend-vue/tests/modules-listing-per-request.test.ts +0 -104
  424. package/frontend-vue/tests/page-layout-background-refresh.test.ts +0 -321
@@ -1,137 +1,203 @@
1
+ import type { Tone } from "#dms-ui/app/types/tone";
2
+ import type { StatusPillDot } from "#dms-ui/app/components/status-pill/StatusPill.vue";
3
+ import type {
4
+ StoreListingEntry,
5
+ StoreModuleState,
6
+ StoreOperationKind,
7
+ } from "../../types/module-store";
1
8
  import type { DisplayResolver } from "./modules-catalog";
2
9
 
3
- /**
4
- * One official module offered by the module store preview. The list below is
5
- * static for now: it stands in for the AntelopeJS module registry the store
6
- * will read once installing from the dashboard is possible.
7
- */
8
- export interface ModuleStoreEntry {
10
+ /** A store module as the Modules page filters and draws it. */
11
+ export type ModuleStoreEntry = StoreListingEntry & {
12
+ /** Store category, a `$i18n.key` (what the category chips read). */
13
+ catalogCategory: string;
14
+ };
15
+
16
+ /** Which store modules the store section shows. */
17
+ export type StoreScope = "all" | "available" | "installed";
18
+
19
+ /** What a store tile's pill says. */
20
+ export interface StoreTileStatus {
21
+ tone: Tone;
22
+ /** i18n key under `modules.store.state`. */
23
+ labelKey: string;
24
+ dot?: StatusPillDot;
25
+ }
26
+
27
+ /** One action a store tile offers. */
28
+ export interface StoreAction {
29
+ kind: StoreOperationKind;
30
+ /** i18n key under `modules.store.actions`. */
31
+ labelKey: string;
32
+ icon: string;
33
+ /** The action runs only once the owner confirms it. */
34
+ confirm?: boolean;
9
35
  /**
10
- * Id the module registers in the catalog (`RegisterModule({ id })`): a
11
- * store entry whose id is installed is hidden.
36
+ * Offered in the tile's menu only, never as its button: pausing or
37
+ * removing a running module is not what its tile should invite.
12
38
  */
13
- id: string;
14
- /** npm package the AntelopeJS CLI adds to the project. */
15
- packageName: string;
16
- /** Display name, a `$i18n.key`. */
17
- title: string;
18
- /** One-line summary, a `$i18n.key`. */
19
- description: string;
20
- /** Iconify name, the icon the module registers once installed. */
21
- icon: string;
22
- /** Store category, a `$i18n.key`. */
23
- catalogCategory: string;
39
+ isMenuOnly?: boolean;
24
40
  }
25
41
 
26
- const CATEGORY = {
27
- ai: "$modules.store.category.ai",
28
- business: "$modules.store.category.business",
29
- content: "$modules.store.category.content",
30
- data: "$modules.store.category.data",
31
- developer: "$modules.store.category.developer",
32
- growth: "$modules.store.category.growth",
33
- operations: "$modules.store.category.operations",
34
- } as const;
35
-
36
- function storeEntry(
37
- id: string,
38
- packageName: string,
39
- icon: string,
40
- catalogCategory: string,
41
- ): ModuleStoreEntry {
42
+ const CATEGORY_PREFIX = "$modules.store.category";
43
+
44
+ /** The command that turns a production frontend into one serving new layers. */
45
+ export const FRONTEND_BUILD_COMMAND = "ajs dms build";
46
+
47
+ const STATUSES: Record<StoreModuleState, StoreTileStatus> = {
48
+ available: { tone: "neutral", labelKey: "available", dot: "none" },
49
+ busy: { tone: "info", labelKey: "busy", dot: "live" },
50
+ active: { tone: "success", labelKey: "active" },
51
+ disabled: { tone: "neutral", labelKey: "disabled" },
52
+ error: { tone: "error", labelKey: "error" },
53
+ pending: { tone: "warning", labelKey: "pending" },
54
+ };
55
+
56
+ const ACTIONS: Record<StoreOperationKind, StoreAction> = {
57
+ install: {
58
+ kind: "install",
59
+ labelKey: "install",
60
+ icon: "i-ph-download-simple",
61
+ },
62
+ update: { kind: "update", labelKey: "update", icon: "i-ph-arrow-circle-up" },
63
+ enable: { kind: "enable", labelKey: "enable", icon: "i-ph-play" },
64
+ disable: {
65
+ kind: "disable",
66
+ labelKey: "disable",
67
+ icon: "i-ph-pause",
68
+ isMenuOnly: true,
69
+ },
70
+ uninstall: {
71
+ kind: "uninstall",
72
+ labelKey: "uninstall",
73
+ icon: "i-ph-trash",
74
+ confirm: true,
75
+ isMenuOnly: true,
76
+ },
77
+ apply: { kind: "apply", labelKey: "apply", icon: "i-ph-arrows-clockwise" },
78
+ };
79
+
80
+ /** States of a module that antelope.config.ts declares. */
81
+ const INSTALLED_STATES = new Set<StoreModuleState>([
82
+ "active",
83
+ "disabled",
84
+ "pending",
85
+ ]);
86
+
87
+ export function toModuleStoreEntry(entry: StoreListingEntry): ModuleStoreEntry {
42
88
  return {
43
- id,
44
- packageName,
45
- title: `$modules.store.catalog.${id}.title`,
46
- description: `$modules.store.catalog.${id}.description`,
47
- icon,
48
- catalogCategory,
89
+ ...entry,
90
+ catalogCategory: `${CATEGORY_PREFIX}.${entry.category}`,
91
+ };
92
+ }
93
+
94
+ /** Whether the module is installed, from the store or by the config. */
95
+ export function isInstalled(entry: StoreListingEntry): boolean {
96
+ if (INSTALLED_STATES.has(entry.state)) return true;
97
+ return entry.installedVersion !== undefined;
98
+ }
99
+
100
+ export function filterByScope<T extends StoreListingEntry>(
101
+ entries: readonly T[],
102
+ scope: StoreScope,
103
+ ): T[] {
104
+ if (scope === "all") return [...entries];
105
+ const wantsInstalled = scope === "installed";
106
+ return entries.filter((entry) => isInstalled(entry) === wantsInstalled);
107
+ }
108
+
109
+ /** The store modules, alphabetical by their shown name. */
110
+ export function sortStoreModules<T extends StoreListingEntry>(
111
+ entries: readonly T[],
112
+ resolve: DisplayResolver,
113
+ ): T[] {
114
+ return [...entries].sort((a, b) =>
115
+ resolve(a.title).localeCompare(resolve(b.title)),
116
+ );
117
+ }
118
+
119
+ export function storeTileStatus(entry: StoreListingEntry): StoreTileStatus {
120
+ if (entry.state === "active" && entry.updateAvailable) {
121
+ return { tone: "warning", labelKey: "update_available" };
122
+ }
123
+ return STATUSES[entry.state];
124
+ }
125
+
126
+ /**
127
+ * What the tile offers, the main action first. A module in error offers to
128
+ * try its last step again: installing it when the file does not hold it,
129
+ * applying the file otherwise.
130
+ */
131
+ export function storeActions(entry: StoreListingEntry): StoreAction[] {
132
+ const installed = entry.installedVersion !== undefined;
133
+ const byState: Record<StoreModuleState, StoreOperationKind[]> = {
134
+ available: ["install"],
135
+ busy: [],
136
+ active: entry.updateAvailable
137
+ ? ["update", "disable", "uninstall"]
138
+ : ["disable", "uninstall"],
139
+ disabled: ["enable", "uninstall"],
140
+ error: installed ? ["apply", "uninstall"] : ["install"],
141
+ pending: ["apply", "uninstall"],
49
142
  };
143
+ return byState[entry.state].map((kind) => ACTIONS[kind]);
50
144
  }
51
145
 
52
146
  /**
53
- * The official AntelopeJS DMS modules (one repository each under the
54
- * AntelopeJS organisation). Ids and icons are the ones each module registers;
55
- * Media and Builder register no catalog module, so theirs come from their
56
- * settings page and the v2 design.
147
+ * Whether the page waits on the frontend for this module: its backend side
148
+ * changed and the frontend server has not reported serving it yet.
57
149
  */
58
- export const MODULE_STORE_CATALOG: readonly ModuleStoreEntry[] = [
59
- storeEntry("ai", "@antelopejs/dms-ai", "i-ph-robot", CATEGORY.ai),
60
- storeEntry(
61
- "api",
62
- "@antelopejs/dms-api",
63
- "i-ph-brackets-curly",
64
- CATEGORY.developer,
65
- ),
66
- storeEntry(
67
- "automation",
68
- "@antelopejs/dms-automation",
69
- "i-ph-flow-arrow",
70
- CATEGORY.operations,
71
- ),
72
- storeEntry(
73
- "builder",
74
- "@antelopejs/dms-builder",
75
- "i-ph-hammer",
76
- CATEGORY.developer,
77
- ),
78
- storeEntry(
79
- "cicd",
80
- "@antelopejs-private/dms-cicd",
81
- "i-ph-rocket-launch",
82
- CATEGORY.operations,
83
- ),
84
- storeEntry(
85
- "database",
86
- "@antelopejs/dms-database",
87
- "i-ph-database",
88
- CATEGORY.data,
89
- ),
90
- storeEntry(
91
- "lang",
92
- "@antelopejs/dms-lang",
93
- "i-ph-translate",
94
- CATEGORY.content,
95
- ),
96
- storeEntry(
97
- "mailing",
98
- "@antelopejs/dms-mailing",
99
- "i-ph-envelope-simple",
100
- CATEGORY.growth,
101
- ),
102
- storeEntry(
103
- "marketing",
104
- "@antelopejs/dms-marketing",
105
- "i-ph-chart-line-up",
106
- CATEGORY.growth,
107
- ),
108
- storeEntry("media", "@antelopejs/dms-media", "i-ph-images", CATEGORY.content),
109
- storeEntry(
110
- "saas",
111
- "@antelopejs/dms-saas",
112
- "i-ph-buildings",
113
- CATEGORY.business,
114
- ),
115
- ];
116
-
117
- /** The AntelopeJS CLI command that adds a package module to a project. */
118
- export const MODULE_INSTALL_COMMAND = "ajs project modules add";
119
-
120
- /** The full CLI command that adds the module to a project. */
121
- export function moduleInstallCommand(entry: ModuleStoreEntry): string {
122
- return `${MODULE_INSTALL_COMMAND} ${entry.packageName}`;
150
+ export function isAwaitingFrontend(entry: StoreListingEntry): boolean {
151
+ return entry.state !== "busy" && entry.frontend === "pending";
123
152
  }
124
153
 
154
+ /** Why an action the tile offers cannot run, as the tile explains it. */
155
+ export type StoreActionBlock =
156
+ | "no_release"
157
+ | "incompatible"
158
+ | "frontend"
159
+ | "interfaces"
160
+ | "dependents";
161
+
162
+ /** Actions that load a new release of the module. */
163
+ const RELEASE_ACTIONS = new Set<StoreOperationKind>(["install", "update"]);
125
164
  /**
126
- * The store entries not installed on this platform, alphabetical by their
127
- * shown name.
165
+ * Actions that pick the missing interfaces' modules in a dialog; the others
166
+ * refuse a release that misses one.
128
167
  */
129
- export function availableStoreModules(
130
- catalog: readonly ModuleStoreEntry[],
131
- installedIds: ReadonlySet<string>,
132
- resolve: DisplayResolver,
133
- ): ModuleStoreEntry[] {
134
- return catalog
135
- .filter((entry) => !installedIds.has(entry.id))
136
- .sort((a, b) => resolve(a.title).localeCompare(resolve(b.title)));
168
+ const PICKS_DEPENDENCIES = new Set<StoreOperationKind>(["install"]);
169
+ /** Actions that take a running module down. */
170
+ const TAKE_DOWN_ACTIONS = new Set<StoreOperationKind>(["disable", "uninstall"]);
171
+
172
+ function releaseBlock(
173
+ entry: StoreListingEntry,
174
+ kind: StoreOperationKind,
175
+ ): StoreActionBlock | undefined {
176
+ if (!entry.latestVersion) return "no_release";
177
+ if (!entry.compatible) return "incompatible";
178
+ if (entry.frontendRequirement) return "frontend";
179
+ if (!PICKS_DEPENDENCIES.has(kind) && entry.missingInterfaces.length > 0) {
180
+ return "interfaces";
181
+ }
182
+ return undefined;
183
+ }
184
+
185
+ /** Why `kind` cannot run on the module, or undefined when it can. */
186
+ export function storeActionBlock(
187
+ entry: StoreListingEntry,
188
+ kind: StoreOperationKind,
189
+ ): StoreActionBlock | undefined {
190
+ if (RELEASE_ACTIONS.has(kind)) return releaseBlock(entry, kind);
191
+ if (TAKE_DOWN_ACTIONS.has(kind) && entry.dependents.length > 0) {
192
+ return "dependents";
193
+ }
194
+ return undefined;
195
+ }
196
+
197
+ /** Whether `kind` first asks which modules implement the missing interfaces. */
198
+ export function needsDependencyChoice(
199
+ entry: StoreListingEntry,
200
+ kind: StoreOperationKind,
201
+ ): boolean {
202
+ return PICKS_DEPENDENCIES.has(kind) && entry.missingInterfaces.length > 0;
137
203
  }
@@ -0,0 +1,146 @@
1
+ import type {
2
+ MissingInterface,
3
+ StoreActionBody,
4
+ StoreDependencyChoice,
5
+ } from "../../types/module-store";
6
+
7
+ /**
8
+ * The radio value of the "Other npm package" choice: not an npm package
9
+ * name, so no candidate can take it.
10
+ */
11
+ export const OTHER_PACKAGE_CHOICE = ":other";
12
+
13
+ /**
14
+ * An npm package name, scoped or not: never a path, a URL or a git source,
15
+ * as the store only installs from the registry.
16
+ */
17
+ const NPM_PACKAGE_PATTERN =
18
+ /^(?:@[a-z0-9][a-z0-9._~-]*\/)?[a-z0-9][a-z0-9._~-]*$/;
19
+ const NPM_PACKAGE_MAX_LENGTH = 214;
20
+
21
+ /** What the install dialog holds for one missing interface. */
22
+ export interface DependencyDraft {
23
+ interfacePackage: string;
24
+ /** A candidate's package, or {@link OTHER_PACKAGE_CHOICE}. */
25
+ choice: string;
26
+ /** The package typed for the "Other npm package" choice. */
27
+ otherPackage: string;
28
+ /** The config as typed, JSON; empty means none. */
29
+ config: string;
30
+ }
31
+
32
+ /** Why a draft cannot be sent, by field: i18n keys under the dialog's errors. */
33
+ export type DependencyDraftError =
34
+ | "package_required"
35
+ | "package_invalid"
36
+ | "config_invalid";
37
+
38
+ export interface DependencyDraftErrors {
39
+ package?: DependencyDraftError;
40
+ config?: DependencyDraftError;
41
+ }
42
+
43
+ /** A config as the dialog reads it: absent when left empty. */
44
+ export interface ParsedDependencyConfig {
45
+ isValid: boolean;
46
+ config?: Record<string, unknown>;
47
+ }
48
+
49
+ export function isNpmPackageName(value: string): boolean {
50
+ return (
51
+ value.length <= NPM_PACKAGE_MAX_LENGTH && NPM_PACKAGE_PATTERN.test(value)
52
+ );
53
+ }
54
+
55
+ const isPlainObject = (value: unknown): value is Record<string, unknown> =>
56
+ typeof value === "object" && value !== null && !Array.isArray(value);
57
+
58
+ /** Reads a typed config: empty is no config, anything else a JSON object. */
59
+ export function parseDependencyConfig(text: string): ParsedDependencyConfig {
60
+ if (!text.trim()) return { isValid: true };
61
+ try {
62
+ const value: unknown = JSON.parse(text);
63
+ return isPlainObject(value)
64
+ ? { isValid: true, config: value }
65
+ : { isValid: false };
66
+ } catch {
67
+ return { isValid: false };
68
+ }
69
+ }
70
+
71
+ /**
72
+ * One draft per missing interface, the first candidate picked. A draft of
73
+ * an earlier attempt at the same interface is kept, so a retry starts from
74
+ * what the owner typed.
75
+ */
76
+ export function createDependencyDrafts(
77
+ missing: readonly MissingInterface[],
78
+ previous: readonly DependencyDraft[] = [],
79
+ ): DependencyDraft[] {
80
+ return missing.map(({ interfacePackage, candidates }) => {
81
+ const earlier = previous.find(
82
+ (draft) => draft.interfacePackage === interfacePackage,
83
+ );
84
+ if (earlier) return { ...earlier };
85
+ return {
86
+ interfacePackage,
87
+ choice: candidates[0]?.package ?? OTHER_PACKAGE_CHOICE,
88
+ otherPackage: "",
89
+ config: "",
90
+ };
91
+ });
92
+ }
93
+
94
+ /** The package a draft installs, as the owner picked or typed it. */
95
+ export function draftPackage(draft: DependencyDraft): string {
96
+ return draft.choice === OTHER_PACKAGE_CHOICE
97
+ ? draft.otherPackage.trim()
98
+ : draft.choice;
99
+ }
100
+
101
+ function packageError(
102
+ draft: DependencyDraft,
103
+ ): DependencyDraftError | undefined {
104
+ const packageName = draftPackage(draft);
105
+ if (!packageName) return "package_required";
106
+ return isNpmPackageName(packageName) ? undefined : "package_invalid";
107
+ }
108
+
109
+ export function validateDependencyDraft(
110
+ draft: DependencyDraft,
111
+ ): DependencyDraftErrors {
112
+ const errors: DependencyDraftErrors = {};
113
+ const packageProblem = packageError(draft);
114
+ if (packageProblem) errors.package = packageProblem;
115
+ if (!parseDependencyConfig(draft.config).isValid) {
116
+ errors.config = "config_invalid";
117
+ }
118
+ return errors;
119
+ }
120
+
121
+ export function areDependencyDraftsValid(
122
+ drafts: readonly DependencyDraft[],
123
+ ): boolean {
124
+ return drafts.every(
125
+ (draft) => Object.keys(validateDependencyDraft(draft)).length === 0,
126
+ );
127
+ }
128
+
129
+ function toDependencyChoice(draft: DependencyDraft): StoreDependencyChoice {
130
+ const { config } = parseDependencyConfig(draft.config);
131
+ const choice: StoreDependencyChoice = {
132
+ interfacePackage: draft.interfacePackage,
133
+ package: draftPackage(draft),
134
+ };
135
+ if (config) choice.config = config;
136
+ return choice;
137
+ }
138
+
139
+ /** The install request for module `id`, from drafts already validated. */
140
+ export function buildInstallBody(
141
+ id: string,
142
+ drafts: readonly DependencyDraft[],
143
+ ): StoreActionBody {
144
+ if (drafts.length === 0) return { id };
145
+ return { id, dependencies: drafts.map(toDependencyChoice) };
146
+ }
@@ -2,6 +2,7 @@ import type {
2
2
  ModuleCatalogEntry,
3
3
  ModuleStatus,
4
4
  PageInfo,
5
+ SiteLayout,
5
6
  } from "../../types/page";
6
7
 
7
8
  /** Category of the modules that declare none. */
@@ -148,11 +149,10 @@ export function sortModules(
148
149
 
149
150
  /**
150
151
  * A page as `/dms/sitelayout` serves it: the payload also carries the
151
- * owning module and the parent category, which the shared type leaves out.
152
+ * owning module, which the shared type leaves out.
152
153
  */
153
154
  type ServedPage = PageInfo & {
154
155
  module?: string;
155
- category?: { displayName?: string; isModuleRoot?: boolean };
156
156
  };
157
157
 
158
158
  function isOpenablePage(page: ServedPage): boolean {
@@ -169,19 +169,21 @@ function isOpenablePage(page: ServedPage): boolean {
169
169
  * the empty state can point to where the thing actually lives.
170
170
  */
171
171
  export function findModulePageMatch(
172
- pages: Record<string, PageInfo> | undefined,
172
+ siteLayout: Pick<SiteLayout, "pages" | "categories"> | undefined,
173
173
  query: string,
174
174
  installedModuleIds: ReadonlySet<string>,
175
175
  resolve: DisplayResolver,
176
176
  ): ModulePageMatch | null {
177
177
  const needle = normalizeQuery(query);
178
- if (!needle || !pages) return null;
179
- for (const page of Object.values(pages) as ServedPage[]) {
178
+ if (!needle || !siteLayout) return null;
179
+ for (const page of Object.values(siteLayout.pages) as ServedPage[]) {
180
180
  if (!page.module || !installedModuleIds.has(page.module)) continue;
181
181
  if (!isOpenablePage(page)) continue;
182
182
  const pageTitle = resolve(page.displayName);
183
183
  if (!pageTitle.toLocaleLowerCase().includes(needle)) continue;
184
- const parent = page.category;
184
+ const parent = page.categoryId
185
+ ? siteLayout.categories[page.categoryId]
186
+ : undefined;
185
187
  const sectionTitle =
186
188
  parent?.displayName && !parent.isModuleRoot
187
189
  ? resolve(parent.displayName)
@@ -0,0 +1,19 @@
1
+ // A fetch failure names the request it failed: `[GET] "http://10.0.0.4:5010/
2
+ // dms/page?path=/x": 502 Bad Gateway`. On a server, that is the backend's
3
+ // internal address, which an error page must never show.
4
+ const FETCH_FAILURE = /^\[[A-Z]+\]\s+"[^"]*":\s*(.*)$/s;
5
+ const ABSOLUTE_URL = /\b[a-z][a-z0-9+.-]*:\/\/[^\s"'<>]+/gi;
6
+ const SPACES = /\s{2,}/g;
7
+
8
+ /**
9
+ * The part of an error's message a page may show: a fetch failure's status
10
+ * text alone, and no absolute URL anywhere; nothing when nothing is left.
11
+ */
12
+ export function publicErrorMessage(
13
+ message: string | undefined,
14
+ ): string | undefined {
15
+ if (!message) return undefined;
16
+ const statusText = FETCH_FAILURE.exec(message)?.[1] ?? message;
17
+ const shown = statusText.replace(ABSOLUTE_URL, "").replace(SPACES, " ");
18
+ return shown.trim() || undefined;
19
+ }
@@ -102,15 +102,6 @@ const paddingLeft = computed(() => {
102
102
  const chevronClass = (isOpen: boolean) => {
103
103
  return theme({ isOpen }).chevron();
104
104
  };
105
-
106
- const { prefetchPageLayout } = usePrefetch();
107
-
108
- function onLinkHover(): void {
109
- const { to } = props.item;
110
- if (typeof to === "string") {
111
- prefetchPageLayout(stripQueryAndHash(to));
112
- }
113
- }
114
105
  </script>
115
106
 
116
107
  <template>
@@ -128,7 +119,6 @@ function onLinkHover(): void {
128
119
  :is="item.to ? DmsLink : 'div'"
129
120
  :to="item.to"
130
121
  :class="ui.topLevelButton()"
131
- @mouseenter="onLinkHover"
132
122
  >
133
123
  <UIcon v-if="item.icon" :name="item.icon" :class="ui.icon()" />
134
124
  <UIcon
@@ -150,7 +140,6 @@ function onLinkHover(): void {
150
140
  :class="ui.link()"
151
141
  :style="{ paddingLeft }"
152
142
  :title="item.title"
153
- @mouseenter="onLinkHover"
154
143
  >
155
144
  <UIcon v-if="item.icon" :name="item.icon" :class="ui.icon()" />
156
145
  <span>{{ item.label }}</span>
@@ -206,6 +206,9 @@ function decorateMenuItem(item: DmsMenuItem): DmsMenuItem {
206
206
 
207
207
  return {
208
208
  ...item,
209
+ // Inertia prefetches the page on hover, the way a `DmsLink` does: the
210
+ // visit then shows it without a request, and its components load early.
211
+ ...(item.to ? { prefetch: true } : {}),
209
212
  ...(variant.link ? { class: [item.class, variant.link] } : {}),
210
213
  ...(statusClass ? { trailingIcon: STATUS_DOT_ICON } : {}),
211
214
  badge,
@@ -232,18 +235,6 @@ const groupedItems = computed((): NavigationMenuItem[][] => {
232
235
  );
233
236
  });
234
237
 
235
- const { prefetchPageLayout } = usePrefetch();
236
-
237
- function onMenuMouseover(event: MouseEvent): void {
238
- const target = (event.target as HTMLElement).closest("a[href]");
239
- if (!target) return;
240
-
241
- const href = target.getAttribute("href");
242
- if (href && href.startsWith("/")) {
243
- prefetchPageLayout(stripQueryAndHash(href));
244
- }
245
- }
246
-
247
238
  function isDesiredOpen(id: string): boolean {
248
239
  return props.modelValue?.includes(id) ?? false;
249
240
  }
@@ -271,7 +262,6 @@ function onStateChange(id: string, open: boolean): void {
271
262
  :orientation="orientation"
272
263
  :highlight="orientation === 'vertical'"
273
264
  value-key="id"
274
- @mouseover="onMenuMouseover"
275
265
  >
276
266
  <template #item-leading="{ item, active, ui }">
277
267
  <DmsAccordionItemSync
@@ -16,17 +16,13 @@ export const useSystemState = () => {
16
16
  data: systemState,
17
17
  error,
18
18
  refresh,
19
- } = useDmsLazyAsyncData(
20
- SYSTEM_STATE_CACHE_KEY,
21
- async () => {
22
- const { $authFetch } = useAuthFetch();
23
- return await $authFetch<SystemState>(SYSTEM_STATE_ENDPOINT);
24
- },
25
- {
26
- default: () => ({ ...DEFAULT_SYSTEM_STATE }),
27
- server: true,
28
- },
29
- );
19
+ } = useApiData<SystemState>(SYSTEM_STATE_ENDPOINT, {
20
+ key: SYSTEM_STATE_CACHE_KEY,
21
+ lazy: true,
22
+ default: () => ({ ...DEFAULT_SYSTEM_STATE }),
23
+ // The deployment's state, not the page's data.
24
+ refreshOnPageRefresh: false,
25
+ });
30
26
 
31
27
  const metaTitle = computed(() => systemState.value?.meta?.title ?? "");
32
28
  const metaDescription = computed(