@antelopejs/dms 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/dist/implementations/dms/frontend-backend-proxies.d.ts +3 -0
- package/dist/implementations/dms/frontend-backend-proxies.js +39 -0
- package/dist/implementations/dms/frontend-backend-proxies.js.map +1 -0
- package/dist/implementations/dms/frontend-bootstrap.d.ts +1 -0
- package/dist/implementations/dms/frontend-bootstrap.js +13 -0
- package/dist/implementations/dms/frontend-bootstrap.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +4 -2
- package/dist/implementations/dms/frontend-modules.js +8 -7
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +6 -0
- package/dist/implementations/dms/manifest.js +13 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/menu-categories.d.ts +16 -0
- package/dist/implementations/dms/menu-categories.js +30 -0
- package/dist/implementations/dms/menu-categories.js.map +1 -0
- package/dist/implementations/dms/nav-badges.d.ts +0 -8
- package/dist/implementations/dms/nav-badges.js +0 -16
- package/dist/implementations/dms/nav-badges.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +11 -5
- package/dist/implementations/dms/page.js +56 -41
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms-html-render/index.js +5 -2
- package/dist/implementations/dms-html-render/index.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +4 -3
- package/dist/implementations/dms-notifications/index.js +14 -6
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/pages/settings/settings-index.js +3 -2
- package/dist/pages/settings/settings-index.js.map +1 -1
- package/dist/pages/settings/users/invites.js +2 -2
- package/dist/pages/settings/users/invites.js.map +1 -1
- package/dist/pages/settings/users/member-change-notifications.js +17 -6
- package/dist/pages/settings/users/member-change-notifications.js.map +1 -1
- package/dist/pages/settings/users/member-invite-form.d.ts +3 -2
- package/dist/pages/settings/users/member-invite-form.js +7 -4
- package/dist/pages/settings/users/member-invite-form.js.map +1 -1
- package/dist/pages/settings/users/member-roles-field.js +2 -1
- package/dist/pages/settings/users/member-roles-field.js.map +1 -1
- package/dist/pages/settings/users/members.js +4 -1
- package/dist/pages/settings/users/members.js.map +1 -1
- package/dist/pages/settings/users/notifications.d.ts +6 -0
- package/dist/pages/settings/users/notifications.js +23 -15
- package/dist/pages/settings/users/notifications.js.map +1 -1
- package/dist/pages/settings/users/profile.js +1 -1
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/region-form.d.ts +1 -1
- package/dist/pages/settings/users/region-form.js +2 -1
- package/dist/pages/settings/users/region-form.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.d.ts +3 -1
- package/dist/pages/settings/users/role-editor-store.js +17 -4
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/roles.js +1 -0
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/realtime/registry.d.ts +1 -0
- package/dist/realtime/registry.js +5 -0
- package/dist/realtime/registry.js.map +1 -1
- package/dist/routes/realtime.d.ts +5 -1
- package/dist/routes/realtime.js +16 -9
- package/dist/routes/realtime.js.map +1 -1
- package/dist/test/integration/account-deletion.test.js +2 -2
- package/dist/test/integration/account-deletion.test.js.map +1 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js +2 -1
- package/dist/test/integration/auth/auth-key-rotation.test.js.map +1 -1
- package/dist/test/integration/auth/current-password.test.js +2 -1
- package/dist/test/integration/auth/current-password.test.js.map +1 -1
- package/dist/test/integration/auth/session-revocation.test.js +2 -1
- package/dist/test/integration/auth/session-revocation.test.js.map +1 -1
- package/dist/test/integration/auth/sign-in-country.test.js +2 -1
- package/dist/test/integration/auth/sign-in-country.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-codes.test.js +2 -1
- package/dist/test/integration/auth/two-factor-codes.test.js.map +1 -1
- package/dist/test/integration/auth/two-factor-password.test.js +3 -2
- package/dist/test/integration/auth/two-factor-password.test.js.map +1 -1
- package/dist/test/integration/email-change.test.js +2 -1
- package/dist/test/integration/email-change.test.js.map +1 -1
- package/dist/test/integration/files/component-files.test.js +2 -2
- package/dist/test/integration/files/component-files.test.js.map +1 -1
- package/dist/test/integration/member-settings-access.test.js +8 -7
- package/dist/test/integration/member-settings-access.test.js.map +1 -1
- package/dist/test/integration/menu-access-events.test.d.ts +1 -0
- package/dist/test/integration/menu-access-events.test.js +148 -0
- package/dist/test/integration/menu-access-events.test.js.map +1 -0
- package/dist/test/integration/notification-inbox.test.js +4 -1
- package/dist/test/integration/notification-inbox.test.js.map +1 -1
- package/dist/test/integration/notification-locked-subjects.test.js +3 -2
- package/dist/test/integration/notification-locked-subjects.test.js.map +1 -1
- package/dist/test/integration/notification-unread-badge.test.js +51 -5
- package/dist/test/integration/notification-unread-badge.test.js.map +1 -1
- package/dist/test/integration/page-layout-route-guard.test.d.ts +1 -0
- package/dist/test/integration/page-layout-route-guard.test.js +56 -0
- package/dist/test/integration/page-layout-route-guard.test.js.map +1 -0
- package/dist/test/integration/permission-warnings.test.js +3 -2
- package/dist/test/integration/permission-warnings.test.js.map +1 -1
- package/dist/test/integration/removed-member-notifications.test.js +3 -2
- package/dist/test/integration/removed-member-notifications.test.js.map +1 -1
- package/dist/test/integration/role-delete-reassign.test.js +2 -1
- package/dist/test/integration/role-delete-reassign.test.js.map +1 -1
- package/dist/test/integration/role-permission-ancestors.test.js +2 -1
- package/dist/test/integration/role-permission-ancestors.test.js.map +1 -1
- package/dist/test/unit/frontend-bootstrap.test.js +13 -0
- package/dist/test/unit/frontend-bootstrap.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js +14 -5
- package/dist/test/unit/implementations/dms/category-slug-registry.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.d.ts +1 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js +66 -0
- package/dist/test/unit/implementations/dms/frontend-backend-proxies.test.js.map +1 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js +26 -0
- package/dist/test/unit/implementations/dms/manifest-strip.test.js.map +1 -1
- package/dist/test/unit/implementations/dms/nav-badges.test.js +23 -9
- package/dist/test/unit/implementations/dms/nav-badges.test.js.map +1 -1
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js +22 -4
- package/dist/test/unit/implementations/dms-html-render/generate-html.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js +12 -0
- package/dist/test/unit/interfaces/dms/page-layout-query.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js +1 -1
- package/dist/test/unit/interfaces/dms/page-teardown.test.js.map +1 -1
- package/dist/test/unit/interfaces/dms-base/table-and-card-catalog.test.js +25 -0
- package/dist/test/unit/interfaces/dms-base/table-and-card-catalog.test.js.map +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js +1 -1
- package/dist/test/unit/pages/settings/member-roles-field.test.js.map +1 -1
- package/dist/test/unit/pages/settings/role-delete-confirm.test.js.map +1 -1
- package/dist/test/unit/realtime/menu-topics.test.d.ts +1 -0
- package/dist/test/unit/realtime/menu-topics.test.js +26 -0
- package/dist/test/unit/realtime/menu-topics.test.js.map +1 -0
- package/dist/validation/regional-preferences.schema.d.ts +2 -2
- package/frontend-vue/deferred-plugins.ts +3 -4
- package/frontend-vue/dms.email.ts +5 -1
- package/frontend-vue/dms.frontend.ts +15 -5
- package/frontend-vue/dms.server.ts +74 -0
- package/frontend-vue/layers/dms-auth/app/composables/useLogout.ts +1 -1
- package/frontend-vue/layers/dms-auth/app/composables/usePostLoginRedirect.ts +16 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/2fa.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/backup.vue +3 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/forgot-validation.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/oauth-complete.vue +26 -3
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/recover.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/signup.vue +22 -25
- package/frontend-vue/layers/dms-auth/app/custom-pages/auth/validate.vue +1 -1
- package/frontend-vue/layers/dms-auth/app/middleware/auth.ts +5 -7
- package/frontend-vue/layers/dms-auth/app/plugins/permissions-preload.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +135 -0
- package/frontend-vue/layers/dms-auth/server/actions.ts +251 -0
- package/frontend-vue/layers/dms-auth/server/oauth.ts +260 -0
- package/frontend-vue/layers/dms-auth/server/session.ts +158 -0
- package/frontend-vue/layers/dms-core/app/build/utils/asyncDataScope.ts +47 -0
- package/frontend-vue/layers/dms-core/app/build/utils/permission-preview.ts +6 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +148 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useAuthFetch.ts +7 -20
- package/frontend-vue/layers/dms-core/app/composables/auth/useCurrentUser.ts +2 -2
- package/frontend-vue/layers/dms-core/app/composables/auth/useMultiAccount.ts +18 -5
- package/frontend-vue/layers/dms-core/app/composables/auth/usePermissions.ts +35 -6
- package/frontend-vue/layers/dms-core/app/composables/auth/useSessionRecovery.ts +1 -1
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +20 -14
- package/frontend-vue/layers/dms-core/app/composables/period/periodClock.ts +121 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +42 -0
- package/frontend-vue/layers/dms-core/app/composables/period/periodTimeZone.ts +31 -0
- package/frontend-vue/layers/dms-core/app/composables/period/presetResolvers.ts +48 -19
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +33 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +78 -25
- package/frontend-vue/layers/dms-core/app/composables/realtime/usePageRealtime.ts +3 -0
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +36 -6
- package/frontend-vue/layers/dms-core/app/composables/user/useUserRegionalPreferences.ts +1 -1
- package/frontend-vue/layers/dms-core/app/plugins/async-data-scope.ts +26 -0
- package/frontend-vue/layers/dms-core/app/utils/pageRoutes.ts +22 -0
- package/frontend-vue/layers/dms-core/app/utils/regional.ts +36 -3
- package/frontend-vue/layers/dms-core/app/utils/trailingDeduper.ts +10 -1
- package/frontend-vue/layers/dms-core/shared/types/app-config.ts +48 -4
- package/frontend-vue/layers/dms-layout/app/app.config.ts +42 -23
- package/frontend-vue/layers/dms-layout/app/assets/css/main.css +72 -20
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardFrame.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +0 -3
- package/frontend-vue/layers/dms-layout/app/build/components/notification/NotificationPopover.vue +9 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/members/MemberRolePicker.vue +13 -12
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +17 -7
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationPreferencesForm.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfilePersonalInfo.vue +36 -33
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/profile/ProfileSecuritySummary.vue +1 -2
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-types.ts +5 -4
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecuritySessions.vue +35 -28
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityStatus.vue +0 -1
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/security/SecurityTwoFactor.vue +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/dev-reload/useDevReloadHolder.ts +7 -6
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/sidePanelState.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/navigation/useVisitNavBadges.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationCatalog.ts +25 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationPreferences.ts +44 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/security/useSessionHandoff.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/preferencesSummary.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useAccountData.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfileAvatar.ts +1 -18
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/profile/useProfilePreferences.ts +19 -23
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityFormat.ts +6 -3
- package/frontend-vue/layers/dms-layout/app/build/composables/settings/security/useSecurityOverview.ts +13 -18
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +8 -6
- package/frontend-vue/layers/dms-layout/app/build/utils/publicErrorMessage.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/components/navigation/CollapsedMenuItem.vue +0 -11
- package/frontend-vue/layers/dms-layout/app/components/navigation/NavigationMenu.vue +3 -13
- package/frontend-vue/layers/dms-layout/app/composables/general/useSystemState.ts +7 -11
- package/frontend-vue/layers/dms-layout/app/composables/notification/useNotifications.ts +103 -22
- package/frontend-vue/layers/dms-layout/app/composables/page/useModulesListing.ts +25 -102
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +0 -10
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +177 -96
- package/frontend-vue/layers/dms-layout/app/composables/profile/useUserLanguage.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +6 -28
- package/frontend-vue/layers/dms-layout/app/emails/components/EmailLayout.vue +31 -11
- package/frontend-vue/layers/dms-layout/app/error.vue +5 -2
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +1 -1
- package/frontend-vue/layers/dms-layout/app/middleware/page-leave-guard.global.ts +2 -2
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +12 -9
- package/frontend-vue/layers/dms-layout/app/plugins/dms-menu-sync.client.ts +31 -3
- package/frontend-vue/layers/dms-layout/app/plugins/favicon.ts +58 -0
- package/frontend-vue/layers/dms-layout/app/plugins/notification-stream.client.ts +15 -7
- package/frontend-vue/layers/dms-layout/app/plugins/profile-sync.client.ts +5 -1
- package/frontend-vue/layers/dms-layout/app/plugins/regional-preferences.ts +10 -4
- package/frontend-vue/layers/dms-layout/app/plugins/time-zone-cookie.client.ts +12 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +13 -0
- package/frontend-vue/layers/dms-layout/server/html-render.ts +58 -0
- package/frontend-vue/layers/dms-layout/server/render-token.ts +95 -0
- package/frontend-vue/layers/dms-onboarding/app/composables/onboarding/useOnboarding.ts +25 -21
- package/frontend-vue/layers/dms-onboarding/app/middleware/onboarding.global.ts +1 -1
- package/frontend-vue/layers/dms-onboarding/app/plugins/onboarding-preload.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +4 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/files/useFileMetadata.ts +162 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formRecordLoad.ts +23 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageDataRefresh.ts +101 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/navigation/useNavBadges.ts +33 -6
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +19 -16
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodQuery.ts +40 -20
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +6 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableListData.ts +42 -14
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +19 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +5 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +9 -9
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +20 -18
- package/frontend-vue/layers/dms-ui/app/components/form/components/Cascader.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/File.vue +26 -132
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +40 -105
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +2 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +4 -4
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +9 -13
- package/frontend-vue/layers/dms-ui/app/components/table-view/CascaderPath.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/FilePreview.vue +12 -24
- package/frontend-vue/layers/dms-ui/app/components/table-view/ImagePreview.vue +17 -29
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +7 -3
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +20 -8
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +8 -11
- package/frontend-vue/layers/dms-ui/app/composables/chart/useChartFetch.ts +146 -182
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLiveRefresh.ts +8 -2
- package/frontend-vue/layers/dms-ui/app/composables/useFileReadUrls.ts +11 -99
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +32 -3
- package/frontend-vue/package.json +2 -2
- package/frontend-vue/pnpm-lock.yaml +5 -5
- package/frontend-vue/server/private-options.ts +27 -0
- package/frontend-vue/tests/async-data-scope.test.ts +121 -0
- package/frontend-vue/tests/auth-middleware-error.test.ts +3 -3
- package/frontend-vue/tests/banner-block-fetch.test.ts +3 -0
- package/frontend-vue/tests/block-fetch-options-forwarding.test.ts +3 -0
- package/frontend-vue/tests/block-items-period-realtime.test.ts +3 -0
- package/frontend-vue/tests/block-not-found.test.ts +16 -2
- package/frontend-vue/tests/block-page-refresh.test.ts +3 -0
- package/frontend-vue/tests/block-route-params.test.ts +3 -0
- package/frontend-vue/tests/branding-defaults.test.ts +34 -0
- package/frontend-vue/tests/builtin-emails.test.ts +23 -2
- package/frontend-vue/tests/chart-live-pause.test.ts +4 -1
- package/frontend-vue/tests/chart-value-propagation.test.ts +4 -1
- package/frontend-vue/tests/composed-text-blocks.test.ts +3 -0
- package/frontend-vue/tests/email-layout.test.ts +66 -4
- package/frontend-vue/tests/error-page-message.test.ts +25 -2
- package/frontend-vue/tests/favicon.test.ts +131 -0
- package/frontend-vue/tests/file-fields-ssr.test.ts +145 -0
- package/frontend-vue/tests/file-read-urls.test.ts +145 -34
- package/frontend-vue/tests/file-unavailable.test.ts +5 -1
- package/frontend-vue/tests/fixtures/dataEngine.ts +44 -0
- package/frontend-vue/tests/form-load-error.test.ts +55 -1
- package/frontend-vue/tests/helpers/api-data-runtime.ts +77 -0
- package/frontend-vue/tests/image-hydration-ids.test.ts +21 -16
- package/frontend-vue/tests/image-reference-preservation.test.ts +59 -53
- package/frontend-vue/tests/input-number-adjust.test.ts +14 -0
- package/frontend-vue/tests/key-value-list-copy.test.ts +3 -0
- package/frontend-vue/tests/member-role-picker.test.ts +4 -2
- package/frontend-vue/tests/menu-sync-reconnect.test.ts +47 -0
- package/frontend-vue/tests/metadata-retry.test.ts +5 -1
- package/frontend-vue/tests/module-routing.test.ts +2 -5
- package/frontend-vue/tests/modules-catalog.test.ts +17 -4
- package/frontend-vue/tests/modules-listing-transfer.test.ts +95 -0
- package/frontend-vue/tests/multi-account-validate.test.ts +23 -1
- package/frontend-vue/tests/nav-badge-tones.test.ts +66 -3
- package/frontend-vue/tests/notification-counts-from-writes.test.ts +223 -0
- package/frontend-vue/tests/notification-counts-transfer.test.ts +88 -0
- package/frontend-vue/tests/notification-inbox-display.test.ts +57 -5
- package/frontend-vue/tests/notification-popover-errors.test.ts +21 -1
- package/frontend-vue/tests/oauth-complete-handoff.test.ts +209 -0
- package/frontend-vue/tests/onboarding-middleware.test.ts +1 -1
- package/frontend-vue/tests/onboarding-transfer.test.ts +106 -0
- package/frontend-vue/tests/page-data-refresh.test.ts +141 -0
- package/frontend-vue/tests/page-fill-height.test.ts +16 -3
- package/frontend-vue/tests/page-no-visible-blocks.test.ts +12 -2
- package/frontend-vue/tests/page-realtime-remount.test.ts +1 -1
- package/frontend-vue/tests/page-record-label.test.ts +0 -7
- package/frontend-vue/tests/page-validation-redirect.test.ts +12 -2
- package/frontend-vue/tests/period-hydration-now.test.ts +185 -0
- package/frontend-vue/tests/period-live-pause.test.ts +3 -0
- package/frontend-vue/tests/period-reader-zone.test.ts +137 -0
- package/frontend-vue/tests/period-scope-first-fetch.test.ts +17 -1
- package/frontend-vue/tests/period-ssr.test.ts +195 -0
- package/frontend-vue/tests/permission-preview-partial.test.ts +0 -2
- package/frontend-vue/tests/permissions-transfer.test.ts +194 -0
- package/frontend-vue/tests/post-login-redirect.test.ts +10 -5
- package/frontend-vue/tests/profile-personal-info-ssr.test.ts +150 -0
- package/frontend-vue/tests/profile-preferences-summary.test.ts +15 -11
- package/frontend-vue/tests/realtime-reconnect.test.ts +22 -3
- package/frontend-vue/tests/roles-name-errors.test.ts +1 -1
- package/frontend-vue/tests/row-side-panel.test.ts +1 -1
- package/frontend-vue/tests/security-sessions-load-error.test.ts +3 -1
- package/frontend-vue/tests/server-routes-vs-pages.test.ts +26 -0
- package/frontend-vue/tests/server-routes.test.ts +582 -0
- package/frontend-vue/tests/session-handoff.test.ts +1 -1
- package/frontend-vue/tests/settings-reads-ssr.test.ts +232 -0
- package/frontend-vue/tests/side-panels.test.ts +8 -1
- package/frontend-vue/tests/sign-in-exit-history.test.ts +219 -0
- package/frontend-vue/tests/signup-invitation-link.test.ts +56 -8
- package/frontend-vue/tests/site-layout-categories.test.ts +42 -0
- package/frontend-vue/tests/site-layout-hydration.test.ts +3 -3
- package/frontend-vue/tests/site-layout-rendered.test.ts +103 -0
- package/frontend-vue/tests/site-layout-resync.test.ts +87 -0
- package/frontend-vue/tests/ssr-guard-preload.test.ts +188 -0
- package/frontend-vue/tests/table-cell-tones.test.ts +36 -1
- package/frontend-vue/tests/table-data-ssr.test.ts +195 -0
- package/frontend-vue/tests/table-list-data-requests.test.ts +154 -0
- package/frontend-vue/tests/table-retry-loading.test.ts +37 -0
- package/frontend-vue/tests/table-source-navigation.test.ts +0 -1
- package/frontend-vue/tests/table-source-period-scope.test.ts +0 -1
- package/frontend-vue/tests/table-view-views.test.ts +1 -1
- package/frontend-vue/tests/top-list-card-actions.test.ts +3 -0
- package/frontend-vue/tests/top-list-card-period-badge.test.ts +79 -0
- package/frontend-vue/tests/top-list-card-value-suffix.test.ts +3 -0
- package/frontend-vue/tests/trailing-deduper.test.ts +22 -0
- package/frontend-vue/tests/use-api-data-update.test.ts +119 -0
- package/frontend-vue/tests/use-api-data.test.ts +410 -0
- package/frontend-vue/tests/user-language.test.ts +8 -5
- package/frontend-vue/tests/widget-label-i18n.test.ts +4 -1
- package/frontend-vue/vitest.config.ts +6 -0
- package/package.json +2 -2
- package/skills/dms/SKILL.md +2 -1
- package/skills/dms-module/SKILL.md +1 -1
- package/skills/dms-pages/SKILL.md +4 -1
- package/frontend-vue/layers/dms-core/app/build/composables/page/usePageRecordLabel.ts +0 -9
- package/frontend-vue/layers/dms-layout/app/composables/page/usePrefetch.ts +0 -21
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useControlError.ts +0 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts +0 -23
- package/frontend-vue/tests/control-error-path.test.ts +0 -11
- package/frontend-vue/tests/modules-listing-per-request.test.ts +0 -104
- package/frontend-vue/tests/page-layout-background-refresh.test.ts +0 -321
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
/**
|
|
3
|
+
* `useApiData` through the runtime's real async data and the real
|
|
4
|
+
* `$authFetch`: a server render reads, renders and hands over the data, the
|
|
5
|
+
* browser hydrates with it and follows reactive requests, and every request
|
|
6
|
+
* keeps `$authFetch`'s headers and session retry.
|
|
7
|
+
*/
|
|
8
|
+
import {
|
|
9
|
+
createApp,
|
|
10
|
+
createSSRApp,
|
|
11
|
+
defineComponent,
|
|
12
|
+
h,
|
|
13
|
+
nextTick,
|
|
14
|
+
ref,
|
|
15
|
+
type App,
|
|
16
|
+
type Component,
|
|
17
|
+
} from "vue";
|
|
18
|
+
import { renderToString } from "vue/server-renderer";
|
|
19
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
20
|
+
import type { DmsFrontendRuntime, DmsPayload } from "#dms/frontend-module";
|
|
21
|
+
|
|
22
|
+
const engine = await import("#dms/frontend-module");
|
|
23
|
+
vi.stubGlobal("$fetch", engine.$fetch);
|
|
24
|
+
vi.stubGlobal("useDmsFetch", engine.useDmsFetch);
|
|
25
|
+
vi.stubGlobal("useDmsAsyncData", engine.useDmsAsyncData);
|
|
26
|
+
vi.stubGlobal("useDmsState", engine.useDmsState);
|
|
27
|
+
vi.stubGlobal("useDmsRuntimeConfig", () => ({
|
|
28
|
+
public: { dms: { baseURL: "" } },
|
|
29
|
+
}));
|
|
30
|
+
// The app's language, which a test may switch.
|
|
31
|
+
const appLocale = ref("fr");
|
|
32
|
+
vi.stubGlobal("useDmsApp", () => ({ $i18n: { locale: appLocale } }));
|
|
33
|
+
vi.stubGlobal("HTTP_UNAUTHORIZED", 401);
|
|
34
|
+
vi.stubGlobal("useDmsUserSession", () => ({ loggedIn: { value: true } }));
|
|
35
|
+
const refreshSession = vi.fn(async () => true);
|
|
36
|
+
vi.stubGlobal("useSessionRecovery", () => ({
|
|
37
|
+
refreshSession,
|
|
38
|
+
reconcileSession: vi.fn(),
|
|
39
|
+
redirectToAuth: vi.fn(),
|
|
40
|
+
}));
|
|
41
|
+
const { useAuthFetch } = await import(
|
|
42
|
+
"../layers/dms-core/app/composables/auth/useAuthFetch"
|
|
43
|
+
);
|
|
44
|
+
vi.stubGlobal("useAuthFetch", useAuthFetch);
|
|
45
|
+
const { useApiData, useApiPagedData } = await import(
|
|
46
|
+
"../layers/dms-core/app/composables/auth/useApiData"
|
|
47
|
+
);
|
|
48
|
+
vi.stubGlobal("useApiData", useApiData);
|
|
49
|
+
vi.stubGlobal("useDmsPagedData", engine.useDmsPagedData);
|
|
50
|
+
|
|
51
|
+
interface Sent {
|
|
52
|
+
url: string;
|
|
53
|
+
query?: Record<string, unknown>;
|
|
54
|
+
headers: Headers;
|
|
55
|
+
signal?: AbortSignal;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const sent: Sent[] = [];
|
|
59
|
+
let failNextWith: number | undefined;
|
|
60
|
+
|
|
61
|
+
/** The transport under `$fetch`: what reaches the backend. */
|
|
62
|
+
const transport = (async (
|
|
63
|
+
url: string,
|
|
64
|
+
options: {
|
|
65
|
+
query?: Record<string, unknown>;
|
|
66
|
+
headers?: HeadersInit;
|
|
67
|
+
signal?: AbortSignal;
|
|
68
|
+
onRequest?: (context: { options: { headers: Headers } }) => void;
|
|
69
|
+
} = {},
|
|
70
|
+
) => {
|
|
71
|
+
const headers = new Headers(options.headers);
|
|
72
|
+
options.onRequest?.({ options: { headers } });
|
|
73
|
+
sent.push({ url, query: options.query, headers, signal: options.signal });
|
|
74
|
+
if (failNextWith) {
|
|
75
|
+
const status = failNextWith;
|
|
76
|
+
failNextWith = undefined;
|
|
77
|
+
throw Object.assign(new Error("Unauthorized"), {
|
|
78
|
+
statusCode: status,
|
|
79
|
+
response: { status },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return { url, page: options.query?.page ?? null };
|
|
83
|
+
}) as never;
|
|
84
|
+
|
|
85
|
+
const mounted: App[] = [];
|
|
86
|
+
|
|
87
|
+
beforeEach(() => {
|
|
88
|
+
sent.length = 0;
|
|
89
|
+
failNextWith = undefined;
|
|
90
|
+
refreshSession.mockClear();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
afterEach(() => {
|
|
94
|
+
mounted.splice(0).forEach((app) => app.unmount());
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
async function serverRender(
|
|
98
|
+
root: Component,
|
|
99
|
+
): Promise<{ html: string; payload: DmsPayload }> {
|
|
100
|
+
const runtime = engine.createDmsFrontendRuntime(transport, {}, true);
|
|
101
|
+
const app = createSSRApp(root);
|
|
102
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
103
|
+
const html = await renderToString(app);
|
|
104
|
+
return { html, payload: engine.serializeDmsPayload(runtime) };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function hydrate(root: Component, html: string, payload: DmsPayload) {
|
|
108
|
+
const runtime: DmsFrontendRuntime = engine.createDmsFrontendRuntime(
|
|
109
|
+
transport,
|
|
110
|
+
{},
|
|
111
|
+
);
|
|
112
|
+
engine.restoreDmsPayload(runtime, structuredClone(payload));
|
|
113
|
+
const container = document.createElement("div");
|
|
114
|
+
container.innerHTML = html;
|
|
115
|
+
const app = createSSRApp(root);
|
|
116
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
117
|
+
app.mount(container);
|
|
118
|
+
mounted.push(app);
|
|
119
|
+
return container;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const settle = async () => {
|
|
123
|
+
await nextTick();
|
|
124
|
+
await new Promise((done) => setTimeout(done, 0));
|
|
125
|
+
await nextTick();
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
describe("useApiData", () => {
|
|
129
|
+
it("reads in the server render, and the browser hydrates without asking again", async () => {
|
|
130
|
+
const page = ref(1);
|
|
131
|
+
const Orders = defineComponent({
|
|
132
|
+
setup() {
|
|
133
|
+
const { data } = useApiData<{ page: number }>("/api/orders", {
|
|
134
|
+
query: () => ({ page: page.value }),
|
|
135
|
+
});
|
|
136
|
+
return () => h("p", `page:${data.value?.page}`);
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const { html, payload } = await serverRender(Orders);
|
|
141
|
+
expect(html).toBe("<p>page:1</p>");
|
|
142
|
+
expect(sent).toHaveLength(1);
|
|
143
|
+
expect(sent[0]!.headers.get("x-content-language")).toBe("fr");
|
|
144
|
+
expect(sent[0]!.signal).toBeInstanceOf(AbortSignal);
|
|
145
|
+
expect(payload.data).toEqual({
|
|
146
|
+
[engine.dmsRequestKey("/api/orders", { query: { page: 1 } })!]: {
|
|
147
|
+
url: "/api/orders",
|
|
148
|
+
page: 1,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
sent.length = 0;
|
|
153
|
+
const container = hydrate(Orders, html, payload);
|
|
154
|
+
await settle();
|
|
155
|
+
expect(sent).toEqual([]);
|
|
156
|
+
|
|
157
|
+
page.value = 2;
|
|
158
|
+
await settle();
|
|
159
|
+
expect(sent.map(({ query }) => query)).toEqual([{ page: 2 }]);
|
|
160
|
+
expect(container.textContent).toBe("page:2");
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it("reads nothing while its URL is null", async () => {
|
|
164
|
+
const id = ref<string | null>(null);
|
|
165
|
+
const Detail = defineComponent({
|
|
166
|
+
setup() {
|
|
167
|
+
const { data } = useApiData<{ url: string }>(() =>
|
|
168
|
+
id.value ? `/api/orders/${id.value}` : null,
|
|
169
|
+
);
|
|
170
|
+
return () => h("p", data.value?.url ?? "none");
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const { html, payload } = await serverRender(Detail);
|
|
175
|
+
expect(html).toBe("<p>none</p>");
|
|
176
|
+
const container = hydrate(Detail, html, payload);
|
|
177
|
+
await settle();
|
|
178
|
+
expect(sent).toEqual([]);
|
|
179
|
+
|
|
180
|
+
id.value = "7";
|
|
181
|
+
await settle();
|
|
182
|
+
expect(container.textContent).toBe("/api/orders/7");
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it("loads data made of several requests under one key", async () => {
|
|
186
|
+
const Summary = defineComponent({
|
|
187
|
+
setup() {
|
|
188
|
+
const { data } = useApiData(
|
|
189
|
+
"orders-summary",
|
|
190
|
+
async ({ $authFetch, signal }) => {
|
|
191
|
+
const [orders, stats] = await Promise.all([
|
|
192
|
+
$authFetch<{ url: string }>("/api/orders", { signal }),
|
|
193
|
+
$authFetch<{ url: string }>("/api/stats", { signal }),
|
|
194
|
+
]);
|
|
195
|
+
return `${orders.url}+${stats.url}`;
|
|
196
|
+
},
|
|
197
|
+
);
|
|
198
|
+
return () => h("p", String(data.value));
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const { html, payload } = await serverRender(Summary);
|
|
203
|
+
expect(html).toBe("<p>/api/orders+/api/stats</p>");
|
|
204
|
+
expect(payload.data["orders-summary"]).toBe("/api/orders+/api/stats");
|
|
205
|
+
expect(sent.every(({ signal }) => signal instanceof AbortSignal)).toBe(
|
|
206
|
+
true,
|
|
207
|
+
);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("refreshes an expired session and reads again", async () => {
|
|
211
|
+
failNextWith = 401;
|
|
212
|
+
const Orders = defineComponent({
|
|
213
|
+
setup() {
|
|
214
|
+
const { data } = useApiData<{ url: string }>("/api/orders");
|
|
215
|
+
return () => h("p", data.value?.url ?? "none");
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const { html } = await serverRender(Orders);
|
|
220
|
+
expect(html).toBe("<p>/api/orders</p>");
|
|
221
|
+
expect(refreshSession).toHaveBeenCalledTimes(1);
|
|
222
|
+
expect(sent.map(({ headers }) => headers.get("x-auth-retry"))).toEqual([
|
|
223
|
+
null,
|
|
224
|
+
"1",
|
|
225
|
+
]);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
it("waits for execute() with immediate: false, as a dialog does", async () => {
|
|
229
|
+
let execute: (() => Promise<void>) | undefined;
|
|
230
|
+
const Dialog = defineComponent({
|
|
231
|
+
setup() {
|
|
232
|
+
const result = useApiData<{ url: string }>("/api/invoice", {
|
|
233
|
+
immediate: false,
|
|
234
|
+
});
|
|
235
|
+
execute = () => result.execute();
|
|
236
|
+
return () => h("p", result.data.value?.url ?? "closed");
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
const container = document.createElement("div");
|
|
240
|
+
const runtime = engine.createDmsFrontendRuntime(transport);
|
|
241
|
+
const dialog = createApp(Dialog);
|
|
242
|
+
engine.provideDmsFrontendRuntime(dialog, runtime);
|
|
243
|
+
dialog.mount(container);
|
|
244
|
+
mounted.push(dialog);
|
|
245
|
+
await settle();
|
|
246
|
+
expect(sent).toEqual([]);
|
|
247
|
+
expect(container.textContent).toBe("closed");
|
|
248
|
+
|
|
249
|
+
await execute!();
|
|
250
|
+
await settle();
|
|
251
|
+
expect(sent.map(({ url }) => url)).toEqual(["/api/invoice"]);
|
|
252
|
+
expect(container.textContent).toBe("/api/invoice");
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("polls with refreshInterval while a component shows the data", async () => {
|
|
256
|
+
const Status = defineComponent({
|
|
257
|
+
setup() {
|
|
258
|
+
const { data } = useApiData<{ url: string }>("/api/status", {
|
|
259
|
+
refreshInterval: 20,
|
|
260
|
+
});
|
|
261
|
+
return () => h("p", data.value?.url ?? "none");
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
const runtime = engine.createDmsFrontendRuntime(transport);
|
|
265
|
+
const app = createApp(Status);
|
|
266
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
267
|
+
app.mount(document.createElement("div"));
|
|
268
|
+
await settle();
|
|
269
|
+
expect(sent).toHaveLength(1);
|
|
270
|
+
await new Promise((done) => setTimeout(done, 70));
|
|
271
|
+
expect(sent.length).toBeGreaterThan(1);
|
|
272
|
+
|
|
273
|
+
app.unmount();
|
|
274
|
+
const afterUnmount = sent.length;
|
|
275
|
+
await new Promise((done) => setTimeout(done, 70));
|
|
276
|
+
expect(sent).toHaveLength(afterUnmount);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it("follows a reactive refreshInterval, and stops at 0", async () => {
|
|
280
|
+
const interval = ref(20);
|
|
281
|
+
const Install = defineComponent({
|
|
282
|
+
setup() {
|
|
283
|
+
useApiData("/api/install-check", { refreshInterval: interval });
|
|
284
|
+
return () => h("p");
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
const runtime = engine.createDmsFrontendRuntime(transport);
|
|
288
|
+
const app = createApp(Install);
|
|
289
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
290
|
+
app.mount(document.createElement("div"));
|
|
291
|
+
mounted.push(app);
|
|
292
|
+
await new Promise((done) => setTimeout(done, 70));
|
|
293
|
+
expect(sent.length).toBeGreaterThan(1);
|
|
294
|
+
|
|
295
|
+
interval.value = 0;
|
|
296
|
+
await settle();
|
|
297
|
+
const stopped = sent.length;
|
|
298
|
+
await new Promise((done) => setTimeout(done, 70));
|
|
299
|
+
expect(sent).toHaveLength(stopped);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
it("polls from a plugin, outside any component", async () => {
|
|
303
|
+
const runtime = engine.createDmsFrontendRuntime(transport);
|
|
304
|
+
const app = createApp({ render: () => null });
|
|
305
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
306
|
+
const polling = ref(true);
|
|
307
|
+
await settle();
|
|
308
|
+
app.runWithContext(() =>
|
|
309
|
+
useApiData("/api/_ai/status", {
|
|
310
|
+
refreshInterval: () => (polling.value ? 20 : null),
|
|
311
|
+
}),
|
|
312
|
+
);
|
|
313
|
+
await new Promise((done) => setTimeout(done, 70));
|
|
314
|
+
expect(sent.length).toBeGreaterThan(1);
|
|
315
|
+
|
|
316
|
+
polling.value = false;
|
|
317
|
+
await settle();
|
|
318
|
+
const stopped = sent.length;
|
|
319
|
+
await new Promise((done) => setTimeout(done, 70));
|
|
320
|
+
expect(sent).toHaveLength(stopped);
|
|
321
|
+
runtime.scope.stop();
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it("asks in the language current when each request leaves", async () => {
|
|
325
|
+
const runtime = engine.createDmsFrontendRuntime(transport);
|
|
326
|
+
const app = createApp({ render: () => null });
|
|
327
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
328
|
+
const { $authFetch } = app.runWithContext(() => useAuthFetch());
|
|
329
|
+
try {
|
|
330
|
+
appLocale.value = "de";
|
|
331
|
+
await app.runWithContext(() => $authFetch("/api/orders"));
|
|
332
|
+
expect(sent[0]!.headers.get("x-content-language")).toBe("de");
|
|
333
|
+
} finally {
|
|
334
|
+
appLocale.value = "fr";
|
|
335
|
+
runtime.scope.stop();
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("is the one read helper: useAuthFetch returns $authFetch alone", () => {
|
|
340
|
+
expect(Object.keys(useAuthFetch())).toEqual(["$authFetch"]);
|
|
341
|
+
});
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
describe("useApiPagedData", () => {
|
|
345
|
+
interface Listing {
|
|
346
|
+
url: string;
|
|
347
|
+
page: number | null;
|
|
348
|
+
}
|
|
349
|
+
const LAST_PAGE = 3;
|
|
350
|
+
|
|
351
|
+
function listing() {
|
|
352
|
+
let list: ReturnType<typeof useApiPagedData<Listing, number>> | undefined;
|
|
353
|
+
const Explorer = defineComponent({
|
|
354
|
+
setup() {
|
|
355
|
+
list = useApiPagedData<Listing, number>(
|
|
356
|
+
"media:root",
|
|
357
|
+
({ $authFetch, cursor, signal }) =>
|
|
358
|
+
$authFetch<Listing>("/api/media", {
|
|
359
|
+
query: { page: cursor },
|
|
360
|
+
signal,
|
|
361
|
+
}),
|
|
362
|
+
{
|
|
363
|
+
initialCursor: 1,
|
|
364
|
+
nextCursor: (page) =>
|
|
365
|
+
(page.page ?? LAST_PAGE) < LAST_PAGE ? page.page! + 1 : null,
|
|
366
|
+
},
|
|
367
|
+
);
|
|
368
|
+
return () =>
|
|
369
|
+
h("p", list!.pages.value.map((page) => page.page).join(","));
|
|
370
|
+
},
|
|
371
|
+
});
|
|
372
|
+
return { Explorer, list: () => list! };
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
it("renders the first page on the server, then loads more in the browser", async () => {
|
|
376
|
+
const server = listing();
|
|
377
|
+
const { html, payload } = await serverRender(server.Explorer);
|
|
378
|
+
expect(html).toBe("<p>1</p>");
|
|
379
|
+
expect(sent[0]!.headers.get("x-content-language")).toBe("fr");
|
|
380
|
+
|
|
381
|
+
sent.length = 0;
|
|
382
|
+
const browser = listing();
|
|
383
|
+
const container = hydrate(browser.Explorer, html, payload);
|
|
384
|
+
await settle();
|
|
385
|
+
expect(sent).toEqual([]);
|
|
386
|
+
|
|
387
|
+
await browser.list().loadMore();
|
|
388
|
+
await browser.list().loadMore();
|
|
389
|
+
await settle();
|
|
390
|
+
expect(sent.map(({ query }) => query)).toEqual([{ page: 2 }, { page: 3 }]);
|
|
391
|
+
expect(container.textContent).toBe("1,2,3");
|
|
392
|
+
expect(browser.list().hasMore.value).toBe(false);
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
it("reads every loaded page again on a refresh", async () => {
|
|
396
|
+
const { Explorer, list } = listing();
|
|
397
|
+
const runtime = engine.createDmsFrontendRuntime(transport);
|
|
398
|
+
const app = createApp(Explorer);
|
|
399
|
+
engine.provideDmsFrontendRuntime(app, runtime);
|
|
400
|
+
app.mount(document.createElement("div"));
|
|
401
|
+
mounted.push(app);
|
|
402
|
+
await settle();
|
|
403
|
+
await list().loadMore();
|
|
404
|
+
sent.length = 0;
|
|
405
|
+
|
|
406
|
+
await list().refresh();
|
|
407
|
+
expect(sent.map(({ query }) => query)).toEqual([{ page: 1 }, { page: 2 }]);
|
|
408
|
+
expect(list().pages.value).toHaveLength(2);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
@@ -17,7 +17,7 @@ function stubComposables(authFetch = vi.fn(async () => ({}))): LanguageHarness {
|
|
|
17
17
|
$i18n: { setLocale: harness.setLocale },
|
|
18
18
|
}));
|
|
19
19
|
vi.stubGlobal("useAuthFetch", () => ({ $authFetch: harness.authFetch }));
|
|
20
|
-
vi.stubGlobal("
|
|
20
|
+
vi.stubGlobal("useDmsUserSession", () => ({
|
|
21
21
|
user: { value: { name: "Ada", email: "ada@example.com", language: "en" } },
|
|
22
22
|
fetch: harness.refreshUser,
|
|
23
23
|
}));
|
|
@@ -35,10 +35,13 @@ describe("useUserLanguage", () => {
|
|
|
35
35
|
await useUserLanguage().changeLanguage("fr");
|
|
36
36
|
|
|
37
37
|
expect(harness.setLocale).toHaveBeenCalledWith("fr");
|
|
38
|
-
expect(harness.authFetch).toHaveBeenCalledWith(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
expect(harness.authFetch).toHaveBeenCalledWith(
|
|
39
|
+
"/dms/pages/settings/user/profile",
|
|
40
|
+
{
|
|
41
|
+
method: "POST",
|
|
42
|
+
body: { name: "Ada", email: "ada@example.com", language: "fr" },
|
|
43
|
+
},
|
|
44
|
+
);
|
|
42
45
|
expect(harness.refreshUser).toHaveBeenCalled();
|
|
43
46
|
});
|
|
44
47
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createSSRApp, defineComponent, h, ref, type PropType } from "vue";
|
|
2
2
|
import { renderToString } from "vue/server-renderer";
|
|
3
|
-
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { beforeEach, afterEach, describe, expect, it, vi } from "vitest";
|
|
4
|
+
import { useRealDataEngine } from "./fixtures/dataEngine";
|
|
4
5
|
import Chart from "../layers/dms-ui/app/components/chart/Chart.vue";
|
|
5
6
|
import ChartCard from "../layers/dms-ui/app/components/chart/ChartCard.vue";
|
|
6
7
|
import TopListCard from "../layers/dms-ui/app/components/top-list/TopListCard.vue";
|
|
@@ -144,6 +145,8 @@ afterEach(() => {
|
|
|
144
145
|
sendComponentEvent.mockClear();
|
|
145
146
|
});
|
|
146
147
|
|
|
148
|
+
beforeEach(() => useRealDataEngine());
|
|
149
|
+
|
|
147
150
|
describe("module-supplied widget strings and the $ convention", () => {
|
|
148
151
|
it("resolves top list row titles, descriptions and aria-labels", async () => {
|
|
149
152
|
const html = await renderTopList();
|
|
@@ -15,6 +15,12 @@ export default defineConfig({
|
|
|
15
15
|
__dirname,
|
|
16
16
|
"node_modules/@antelopejs/dms-frontend/templates/vue/frontend-module.ts",
|
|
17
17
|
),
|
|
18
|
+
// The server routes run under Vitest through the renderer's own router
|
|
19
|
+
// (`module-testing.mjs`), so their import resolves to its definitions.
|
|
20
|
+
"#dms/server": resolve(
|
|
21
|
+
__dirname,
|
|
22
|
+
"node_modules/@antelopejs/dms-frontend/templates/vue/server-module.ts",
|
|
23
|
+
),
|
|
18
24
|
"#shortcuts-aggregated": resolve(
|
|
19
25
|
__dirname,
|
|
20
26
|
"layers/dms-ui/app/config/shortcuts-registry.ts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antelopejs/dms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/AntelopeJS/dms.git"
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"@antelopejs/interface-data-api": ">=0.2.0 <1.0.0",
|
|
38
38
|
"@antelopejs/interface-database": ">=0.1.8 <1.0.0",
|
|
39
39
|
"@antelopejs/interface-database-decorators": ">=0.1.7 <1.0.0",
|
|
40
|
-
"@antelopejs/interface-dms": ">=0.
|
|
40
|
+
"@antelopejs/interface-dms": ">=0.7.0 <0.8.0",
|
|
41
41
|
"@antelopejs/interface-email": ">=0.0.5 <1.0.0",
|
|
42
42
|
"@antelopejs/interface-file-storage": ">=0.1.3 <1.0.0",
|
|
43
43
|
"archiver": "^7.0.1",
|
package/skills/dms/SKILL.md
CHANGED
|
@@ -31,7 +31,8 @@ For the framework underneath (the core, `ajs` CLI, interfaces as versioned contr
|
|
|
31
31
|
`@RegisterPage()`. Its **static fields are components** — `Form`, `TableView`, `Chart*`,
|
|
32
32
|
`Grid`, `KpiCard`, … — fluent builders producing a **serializable description**, not live UI: the
|
|
33
33
|
frontend renders it. **Components fetch their own data** via URLs (`fetchUrl`, `submitUrl`, …) —
|
|
34
|
-
routes often declared `@Get`/`@Post` on the same page class
|
|
34
|
+
routes often declared `@Get`/`@Post` on the same page class, served under `/dms/pages<slug>`
|
|
35
|
+
(`pageRoutePath(slug, route)`); the backend stays the source of truth.
|
|
35
36
|
A URL names the record of its page with `{{params.id}}` / `{{query.X}}` (filled from the page URL, percent-encoded;
|
|
36
37
|
nothing is fetched while one is unresolved) — every token is `{{<scope>.<key>}}`: `params`, `query`, `row` (an
|
|
37
38
|
action's row), `response` (a form's redirect), and the same page tokens work in every URL and link option, and `.watch(FormEvents.SUBMIT_SUCCESS,
|
|
@@ -79,7 +79,7 @@ module's frontend; without it the module is backend-only.
|
|
|
79
79
|
|
|
80
80
|
`renderer` names the framework (Vue 3), not a loader release. The `@antelopejs/dms-frontend`
|
|
81
81
|
releases the frontend module runs on go in the frontend module's own `package.json`, as
|
|
82
|
-
`"engines": { "@antelopejs/dms-frontend": ">=0.
|
|
82
|
+
`"engines": { "@antelopejs/dms-frontend": ">=0.6.0 <0.7.0" }`: the loader refuses the module
|
|
83
83
|
outside that range. Cap it below the next loader minor while the loader is 0.x.
|
|
84
84
|
|
|
85
85
|
An absolute link (a public file, a snippet to copy, a mail) is built on `await GetApiBaseUrl()`
|
|
@@ -83,7 +83,10 @@ decorators (`Controller`, `Get`, `Post`, `@Parameter`, `@JSONBody`), return a pl
|
|
|
83
83
|
for 200 or throw `new HTTPResult(status, body)`. `GetModel(Model)` (from
|
|
84
84
|
`@antelopejs/interface-database-decorators`) gives `get` / `getAll` / `insert` / `update` /
|
|
85
85
|
`delete`. Routes under a controller that a page or DataController wraps **inherit** its auth —
|
|
86
|
-
already gated to logged-in callers (see **dms-auth**).
|
|
86
|
+
already gated to logged-in callers (see **dms-auth**). A route declared on a page class is
|
|
87
|
+
served under `/dms/pages`, not at the page's own path (which is the page): name it with
|
|
88
|
+
`pageRoutePath(slug, "/route")` from `@antelopejs/interface-dms/page` in a `fetchUrl` /
|
|
89
|
+
`submitUrl`. A plain `Controller` the frontend calls sits under `/api/<module>/`.
|
|
87
90
|
|
|
88
91
|
A **`TableView` is normally backed by a `DataController`**: a class
|
|
89
92
|
`extends DataController(TableClass, TableViewRoutes.All, Controller("/path"))` registered
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The former path of `usePageRecordLabel`, kept for the modules that import
|
|
3
|
-
* it from here. Import it from `#dms-core/app/composables/page/usePageRecordLabel`,
|
|
4
|
-
* or call the auto-imported `usePageRecordLabel()`.
|
|
5
|
-
*
|
|
6
|
-
* @internal
|
|
7
|
-
* @deprecated Use `#dms-core/app/composables/page/usePageRecordLabel`.
|
|
8
|
-
*/
|
|
9
|
-
export { usePageRecordLabel } from "../../../composables/page/usePageRecordLabel";
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export function usePrefetch() {
|
|
2
|
-
const siteLayout = useSiteLayout();
|
|
3
|
-
|
|
4
|
-
function prefetchPageLayout(slug: string): void {
|
|
5
|
-
const page = siteLayout.siteLayout.value?.pages?.[slug];
|
|
6
|
-
if (!page?.layoutUrl) return;
|
|
7
|
-
|
|
8
|
-
const cached = siteLayout.pageLayouts.value[page.layoutUrl];
|
|
9
|
-
if (cached) {
|
|
10
|
-
prefetchPageLayoutComponents(cached);
|
|
11
|
-
return;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
siteLayout
|
|
15
|
-
.loadPageLayout(page.layoutUrl)
|
|
16
|
-
.then((layout) => prefetchPageLayoutComponents(layout))
|
|
17
|
-
.catch(() => {});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
return { prefetchPageLayout };
|
|
21
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Former home of `useControlError`, kept importable for the modules that
|
|
3
|
-
* reached it here.
|
|
4
|
-
*
|
|
5
|
-
* @deprecated Import `useControlError` from
|
|
6
|
-
* `#dms-ui/app/composables/form/useControlError` (or rely on its auto-import).
|
|
7
|
-
* This path is removed in the next breaking release.
|
|
8
|
-
*/
|
|
9
|
-
export * from "../../../composables/form/useControlError";
|
|
10
|
-
export * from "./formControlErrors";
|
package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useServerRenderedAsyncData.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Async data a list renders from: awaited during the server render, so the
|
|
3
|
-
* markup carries the rows (and hydration reuses them), but never awaited in
|
|
4
|
-
* the browser, so a client navigation paints the list chrome and its skeleton
|
|
5
|
-
* right away instead of holding the page's Suspense until the fetch returns.
|
|
6
|
-
*
|
|
7
|
-
* Callers still `await` the result: on the server it is the fetch, in the
|
|
8
|
-
* browser an already settled value. Read the loading state from the returned
|
|
9
|
-
* `data` (`null` until the first result) rather than from `status`, which
|
|
10
|
-
* stays `idle` until the deferred fetch starts.
|
|
11
|
-
*
|
|
12
|
-
* @param key Async data key (shared with the hydration payload)
|
|
13
|
-
* @param handler Fetches the data
|
|
14
|
-
* @param options `useDmsAsyncData` options (`watch`, `transform`…)
|
|
15
|
-
*/
|
|
16
|
-
export function useServerRenderedAsyncData<T>(
|
|
17
|
-
key: string,
|
|
18
|
-
handler: () => Promise<T>,
|
|
19
|
-
options: Parameters<typeof useDmsAsyncData<T>>[2] = {},
|
|
20
|
-
): Promise<Awaited<ReturnType<typeof useDmsAsyncData<T>>>> {
|
|
21
|
-
if (import.meta.env.SSR) return useDmsAsyncData<T>(key, handler, options);
|
|
22
|
-
return Promise.resolve(useDmsLazyAsyncData<T>(key, handler, options));
|
|
23
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import * as legacy from "../layers/dms-ui/app/build/composables/form/useControlError";
|
|
3
|
-
import { useControlError } from "../layers/dms-ui/app/composables/form/useControlError";
|
|
4
|
-
import { FORM_CONTROL_ERRORS_KEY } from "../layers/dms-ui/app/build/composables/form/formControlErrors";
|
|
5
|
-
|
|
6
|
-
describe("useControlError location", () => {
|
|
7
|
-
it("stays importable from its former build/ path", () => {
|
|
8
|
-
expect(legacy.useControlError).toBe(useControlError);
|
|
9
|
-
expect(legacy.FORM_CONTROL_ERRORS_KEY).toBe(FORM_CONTROL_ERRORS_KEY);
|
|
10
|
-
});
|
|
11
|
-
});
|