@antelopejs/dms 0.9.7 → 0.10.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 +114 -0
- package/README.md +1 -1
- package/data/module-store/catalog.json +2 -2
- package/dist/attachments/table-view.js +2 -2
- package/dist/attachments/table-view.js.map +1 -1
- package/dist/automation/actions.d.ts +2 -0
- package/dist/automation/actions.js +116 -71
- package/dist/automation/actions.js.map +1 -1
- package/dist/automation/i18n.d.ts +4 -0
- package/dist/automation/i18n.js +19 -0
- package/dist/automation/i18n.js.map +1 -0
- package/dist/automation/send-notification.d.ts +21 -0
- package/dist/automation/send-notification.js +163 -0
- package/dist/automation/send-notification.js.map +1 -0
- package/dist/automation/triggers.js +19 -25
- package/dist/automation/triggers.js.map +1 -1
- package/dist/client-origin-cors.d.ts +1 -0
- package/dist/client-origin-cors.js +19 -0
- package/dist/client-origin-cors.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +5 -3
- package/dist/config.js.map +1 -1
- package/dist/frontend-module.d.ts +4 -0
- package/dist/frontend-module.js +37 -0
- package/dist/frontend-module.js.map +1 -0
- package/dist/implementations/dms/client-ip.js +1 -1
- package/dist/implementations/dms/client-ip.js.map +1 -1
- package/dist/implementations/dms/dev-reload.d.ts +5 -0
- package/dist/implementations/dms/dev-reload.js +23 -3
- package/dist/implementations/dms/dev-reload.js.map +1 -1
- package/dist/implementations/dms/frontend-modules.d.ts +2 -4
- package/dist/implementations/dms/frontend-modules.js +22 -89
- package/dist/implementations/dms/frontend-modules.js.map +1 -1
- package/dist/implementations/dms/frontend-ready.d.ts +12 -2
- package/dist/implementations/dms/frontend-ready.js +34 -4
- package/dist/implementations/dms/frontend-ready.js.map +1 -1
- package/dist/implementations/dms/layout-banners.d.ts +2 -1
- package/dist/implementations/dms/layout-banners.js +9 -0
- package/dist/implementations/dms/layout-banners.js.map +1 -1
- package/dist/implementations/dms/manifest.d.ts +1 -2
- package/dist/implementations/dms/manifest.js +2 -0
- package/dist/implementations/dms/manifest.js.map +1 -1
- package/dist/implementations/dms/page.d.ts +4 -3
- package/dist/implementations/dms/page.js +49 -18
- package/dist/implementations/dms/page.js.map +1 -1
- package/dist/implementations/dms/permissions-resolver.d.ts +2 -2
- package/dist/implementations/dms/permissions-resolver.js +2 -2
- package/dist/implementations/dms/permissions-resolver.js.map +1 -1
- package/dist/implementations/dms/realtime.js +1 -0
- package/dist/implementations/dms/realtime.js.map +1 -1
- package/dist/implementations/dms-base/bulk-selection.d.ts +1 -1
- package/dist/implementations/dms-base/bulk-selection.js +23 -1
- package/dist/implementations/dms-base/bulk-selection.js.map +1 -1
- package/dist/implementations/dms-base/filter-tokens.js +5 -3
- package/dist/implementations/dms-base/filter-tokens.js.map +1 -1
- package/dist/implementations/dms-base/search-route.d.ts +1 -0
- package/dist/implementations/dms-base/search-route.js +35 -13
- package/dist/implementations/dms-base/search-route.js.map +1 -1
- package/dist/implementations/dms-base/table-view.js +11 -6
- package/dist/implementations/dms-base/table-view.js.map +1 -1
- package/dist/implementations/dms-notifications/index.d.ts +2 -2
- package/dist/implementations/dms-notifications/index.js +5 -5
- package/dist/implementations/dms-notifications/index.js.map +1 -1
- package/dist/index.js +28 -45
- package/dist/index.js.map +1 -1
- package/dist/pages/modules/index.d.ts +1 -0
- package/dist/pages/modules/index.js +1 -0
- package/dist/pages/modules/index.js.map +1 -1
- package/dist/pages/modules/modules-store.d.ts +9 -0
- package/dist/pages/modules/modules-store.js +30 -0
- package/dist/pages/modules/modules-store.js.map +1 -0
- package/dist/pages/settings/users/account-credentials.js +2 -2
- package/dist/pages/settings/users/account-credentials.js.map +1 -1
- package/dist/pages/settings/users/account-data-store.js +25 -8
- package/dist/pages/settings/users/account-data-store.js.map +1 -1
- package/dist/pages/settings/users/invite-extension-routes.js +9 -3
- package/dist/pages/settings/users/invite-extension-routes.js.map +1 -1
- package/dist/pages/settings/users/member-management.js +13 -14
- package/dist/pages/settings/users/member-management.js.map +1 -1
- package/dist/pages/settings/users/profile.d.ts +1 -4
- package/dist/pages/settings/users/profile.js +0 -35
- package/dist/pages/settings/users/profile.js.map +1 -1
- package/dist/pages/settings/users/role-editor-store.js +26 -18
- package/dist/pages/settings/users/role-editor-store.js.map +1 -1
- package/dist/pages/settings/users/role-editor.d.ts +0 -1
- package/dist/pages/settings/users/role-editor.js +1 -2
- package/dist/pages/settings/users/role-editor.js.map +1 -1
- package/dist/pages/settings/users/role-preview.js +1 -2
- package/dist/pages/settings/users/role-preview.js.map +1 -1
- package/dist/pages/settings/users/roles.js +2 -2
- package/dist/pages/settings/users/roles.js.map +1 -1
- package/dist/pages/settings/users/two-factor-operations.js.map +1 -1
- package/dist/realtime/broker.d.ts +6 -0
- package/dist/realtime/broker.js +8 -1
- package/dist/realtime/broker.js.map +1 -1
- package/dist/realtime/presence.d.ts +8 -3
- package/dist/realtime/presence.js +8 -4
- package/dist/realtime/presence.js.map +1 -1
- package/dist/realtime/sessions.d.ts +11 -1
- package/dist/realtime/sessions.js +21 -3
- package/dist/realtime/sessions.js.map +1 -1
- package/dist/realtime/table-view.d.ts +2 -2
- package/dist/realtime/table-view.js +4 -3
- package/dist/realtime/table-view.js.map +1 -1
- package/dist/routes/auth/client-ip.d.ts +1 -0
- package/dist/routes/auth/client-ip.js +18 -0
- package/dist/routes/auth/client-ip.js.map +1 -1
- package/dist/routes/auth/oauth/config.d.ts +2 -1
- package/dist/routes/auth/oauth/config.js +5 -1
- package/dist/routes/auth/oauth/config.js.map +1 -1
- package/dist/routes/auth/session-handoff.d.ts +0 -1
- package/dist/routes/auth/session-handoff.js +0 -2
- package/dist/routes/auth/session-handoff.js.map +1 -1
- package/dist/routes/auth.js +3 -2
- package/dist/routes/auth.js.map +1 -1
- package/dist/routes/realtime.d.ts +13 -1
- package/dist/routes/realtime.js +114 -29
- package/dist/routes/realtime.js.map +1 -1
- package/dist/startup-config.d.ts +2 -0
- package/dist/startup-config.js +62 -0
- package/dist/startup-config.js.map +1 -0
- package/dist/store/interfaces.d.ts +3 -2
- package/dist/store/interfaces.js +16 -6
- package/dist/store/interfaces.js.map +1 -1
- package/dist/store/listing.js +24 -13
- package/dist/store/listing.js.map +1 -1
- package/dist/store/operations.js +4 -0
- package/dist/store/operations.js.map +1 -1
- package/dist/store/requirements.js +3 -2
- package/dist/store/requirements.js.map +1 -1
- package/dist/store/runtime.d.ts +10 -1
- package/dist/store/runtime.js +33 -6
- package/dist/store/runtime.js.map +1 -1
- package/dist/store/types.d.ts +10 -0
- package/dist/validation/auth.schema.d.ts +2 -2
- package/dist/validation/member-invite.schema.d.ts +2 -10
- package/dist/validation/member-invite.schema.js +2 -15
- package/dist/validation/member-invite.schema.js.map +1 -1
- package/frontend-vue/dms.frontend.ts +24 -5
- package/frontend-vue/dms.server.ts +17 -0
- package/frontend-vue/layers/dms-auth/server/accounts.ts +3 -4
- package/frontend-vue/layers/dms-core/app/build/composables/renderNow.ts +34 -0
- package/frontend-vue/layers/dms-core/app/build/composables/user/tablePreferences.ts +5 -1
- package/frontend-vue/layers/dms-core/app/build/utils/crossTab.ts +199 -0
- package/frontend-vue/layers/dms-core/app/build/utils/sharedRealtimeConnection.ts +410 -0
- package/frontend-vue/layers/dms-core/app/composables/auth/useApiData.ts +77 -12
- package/frontend-vue/layers/dms-core/app/composables/data-types/useDataType.ts +26 -0
- package/frontend-vue/layers/dms-core/app/composables/hydration/isHydratingComponent.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/live/livePauseSource.ts +29 -0
- package/frontend-vue/layers/dms-core/app/composables/period/compareResolvers.ts +150 -9
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriod.ts +6 -4
- package/frontend-vue/layers/dms-core/app/composables/period/usePeriodScope.ts +27 -1
- package/frontend-vue/layers/dms-core/app/composables/realtime/useUserRealtime.ts +256 -119
- package/frontend-vue/layers/dms-core/app/composables/translation/useComposedText.ts +21 -8
- package/frontend-vue/layers/dms-core/app/composables/translation/useTranslation.ts +22 -5
- package/frontend-vue/layers/dms-core/app/plugins/dev-error-log.ts +16 -0
- package/frontend-vue/layers/dms-core/app/types/user.ts +6 -0
- package/frontend-vue/layers/dms-core/app/utils/composedText.ts +7 -2
- package/frontend-vue/layers/dms-core/app/utils/formatter.ts +166 -35
- package/frontend-vue/layers/dms-core/app/utils/url-interpolation.ts +18 -1
- package/frontend-vue/layers/dms-core/i18n/locales/core-en-GB.json +134 -2
- package/frontend-vue/layers/dms-core/i18n/locales/core-fr-FR.json +134 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardBanners.vue +3 -2
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardHeader.vue +41 -21
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardPageBody.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/DashboardSidebar.vue +4 -19
- package/frontend-vue/layers/dms-layout/app/build/components/layout/Footer.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/GithubStarPrompt.vue +18 -7
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionBar.vue +8 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/PageHeaderActionsOutlet.vue +14 -0
- package/frontend-vue/layers/dms-layout/app/build/components/layout/RowSidePanel.vue +43 -82
- package/frontend-vue/layers/dms-layout/app/build/components/layout/SidebarUserMenu.vue +44 -3
- package/frontend-vue/layers/dms-layout/app/build/components/layout/UserIdentity.vue +11 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleCatalogTile.vue +132 -35
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleManageMenu.vue +59 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleSourceLabel.vue +25 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreBanners.vue +73 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreOperation.vue +116 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreRequirements.vue +8 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleStoreTile.vue +85 -207
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTile.vue +10 -3
- package/frontend-vue/layers/dms-layout/app/build/components/pages/modules/ModuleTileSkeleton.vue +68 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxDisplay.vue +24 -8
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/notification/NotificationInboxItem.vue +8 -5
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/region/RegionTimeZoneInput.vue +1 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/role-permissions.ts +19 -0
- package/frontend-vue/layers/dms-layout/app/build/components/pages/settings/roles/useRoleEditor.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/build/composables/layout/useSidePanelWidth.ts +31 -70
- package/frontend-vue/layers/dms-layout/app/build/composables/notification/useNotificationGroupsOpen.ts +11 -1
- package/frontend-vue/layers/dms-layout/app/build/utils/dev-reload-stream.ts +81 -0
- package/frontend-vue/layers/dms-layout/app/build/utils/module-store-catalog.ts +232 -36
- package/frontend-vue/layers/dms-layout/app/build/utils/modules-catalog.ts +122 -14
- package/frontend-vue/layers/dms-layout/app/composables/layout/useLayoutBanners.ts +32 -3
- package/frontend-vue/layers/dms-layout/app/composables/layout/usePageRecord.ts +50 -0
- package/frontend-vue/layers/dms-layout/app/composables/layout/useServedHeaderActions.ts +25 -7
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleHistory.ts +3 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStore.ts +51 -4
- package/frontend-vue/layers/dms-layout/app/composables/page/useModuleStoreActions.ts +194 -0
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageComponentPreload.ts +23 -3
- package/frontend-vue/layers/dms-layout/app/composables/page/usePageLayout.ts +4 -2
- package/frontend-vue/layers/dms-layout/app/composables/page/useSiteLayout.ts +6 -1
- package/frontend-vue/layers/dms-layout/app/custom-layouts/DefaultLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/custom-layouts/SettingsLayout.vue +9 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules-store.vue +291 -0
- package/frontend-vue/layers/dms-layout/app/custom-pages/modules.vue +87 -474
- package/frontend-vue/layers/dms-layout/app/middleware/module-routing.global.ts +3 -0
- package/frontend-vue/layers/dms-layout/app/pages/[...slug].vue +6 -22
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-navigation.client.ts +2 -1
- package/frontend-vue/layers/dms-layout/app/plugins/command-palette-personal-pages.client.ts +14 -44
- package/frontend-vue/layers/dms-layout/app/plugins/dms-dev-reload.client.ts +14 -9
- package/frontend-vue/layers/dms-layout/app/theme/form-controls.ts +9 -4
- package/frontend-vue/layers/dms-layout/app/theme/surfaces.ts +22 -6
- package/frontend-vue/layers/dms-layout/app/types/module-store.ts +9 -0
- package/frontend-vue/layers/dms-layout/app/types/page.ts +15 -1
- package/frontend-vue/layers/dms-layout/app/utils/account-menu.ts +11 -0
- package/frontend-vue/layers/dms-layout/app/utils/breadcrumb.ts +57 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageQuery.ts +26 -0
- package/frontend-vue/layers/dms-layout/app/utils/pageRecord.ts +71 -0
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-en-GB.json +39 -14
- package/frontend-vue/layers/dms-layout/i18n/locales/layout-fr-FR.json +38 -13
- package/frontend-vue/layers/dms-ui/app/build/components/containers/TargetComponentTree.vue +31 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/constants.ts +25 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/drawer/DynamicDrawer.vue +244 -15
- package/frontend-vue/layers/dms-ui/app/build/components/containers/modal/DynamicModal.vue +42 -25
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowHeader.vue +153 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/RowNotFound.vue +44 -0
- package/frontend-vue/layers/dms-ui/app/build/components/containers/row/drawerRowHeader.ts +35 -0
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormEntries.vue +44 -9
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormErrorSummary.vue +11 -4
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldControl.vue +38 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormFieldNotes.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSectionNav.vue +3 -1
- package/frontend-vue/layers/dms-ui/app/build/components/form/FormSections.vue +106 -27
- package/frontend-vue/layers/dms-ui/app/build/components/table/Actions.vue +12 -4
- package/frontend-vue/layers/dms-ui/app/build/components/table/Empty.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table/FiltersRow.vue +38 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Pagination.vue +55 -54
- package/frontend-vue/layers/dms-ui/app/build/components/table/RowSelection.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Table.vue +396 -288
- package/frontend-vue/layers/dms-ui/app/build/components/table/Tabs.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table/Views.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardAvatar.vue +59 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardGridSkeleton.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardRowActions.vue +38 -0
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/CardsDisplay.vue +148 -104
- package/frontend-vue/layers/dms-ui/app/build/components/table-view/LiveHoldBanner.vue +17 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/bulkSelection.ts +43 -9
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowComponentOptions.ts +76 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/rowNavigation.ts +172 -27
- package/frontend-vue/layers/dms-ui/app/build/composables/actions/useActionTargets.ts +270 -46
- package/frontend-vue/layers/dms-ui/app/build/composables/confirm/confirmDialogTexts.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/closingLayers.ts +186 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/context.ts +14 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/drawerSize.ts +229 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/containers/useResizableSize.ts +175 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/metricCells.ts +18 -35
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/numberSuffix.ts +47 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerDefaults.ts +99 -24
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/registerFieldTypes.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationFilter.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationRows.ts +71 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/relationValue.ts +39 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/twoLineCell.ts +3 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/data-types/useColumnValueRenderer.ts +6 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/form/createFormFields.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldSaved.ts +40 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/fieldValueRule.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formEntryContext.ts +5 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formFooter.ts +42 -7
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formLayout.ts +106 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/form/formSections.ts +32 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/readonlyValue.ts +19 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/registerFormFunctions.ts +27 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/repeaterTable.ts +65 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/serverFieldErrors.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/toasterClearance.ts +118 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/form/useInstantForm.ts +12 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/currentPageLive.ts +41 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/livePoller.ts +10 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/live/pageReadClock.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/live/shortcuts.ts +1 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/live/usePageLiveHost.ts +17 -3
- package/frontend-vue/layers/dms-ui/app/build/composables/live/useUpdatedLabel.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/pageToolbarContext.ts +75 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/page-toolbar/toolbarControlHeight.ts +29 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodComparison.ts +52 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodDisplay.ts +21 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/periodShortLabels.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/period/usePeriodDraft.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRelationFilterLabels.ts +73 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useRowScrollAnchor.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTable.ts +9 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/useTableColumns.ts +1 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/archivedCount.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/filterEditorValue.ts +38 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/formatFilterValue.ts +33 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table/utils/scrollEdges.ts +31 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useCardFields.ts +33 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useMobileDisplay.ts +48 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useScrolledPastTop.ts +41 -18
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableFooter.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLinkedVisit.ts +78 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableLiveHold.ts +135 -22
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableRows.ts +56 -25
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViewRowActions.ts +77 -32
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/useTableViews.ts +86 -10
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/bulkActions.ts +14 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/card.ts +127 -2
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/compareValues.ts +13 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/expandedRows.ts +28 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/groupedRows.ts +114 -1
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/linkedArrival.ts +308 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/locateRow.ts +106 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pageArrival.ts +34 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/pinnedRows.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/quickFilters.ts +41 -33
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/recordLink.ts +24 -8
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableCounts.ts +113 -0
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/tableQuery.ts +27 -4
- package/frontend-vue/layers/dms-ui/app/build/composables/table-view/utils/views.ts +22 -6
- package/frontend-vue/layers/dms-ui/app/build/utils/addressSuggestions.ts +49 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/eyebrow.ts +12 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/fieldOptionsI18n.ts +51 -11
- package/frontend-vue/layers/dms-ui/app/build/utils/inputWidth.ts +53 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/numberAdjustment.ts +6 -3
- package/frontend-vue/layers/dms-ui/app/build/utils/relativeDate.ts +19 -5
- package/frontend-vue/layers/dms-ui/app/build/utils/scrollWithin.ts +99 -0
- package/frontend-vue/layers/dms-ui/app/build/utils/urlVariables.ts +36 -14
- package/frontend-vue/layers/dms-ui/app/components/PasswordStrength.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/RecursiveComponent.vue +9 -1
- package/frontend-vue/layers/dms-ui/app/components/blocks/LiveToggleBlock.vue +80 -32
- package/frontend-vue/layers/dms-ui/app/components/blocks/PageToolbarBlock.vue +137 -0
- package/frontend-vue/layers/dms-ui/app/components/blocks/StatGroupBlock.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/chart/ApexChartHost.vue +16 -2
- package/frontend-vue/layers/dms-ui/app/components/chart/Chart.vue +24 -5
- package/frontend-vue/layers/dms-ui/app/components/chart/ChartCard.vue +41 -23
- package/frontend-vue/layers/dms-ui/app/components/confirm/ConfirmModal.vue +1 -1
- package/frontend-vue/layers/dms-ui/app/components/container-footer/ContainerFooter.vue +34 -0
- package/frontend-vue/layers/dms-ui/app/components/field-row/FieldRow.vue +25 -2
- package/frontend-vue/layers/dms-ui/app/components/flow-canvas/FlowCanvas.vue +2 -2
- package/frontend-vue/layers/dms-ui/app/components/form/Form.vue +118 -36
- package/frontend-vue/layers/dms-ui/app/components/form/components/DatePicker.vue +20 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Display.vue +13 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Image.vue +8 -3
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputAddress.vue +53 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputCode.vue +29 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputNumber.vue +45 -8
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputPercentage.vue +28 -5
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputSecret.vue +5 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputText.vue +19 -2
- package/frontend-vue/layers/dms-ui/app/components/form/components/InputTime.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/form/components/Relation.vue +205 -65
- package/frontend-vue/layers/dms-ui/app/components/form/components/Repeater.vue +80 -30
- package/frontend-vue/layers/dms-ui/app/components/form/components/RepeaterTable.vue +203 -0
- package/frontend-vue/layers/dms-ui/app/components/form/components/Select.vue +4 -2
- package/frontend-vue/layers/dms-ui/app/components/grid/GridRow.vue +11 -2
- package/frontend-vue/layers/dms-ui/app/components/key-value-list/KeyValueList.vue +79 -70
- package/frontend-vue/layers/dms-ui/app/components/kpi/KpiCard.vue +117 -21
- package/frontend-vue/layers/dms-ui/app/components/meter/Meter.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/period/PeriodSelector.vue +113 -34
- package/frontend-vue/layers/dms-ui/app/components/save-bar/SaveBar.vue +14 -2
- package/frontend-vue/layers/dms-ui/app/components/section-header/Eyebrow.vue +6 -1
- package/frontend-vue/layers/dms-ui/app/components/segmented/Segmented.vue +13 -0
- package/frontend-vue/layers/dms-ui/app/components/stat-group/StatGroup.vue +52 -3
- package/frontend-vue/layers/dms-ui/app/components/tab/Tab.vue +2 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanBoard.vue +45 -35
- package/frontend-vue/layers/dms-ui/app/components/table-view/KanbanDisplay.vue +4 -1
- package/frontend-vue/layers/dms-ui/app/components/table-view/TableView.vue +410 -96
- package/frontend-vue/layers/dms-ui/app/components/top-list/TopListCard.vue +33 -5
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexCategoryLabels.ts +68 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/apexDatetimeBuckets.ts +306 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/formatValue.ts +4 -3
- package/frontend-vue/layers/dms-ui/app/composables/chart/heatmapColors.ts +93 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/localizeSeries.ts +62 -0
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.ts +116 -8
- package/frontend-vue/layers/dms-ui/app/composables/chart/useApexChart.types.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/composables/containers/types.ts +84 -1
- package/frontend-vue/layers/dms-ui/app/composables/containers/useDrawer.ts +69 -8
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field-saved.ts +11 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/field.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/types/functions.ts +1 -0
- package/frontend-vue/layers/dms-ui/app/composables/form/useForm.ts +142 -21
- package/frontend-vue/layers/dms-ui/app/composables/live/usePageLive.ts +11 -4
- package/frontend-vue/layers/dms-ui/app/composables/page-toolbar/usePageToolbarSize.ts +30 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/kanban.ts +4 -2
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/action-target.ts +22 -3
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/config.ts +4 -0
- package/frontend-vue/layers/dms-ui/app/composables/table-view/types/display.ts +24 -1
- package/frontend-vue/layers/dms-ui/app/plugins/closing-layers.client.ts +10 -0
- package/frontend-vue/layers/dms-ui/app/plugins/live-pause-source.ts +15 -0
- package/frontend-vue/layers/dms-ui/app/plugins/register.ts +2 -0
- package/frontend-vue/layers/dms-ui/app/types/input-number.ts +3 -2
- package/frontend-vue/layers/dms-ui/app/types/page-toolbar.ts +7 -0
- package/frontend-vue/layers/dms-ui/app/types/row-action.ts +0 -2
- package/frontend-vue/layers/dms-ui/app/types/row-navigation.ts +17 -0
- package/frontend-vue/layers/dms-ui/app/types/table/query.ts +2 -1
- package/frontend-vue/layers/dms-ui/app/utils/blockRefresh.ts +3 -1
- package/frontend-vue/layers/dms-ui/app/utils/componentTree.ts +24 -0
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-en-GB.json +16 -4
- package/frontend-vue/layers/dms-ui/i18n/locales/ui-fr-FR.json +16 -4
- package/frontend-vue/package.json +2 -2
- package/package.json +7 -6
- package/skills/dms/SKILL.md +1 -1
- package/skills/dms-auth/SKILL.md +5 -3
- package/skills/dms-best-practices/REFERENCE.md +2 -0
- package/skills/dms-best-practices/SKILL.md +4 -0
- package/skills/dms-module/SKILL.md +3 -2
- package/skills/dms-pages/REFERENCE.md +125 -16
- package/skills/dms-pages/SKILL.md +1 -1
- package/skills/dms-project/DEV-SERVERS.md +23 -1
- package/skills/dms-project/REFERENCE.md +1 -1
- package/skills/dms-project/SKILL.md +4 -1
- package/skills/dms-project/scripts/dms-dev.sh +205 -17
- package/frontend-vue/layers/dms-core/app/composables/period/periodRenderNow.ts +0 -42
|
@@ -7,20 +7,7 @@ const zod_1 = require("zod");
|
|
|
7
7
|
exports.INVITE_NAME_MAX_LENGTH = 100;
|
|
8
8
|
exports.INVITE_FULL_NAME_MAX_LENGTH = exports.INVITE_NAME_MAX_LENGTH * 2;
|
|
9
9
|
exports.INVITE_EMAILS_MAX = 50;
|
|
10
|
-
const LEGACY_EMAIL_KEY = "email";
|
|
11
|
-
const EMAILS_KEY = "emails";
|
|
12
10
|
const NAME_SEPARATOR = /\s+/;
|
|
13
|
-
function acceptLegacySingleEmail(payload) {
|
|
14
|
-
if (typeof payload !== "object" || payload === null)
|
|
15
|
-
return payload;
|
|
16
|
-
const record = payload;
|
|
17
|
-
if (record[EMAILS_KEY] !== undefined)
|
|
18
|
-
return payload;
|
|
19
|
-
const email = record[LEGACY_EMAIL_KEY];
|
|
20
|
-
if (typeof email !== "string")
|
|
21
|
-
return payload;
|
|
22
|
-
return { ...record, [EMAILS_KEY]: [email] };
|
|
23
|
-
}
|
|
24
11
|
const inviteNamePart = zod_1.z.string().trim().max(exports.INVITE_NAME_MAX_LENGTH);
|
|
25
12
|
const memberInviteObjectSchema = zod_1.z.object({
|
|
26
13
|
emails: zod_1.z
|
|
@@ -34,10 +21,10 @@ const memberInviteObjectSchema = zod_1.z.object({
|
|
|
34
21
|
language: zod_1.z.string(),
|
|
35
22
|
asTenantOwner: zod_1.z.boolean(),
|
|
36
23
|
});
|
|
37
|
-
exports.memberInviteSchema =
|
|
24
|
+
exports.memberInviteSchema = memberInviteObjectSchema.refine((data) => data.asTenantOwner || (data.roles && data.roles.length > 0), {
|
|
38
25
|
message: "$page.settings.members.invite.roles_required",
|
|
39
26
|
path: ["roles"],
|
|
40
|
-
})
|
|
27
|
+
});
|
|
41
28
|
function resolveInviteeNameParts(payload) {
|
|
42
29
|
if (payload.firstname || payload.lastname || !payload.name) {
|
|
43
30
|
return { firstname: payload.firstname, lastname: payload.lastname };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"member-invite.schema.js","sourceRoot":"","sources":["../../src/validation/member-invite.schema.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"member-invite.schema.js","sourceRoot":"","sources":["../../src/validation/member-invite.schema.ts"],"names":[],"mappings":";;;AA6DA,0DAQC;AAMD,gDAEC;AA7ED,6BAAwB;AAOX,QAAA,sBAAsB,GAAG,GAAG,CAAC;AAG7B,QAAA,2BAA2B,GAAG,8BAAsB,GAAG,CAAC,CAAC;AAMzD,QAAA,iBAAiB,GAAG,EAAE,CAAC;AAEpC,MAAM,cAAc,GAAG,KAAK,CAAC;AAO7B,MAAM,cAAc,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,8BAAsB,CAAC,CAAC;AAErE,MAAM,wBAAwB,GAAG,OAAC,CAAC,MAAM,CAAC;IACxC,MAAM,EAAE,OAAC;SACN,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,EAAE,CAAC;SAC9C,GAAG,CAAC,CAAC,EAAE,+CAA+C,CAAC;SACvD,GAAG,CAAC,yBAAiB,EAAE,+CAA+C,CAAC;IAC1E,IAAI,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,mCAA2B,CAAC,CAAC,QAAQ,EAAE;IACnE,SAAS,EAAE,cAAc,CAAC,QAAQ,EAAE;IACpC,QAAQ,EAAE,cAAc,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,OAAC,CAAC,KAAK,CAAC,OAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACrC,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE;IACpB,aAAa,EAAE,OAAC,CAAC,OAAO,EAAE;CAC3B,CAAC,CAAC;AAEU,QAAA,kBAAkB,GAAG,wBAAwB,CAAC,MAAM,CAC/D,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,aAAa,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,EACrE;IACE,OAAO,EAAE,8CAA8C;IACvD,IAAI,EAAE,CAAC,OAAO,CAAC;CAChB,CACF,CAAC;AAeF,SAAgB,uBAAuB,CACrC,OAAqE;IAErE,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAC3D,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC;IACtE,CAAC;IACD,MAAM,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAChE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,EAAE,CAAC;AAC9D,CAAC;AAMD,SAAgB,kBAAkB,CAAC,MAAyB;IAC1D,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAC9B,CAAC"}
|
|
@@ -18,8 +18,10 @@ import permissionPreviewPrepaintPlugin from "./layers/dms-layout/app/plugins/per
|
|
|
18
18
|
import regionalPreferencesPlugin from "./layers/dms-layout/app/plugins/regional-preferences";
|
|
19
19
|
import seoPlugin from "./layers/dms-layout/app/plugins/seo";
|
|
20
20
|
import sidePanelHostPlugin from "./layers/dms-layout/app/plugins/side-panel-host";
|
|
21
|
+
import livePauseSourcePlugin from "./layers/dms-ui/app/plugins/live-pause-source";
|
|
21
22
|
import onboardingMiddleware from "./layers/dms-onboarding/app/middleware/onboarding.global";
|
|
22
23
|
import onboardingPreloadPlugin from "./layers/dms-onboarding/app/plugins/onboarding-preload";
|
|
24
|
+
import closingLayersPlugin from "./layers/dms-ui/app/plugins/closing-layers.client";
|
|
23
25
|
import registerPlugin from "./layers/dms-ui/app/plugins/register";
|
|
24
26
|
import shortcutsPlugin from "./layers/dms-ui/app/plugins/shortcuts";
|
|
25
27
|
import tableViewDisplaysPlugin from "./layers/dms-ui/app/plugins/table-view-displays";
|
|
@@ -92,8 +94,11 @@ function lazyComponent(loader: VueLoader): Component {
|
|
|
92
94
|
return defineAsyncComponent(async () => (await loader()).default);
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
//
|
|
96
|
-
//
|
|
97
|
+
// Not a compatibility path: two current contracts name these components.
|
|
98
|
+
// Templates use the component's own name (`<DmsSection>`, `<DmsFieldRow>`,
|
|
99
|
+
// `<DmsActivityFeed>`), while the backend blocks follow the `dms-xxx-block`
|
|
100
|
+
// vocabulary that `block-vocabulary.test.ts` enforces. Each entry maps the
|
|
101
|
+
// template name to the block name, so both resolve to one component.
|
|
97
102
|
const COMPONENT_ALIASES: Record<string, string> = {
|
|
98
103
|
ActivityFeed: "ActivityFeedBlock",
|
|
99
104
|
Section: "SectionBlock",
|
|
@@ -159,11 +164,21 @@ function registerPlugins(sdk: Parameters<DmsFrontendModule["setup"]>[0]): void {
|
|
|
159
164
|
const clientOnly = { clientOnly: true };
|
|
160
165
|
if (import.meta.env.DEV) {
|
|
161
166
|
sdk.registerPlugin(async (context) => {
|
|
162
|
-
const plugin =
|
|
163
|
-
"./layers/dms-layout/app/plugins/dms-dev-reload.client"
|
|
164
|
-
);
|
|
167
|
+
const plugin =
|
|
168
|
+
await import("./layers/dms-layout/app/plugins/dms-dev-reload.client");
|
|
165
169
|
await plugin.default(context);
|
|
166
170
|
}, clientOnly);
|
|
171
|
+
// Browser only: the server already logs a render that fails, with its
|
|
172
|
+
// stack. First, so an error a later plugin's work raises is logged too.
|
|
173
|
+
sdk.registerPlugin(
|
|
174
|
+
async (context) => {
|
|
175
|
+
const plugin = await import(
|
|
176
|
+
"./layers/dms-core/app/plugins/dev-error-log"
|
|
177
|
+
);
|
|
178
|
+
await plugin.default(context);
|
|
179
|
+
},
|
|
180
|
+
{ ...clientOnly, enforce: "pre" },
|
|
181
|
+
);
|
|
167
182
|
}
|
|
168
183
|
// Before any data is read, a higher-priority module's plugins included:
|
|
169
184
|
// every key lives in the session's scope, and a new scope aborts the reads
|
|
@@ -183,6 +198,10 @@ function registerPlugins(sdk: Parameters<DmsFrontendModule["setup"]>[0]): void {
|
|
|
183
198
|
sdk.registerPlugin(seoPlugin);
|
|
184
199
|
sdk.registerPlugin(faviconPlugin);
|
|
185
200
|
sdk.registerPlugin(sidePanelHostPlugin);
|
|
201
|
+
// Not deferred: a page's first poll reads it.
|
|
202
|
+
sdk.registerPlugin(livePauseSourcePlugin, clientOnly);
|
|
203
|
+
// Not deferred: the first dialog the user closes needs it.
|
|
204
|
+
sdk.registerPlugin(closingLayersPlugin, clientOnly);
|
|
186
205
|
sdk.registerPlugin(registerPlugin);
|
|
187
206
|
sdk.registerPlugin(shortcutsPlugin);
|
|
188
207
|
sdk.registerPlugin(tableViewDisplaysPlugin);
|
|
@@ -8,11 +8,28 @@ import { renderHtml } from "./layers/dms-layout/server/html-render";
|
|
|
8
8
|
// provider sign-in routes, the session logic the server runs outside them,
|
|
9
9
|
// the e-mail render the backend asks for, and the backend prefixes the
|
|
10
10
|
// browser reaches through it. The renderer proxies no path on its own.
|
|
11
|
+
|
|
12
|
+
// The largest upload the DMS stack signs: Media's limit, above the DMS's own
|
|
13
|
+
// `uploads.maxSize` default (100 MiB).
|
|
14
|
+
const STORAGE_UPLOAD_MAX_BYTES = 500 * 1024 * 1024;
|
|
15
|
+
// Named rather than inline so the renderer this repository builds against,
|
|
16
|
+
// which predates `maxBodyBytes`, type-checks it.
|
|
17
|
+
const STORAGE_UPLOADS = {
|
|
18
|
+
auth: "none",
|
|
19
|
+
maxBodyBytes: STORAGE_UPLOAD_MAX_BYTES,
|
|
20
|
+
} as const;
|
|
21
|
+
|
|
11
22
|
export default defineDmsServer((server) => {
|
|
12
23
|
// The backend's API and the DMS's own routes (menu, page, page layouts,
|
|
13
24
|
// permissions, realtime), with the session's token added server-side.
|
|
14
25
|
server.registerBackendProxy("/api/**");
|
|
15
26
|
server.registerBackendProxy("/dms/**");
|
|
27
|
+
// The storage's signed URLs (file fields, exports, profile photos, media),
|
|
28
|
+
// authenticated by the token they carry, never by the member's session, so
|
|
29
|
+
// a backend reachable only through the frontend still takes uploads and
|
|
30
|
+
// serves files.
|
|
31
|
+
server.registerBackendProxy("/file-storage/upload/**", STORAGE_UPLOADS);
|
|
32
|
+
server.registerBackendProxy("/file-storage/files/**", { auth: "none" });
|
|
16
33
|
|
|
17
34
|
server.registerRoute("GET", "/api/_auth/session", session.readSessionRoute);
|
|
18
35
|
server.registerRoute(
|
|
@@ -38,12 +38,11 @@ interface SessionUser {
|
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
|
-
* Whether a session, or a stored account, outlives the browser
|
|
42
|
-
* that
|
|
43
|
-
* the flag existed carries none, and keeps the lifetime it was given then.
|
|
41
|
+
* Whether a session, or a stored account, outlives the browser: only one a
|
|
42
|
+
* login that ticked "keep me signed in" wrote (`persistent: true`).
|
|
44
43
|
*/
|
|
45
44
|
export function isPersistent(session?: { persistent?: unknown }): boolean {
|
|
46
|
-
return session?.persistent
|
|
45
|
+
return session?.persistent === true;
|
|
47
46
|
}
|
|
48
47
|
|
|
49
48
|
function lifetime(persistent: boolean): number | undefined {
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { getCurrentInstance } from "vue";
|
|
2
|
+
import { isHydratingComponent } from "../../composables/hydration/isHydratingComponent";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The instant a server render reads the clock at, handed to the browser with
|
|
6
|
+
* the page: the periods' bounds and the relative dates read it.
|
|
7
|
+
*/
|
|
8
|
+
const RENDER_NOW_STATE_KEY = "dms-render-now";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The instant, in milliseconds, a component reads the clock at while its
|
|
12
|
+
* text must match the server's, or `null` for the clock's.
|
|
13
|
+
*
|
|
14
|
+
* A server render reads every such clock of the request at one instant, which
|
|
15
|
+
* it transfers; the browser hydrating that render reads the same one, so a
|
|
16
|
+
* text measured from now ("3 min ago", a relative period's bounds) hydrates
|
|
17
|
+
* as the server wrote it, even across a minute or midnight. Anywhere else (a
|
|
18
|
+
* client navigation, a later render, a call outside a component) it is
|
|
19
|
+
* `null`: the text follows the clock.
|
|
20
|
+
*/
|
|
21
|
+
export function serverRenderNow(): number | null {
|
|
22
|
+
if (!getCurrentInstance()) return null;
|
|
23
|
+
if (typeof window === "undefined") {
|
|
24
|
+
return useDmsState<number>(RENDER_NOW_STATE_KEY, () => Date.now(), {
|
|
25
|
+
transfer: true,
|
|
26
|
+
}).value;
|
|
27
|
+
}
|
|
28
|
+
if (!isHydratingComponent()) return null;
|
|
29
|
+
const transferred = useDmsState<number | null>(
|
|
30
|
+
RENDER_NOW_STATE_KEY,
|
|
31
|
+
() => null,
|
|
32
|
+
).value;
|
|
33
|
+
return typeof transferred === "number" ? transferred : null;
|
|
34
|
+
}
|
|
@@ -13,7 +13,9 @@ export type ServerTableField =
|
|
|
13
13
|
| "paginationScope"
|
|
14
14
|
| "activeTab"
|
|
15
15
|
| "viewMode"
|
|
16
|
-
| "activeView"
|
|
16
|
+
| "activeView"
|
|
17
|
+
| "linkScope"
|
|
18
|
+
| "shownLink";
|
|
17
19
|
|
|
18
20
|
/**
|
|
19
21
|
* @internal
|
|
@@ -224,6 +226,8 @@ export const SERVER_TABLE_FIELDS: Record<ServerTableField, ServerFieldCodec> = {
|
|
|
224
226
|
activeTab: plainField("t"),
|
|
225
227
|
viewMode: plainField("d"),
|
|
226
228
|
activeView: plainField("w"),
|
|
229
|
+
linkScope: plainField("l"),
|
|
230
|
+
shownLink: plainField("k"),
|
|
227
231
|
};
|
|
228
232
|
|
|
229
233
|
/** @internal Whether `field` is kept in the cookie. */
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coordination between the tabs of one browser profile, for the long-lived
|
|
3
|
+
* streams a dashboard keeps open.
|
|
4
|
+
*
|
|
5
|
+
* Over HTTP/1.1 a browser opens at most six connections per host, and a
|
|
6
|
+
* streaming response holds one for as long as it lives: one stream per tab
|
|
7
|
+
* starves every other request once a few tabs are open. One tab is elected
|
|
8
|
+
* (a Web Lock, released by the browser when the tab goes, however it goes),
|
|
9
|
+
* opens the stream, and relays it to the others over a BroadcastChannel.
|
|
10
|
+
*
|
|
11
|
+
* Web Locks need a secure context (HTTPS, or localhost). Where either API is
|
|
12
|
+
* missing, every tab is its own leader on a bus of its own: the behaviour of a
|
|
13
|
+
* stream per tab, unchanged.
|
|
14
|
+
*
|
|
15
|
+
* @internal
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const TAB_LOCK_SEPARATOR = ":tab:";
|
|
19
|
+
|
|
20
|
+
export type TabMessageHandler = (message: unknown) => void;
|
|
21
|
+
|
|
22
|
+
/** The browser APIs the coordination rides on; injected by the tests. */
|
|
23
|
+
export interface CrossTabEnvironment {
|
|
24
|
+
locks?: LockManager;
|
|
25
|
+
createChannel?: (name: string) => BroadcastChannel;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TabBus {
|
|
29
|
+
/** Sends to every tab on the bus, this one included. */
|
|
30
|
+
post(message: unknown): void;
|
|
31
|
+
listen(handler: TabMessageHandler): () => void;
|
|
32
|
+
close(): void;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A lock this tab holds until it releases it or goes. */
|
|
36
|
+
export interface HeldLock {
|
|
37
|
+
/** Resolves once the lock is held: until then, nobody can wait on it. */
|
|
38
|
+
held: Promise<void>;
|
|
39
|
+
release(): void;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** What the running browser offers. */
|
|
43
|
+
export function browserCrossTabEnvironment(): CrossTabEnvironment {
|
|
44
|
+
const locks = globalThis.navigator?.locks;
|
|
45
|
+
const canBroadcast = typeof globalThis.BroadcastChannel === "function";
|
|
46
|
+
if (!locks || !canBroadcast) return {};
|
|
47
|
+
return { locks, createChannel: (name) => new BroadcastChannel(name) };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Whether the tabs can share: without both APIs each tab stays on its own. */
|
|
51
|
+
export function canShareAcrossTabs(environment: CrossTabEnvironment): boolean {
|
|
52
|
+
return !!environment.locks && !!environment.createChannel;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function openTabBus(
|
|
56
|
+
name: string,
|
|
57
|
+
environment: CrossTabEnvironment,
|
|
58
|
+
): TabBus {
|
|
59
|
+
const listeners = new Set<TabMessageHandler>();
|
|
60
|
+
const deliver = (message: unknown) => {
|
|
61
|
+
for (const listener of [...listeners]) listener(message);
|
|
62
|
+
};
|
|
63
|
+
const channel = canShareAcrossTabs(environment)
|
|
64
|
+
? environment.createChannel!(name)
|
|
65
|
+
: undefined;
|
|
66
|
+
if (channel) channel.onmessage = (event) => deliver(event.data);
|
|
67
|
+
return {
|
|
68
|
+
// A BroadcastChannel never echoes to its sender: this tab's own
|
|
69
|
+
// listeners hear the message directly.
|
|
70
|
+
post: (message) => {
|
|
71
|
+
channel?.postMessage(message);
|
|
72
|
+
deliver(message);
|
|
73
|
+
},
|
|
74
|
+
listen: (handler) => {
|
|
75
|
+
listeners.add(handler);
|
|
76
|
+
return () => listeners.delete(handler);
|
|
77
|
+
},
|
|
78
|
+
close: () => {
|
|
79
|
+
listeners.clear();
|
|
80
|
+
channel?.close();
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Runs `lead` in one tab at a time per `name`. When the leading tab resigns
|
|
87
|
+
* or goes, the next waiting tab takes over and runs its own `lead`.
|
|
88
|
+
*
|
|
89
|
+
* @param lead Starts the leader's work; returns what stops it
|
|
90
|
+
* @returns Resigns: stops the work when leading, or stops waiting
|
|
91
|
+
*/
|
|
92
|
+
export function electLeader(
|
|
93
|
+
name: string,
|
|
94
|
+
environment: CrossTabEnvironment,
|
|
95
|
+
lead: () => () => void,
|
|
96
|
+
): () => void {
|
|
97
|
+
if (!canShareAcrossTabs(environment)) return lead();
|
|
98
|
+
const abort = new AbortController();
|
|
99
|
+
let isResigned = false;
|
|
100
|
+
let stepDown: (() => void) | undefined;
|
|
101
|
+
environment
|
|
102
|
+
.locks!.request(name, { signal: abort.signal }, () => {
|
|
103
|
+
// Granted while the resignation was on its way.
|
|
104
|
+
if (isResigned) return;
|
|
105
|
+
return new Promise<void>((resolve) => {
|
|
106
|
+
const stop = lead();
|
|
107
|
+
stepDown = () => {
|
|
108
|
+
stop();
|
|
109
|
+
resolve();
|
|
110
|
+
};
|
|
111
|
+
});
|
|
112
|
+
})
|
|
113
|
+
.catch(() => {});
|
|
114
|
+
return () => {
|
|
115
|
+
isResigned = true;
|
|
116
|
+
abort.abort();
|
|
117
|
+
stepDown?.();
|
|
118
|
+
stepDown = undefined;
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function tabLockName(name: string, tabId: string): string {
|
|
123
|
+
return `${name}${TAB_LOCK_SEPARATOR}${tabId}`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Holds `name` until released; the browser releases it when the tab goes. */
|
|
127
|
+
export function holdLock(
|
|
128
|
+
name: string,
|
|
129
|
+
environment: CrossTabEnvironment,
|
|
130
|
+
): HeldLock {
|
|
131
|
+
if (!canShareAcrossTabs(environment)) {
|
|
132
|
+
return { held: Promise.resolve(), release: () => {} };
|
|
133
|
+
}
|
|
134
|
+
const abort = new AbortController();
|
|
135
|
+
let release: (() => void) | undefined;
|
|
136
|
+
let isReleased = false;
|
|
137
|
+
const held = new Promise<void>((resolveHeld) => {
|
|
138
|
+
environment
|
|
139
|
+
.locks!.request(name, { signal: abort.signal }, () => {
|
|
140
|
+
if (isReleased) return;
|
|
141
|
+
resolveHeld();
|
|
142
|
+
return new Promise<void>((resolve) => {
|
|
143
|
+
release = resolve;
|
|
144
|
+
});
|
|
145
|
+
})
|
|
146
|
+
.catch(() => {});
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
held,
|
|
150
|
+
release: () => {
|
|
151
|
+
isReleased = true;
|
|
152
|
+
abort.abort();
|
|
153
|
+
release?.();
|
|
154
|
+
release = undefined;
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Calls `onReleased` once nobody holds `name` any more: when the tab holding
|
|
161
|
+
* it releases it or goes. Never called once `signal` aborts.
|
|
162
|
+
*/
|
|
163
|
+
export function whenReleased(
|
|
164
|
+
name: string,
|
|
165
|
+
environment: CrossTabEnvironment,
|
|
166
|
+
onReleased: () => void,
|
|
167
|
+
signal: AbortSignal,
|
|
168
|
+
): void {
|
|
169
|
+
if (!canShareAcrossTabs(environment)) return;
|
|
170
|
+
environment
|
|
171
|
+
.locks!.request(name, { signal }, () => {
|
|
172
|
+
if (!signal.aborted) onReleased();
|
|
173
|
+
})
|
|
174
|
+
.catch(() => {});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* One source for every tab of the browser: the elected tab opens it, and what
|
|
179
|
+
* it emits reaches each tab's `onMessage`, the leader's included.
|
|
180
|
+
*
|
|
181
|
+
* @param open Opens the source, handing it what relays a message; returns
|
|
182
|
+
* what closes it
|
|
183
|
+
* @returns Leaves: closes the source when leading, and stops listening
|
|
184
|
+
*/
|
|
185
|
+
export function shareAcrossTabs(
|
|
186
|
+
name: string,
|
|
187
|
+
open: (emit: (message: unknown) => void) => () => void,
|
|
188
|
+
onMessage: TabMessageHandler,
|
|
189
|
+
environment: CrossTabEnvironment = browserCrossTabEnvironment(),
|
|
190
|
+
): () => void {
|
|
191
|
+
const bus = openTabBus(name, environment);
|
|
192
|
+
const unlisten = bus.listen(onMessage);
|
|
193
|
+
const resign = electLeader(name, environment, () => open(bus.post));
|
|
194
|
+
return () => {
|
|
195
|
+
resign();
|
|
196
|
+
unlisten();
|
|
197
|
+
bus.close();
|
|
198
|
+
};
|
|
199
|
+
}
|