@open-mercato/core 0.7.0 → 0.7.1-develop.7102.1.b41f7e3e51
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/.turbo/turbo-build.log +1 -1
- package/dist/helpers/integration/api.js +13 -1
- package/dist/helpers/integration/api.js.map +2 -2
- package/dist/helpers/integration/communicationChannelsFixtures.js +19 -0
- package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
- package/dist/modules/api_keys/api/keys/route.js +1 -1
- package/dist/modules/api_keys/api/keys/route.js.map +2 -2
- package/dist/modules/api_keys/backend/api-keys/page.js +3 -1
- package/dist/modules/api_keys/backend/api-keys/page.js.map +2 -2
- package/dist/modules/api_keys/di.js +9 -0
- package/dist/modules/api_keys/di.js.map +7 -0
- package/dist/modules/api_keys/services/principalService.js +31 -0
- package/dist/modules/api_keys/services/principalService.js.map +7 -0
- package/dist/modules/attachments/api/route.js +2 -2
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentLibrary.js +2 -0
- package/dist/modules/attachments/components/AttachmentLibrary.js.map +2 -2
- package/dist/modules/attachments/di.js +11 -1
- package/dist/modules/attachments/di.js.map +2 -2
- package/dist/modules/attachments/index.js.map +1 -1
- package/dist/modules/attachments/lib/attachment-service.js +313 -0
- package/dist/modules/attachments/lib/attachment-service.js.map +7 -0
- package/dist/modules/attachments/lib/scoped-upload-service.js +15 -2
- package/dist/modules/attachments/lib/scoped-upload-service.js.map +2 -2
- package/dist/modules/attachments/lib/upload-limits.js +6 -2
- package/dist/modules/attachments/lib/upload-limits.js.map +2 -2
- package/dist/modules/audit_logs/backend/audit-logs/page.js +6 -0
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/audit_logs/lib/display-helpers.js +19 -11
- package/dist/modules/audit_logs/lib/display-helpers.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +57 -12
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/acl.js +30 -5
- package/dist/modules/auth/acl.js.map +2 -2
- package/dist/modules/auth/api/feature-check.js +8 -2
- package/dist/modules/auth/api/feature-check.js.map +2 -2
- package/dist/modules/auth/api/login.js +4 -1
- package/dist/modules/auth/api/login.js.map +2 -2
- package/dist/modules/auth/api/reset/validate.js +55 -0
- package/dist/modules/auth/api/reset/validate.js.map +7 -0
- package/dist/modules/auth/api/roles/acl/route.js +28 -33
- package/dist/modules/auth/api/roles/acl/route.js.map +2 -2
- package/dist/modules/auth/api/session/refresh.js +25 -1
- package/dist/modules/auth/api/session/refresh.js.map +2 -2
- package/dist/modules/auth/api/users/acl/route.js +96 -51
- package/dist/modules/auth/api/users/acl/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +13 -5
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js +5 -1
- package/dist/modules/auth/backend/roles/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/roles/page.js +3 -1
- package/dist/modules/auth/backend/roles/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js +5 -1
- package/dist/modules/auth/backend/users/[id]/edit/page.meta.js.map +2 -2
- package/dist/modules/auth/backend/users/page.js +2 -1
- package/dist/modules/auth/backend/users/page.js.map +2 -2
- package/dist/modules/auth/commands/acl.js +254 -0
- package/dist/modules/auth/commands/acl.js.map +7 -0
- package/dist/modules/auth/components/AclEditor.js +39 -30
- package/dist/modules/auth/components/AclEditor.js.map +2 -2
- package/dist/modules/auth/data/entities.js.map +2 -2
- package/dist/modules/auth/data/validators.js +5 -1
- package/dist/modules/auth/data/validators.js.map +2 -2
- package/dist/modules/auth/di.js +35 -9
- package/dist/modules/auth/di.js.map +2 -2
- package/dist/modules/auth/frontend/reset/[token]/page.js +46 -0
- package/dist/modules/auth/frontend/reset/[token]/page.js.map +2 -2
- package/dist/modules/auth/lib/grantChecks.js.map +2 -2
- package/dist/modules/auth/lib/userIdFilter.js +16 -0
- package/dist/modules/auth/lib/userIdFilter.js.map +7 -0
- package/dist/modules/auth/services/authService.js +16 -2
- package/dist/modules/auth/services/authService.js.map +2 -2
- package/dist/modules/auth/services/principalService.js +200 -0
- package/dist/modules/auth/services/principalService.js.map +7 -0
- package/dist/modules/auth/services/rbacService.js +67 -24
- package/dist/modules/auth/services/rbacService.js.map +3 -3
- package/dist/modules/auth/services/roleOrganizationScope.js +41 -0
- package/dist/modules/auth/services/roleOrganizationScope.js.map +7 -0
- package/dist/modules/business_rules/backend/logs/page.js +3 -1
- package/dist/modules/business_rules/backend/logs/page.js.map +2 -2
- package/dist/modules/business_rules/backend/rules/page.js +3 -1
- package/dist/modules/business_rules/backend/rules/page.js.map +2 -2
- package/dist/modules/business_rules/backend/sets/page.js +3 -1
- package/dist/modules/business_rules/backend/sets/page.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +9 -9
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-evaluator.js +10 -9
- package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
- package/dist/modules/catalog/ai-agents.js +1 -1
- package/dist/modules/catalog/ai-agents.js.map +1 -1
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +2 -2
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +5 -2
- package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +2 -2
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js +2 -0
- package/dist/modules/catalog/components/categories/CategoriesDataTable.js.map +2 -2
- package/dist/modules/catalog/components/products/ProductsDataTable.js +7 -1
- package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +2 -0
- package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js +9 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/test-send/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/test-seed/route.js +95 -10
- package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js +3 -0
- package/dist/modules/communication_channels/backend/communication_channels/channels/page.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +14 -1
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/di.js +7 -1
- package/dist/modules/communication_channels/di.js.map +2 -2
- package/dist/modules/communication_channels/lib/email-capabilities.js +4 -1
- package/dist/modules/communication_channels/lib/email-capabilities.js.map +2 -2
- package/dist/modules/communication_channels/lib/outbound-recipient.js +33 -0
- package/dist/modules/communication_channels/lib/outbound-recipient.js.map +7 -0
- package/dist/modules/communication_channels/lib/resolve-channel-type.js +51 -0
- package/dist/modules/communication_channels/lib/resolve-channel-type.js.map +7 -0
- package/dist/modules/communication_channels/lib/test-seed.js +41 -2
- package/dist/modules/communication_channels/lib/test-seed.js.map +2 -2
- package/dist/modules/configs/lib/system-status.js +9 -0
- package/dist/modules/configs/lib/system-status.js.map +2 -2
- package/dist/modules/currencies/backend/currencies/page.js +3 -1
- package/dist/modules/currencies/backend/currencies/page.js.map +2 -2
- package/dist/modules/currencies/backend/exchange-rates/page.js +3 -1
- package/dist/modules/currencies/backend/exchange-rates/page.js.map +2 -2
- package/dist/modules/currencies/services/rateFetchingService.js +6 -1
- package/dist/modules/currencies/services/rateFetchingService.js.map +2 -2
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js +9 -0
- package/dist/modules/customer_accounts/api/admin/domain-mappings.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/roles/page.js.map +2 -2
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js +3 -1
- package/dist/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.js.map +2 -2
- package/dist/modules/customer_accounts/services/domainMappingService.js +10 -0
- package/dist/modules/customer_accounts/services/domainMappingService.js.map +2 -2
- package/dist/modules/customers/ai-tools/companies-pack.js +4 -1
- package/dist/modules/customers/ai-tools/companies-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js +13 -2
- package/dist/modules/customers/ai-tools/deal-analyzer-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/deals-pack.js +52 -6
- package/dist/modules/customers/ai-tools/deals-pack.js.map +2 -2
- package/dist/modules/customers/ai-tools/people-pack.js +4 -1
- package/dist/modules/customers/ai-tools/people-pack.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +48 -6
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +20 -64
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +17 -94
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +8 -5
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +1 -1
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/route.js +8 -25
- package/dist/modules/customers/api/deals/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/stats/route.js +3 -22
- package/dist/modules/customers/api/deals/[id]/stats/route.js.map +2 -2
- package/dist/modules/customers/api/deals/aggregate/route.js +11 -4
- package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
- package/dist/modules/customers/api/deals/map/route.js +2 -1
- package/dist/modules/customers/api/deals/map/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +38 -1
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +4 -4
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +3 -1
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +27 -4
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +4 -3
- package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/context.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/context.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +1 -1
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +11 -10
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/check-phone/route.js +51 -9
- package/dist/modules/customers/api/people/check-phone/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +30 -5
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies/page.js +4 -2
- package/dist/modules/customers/backend/customers/companies/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/page.js +6 -1
- package/dist/modules/customers/backend/customers/deals/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +5 -2
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js +4 -13
- package/dist/modules/customers/backend/customers/deals/pipeline/components/Lane.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js +85 -14
- package/dist/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js +18 -0
- package/dist/modules/customers/backend/customers/deals/pipeline/components/toneClasses.js.map +7 -0
- package/dist/modules/customers/backend/customers/people/page.js +4 -2
- package/dist/modules/customers/backend/customers/people/page.js.map +2 -2
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js +9 -3
- package/dist/modules/customers/backend/hooks/useEmailDuplicateCheck.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +21 -34
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js.map +1 -1
- package/dist/modules/customers/commands/personCompanyLinks.js +131 -2
- package/dist/modules/customers/commands/personCompanyLinks.js.map +2 -2
- package/dist/modules/customers/components/CustomerTodosTable.js +1 -0
- package/dist/modules/customers/components/CustomerTodosTable.js.map +2 -2
- package/dist/modules/customers/components/DealsKpiStrip.js +7 -6
- package/dist/modules/customers/components/DealsKpiStrip.js.map +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js +2 -2
- package/dist/modules/customers/components/calendar/AgendaList.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarEventEditor.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarScreen.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarScreen.js.map +2 -2
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js +1 -1
- package/dist/modules/customers/components/calendar/CalendarSettingsModal.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventBlock.js +1 -1
- package/dist/modules/customers/components/calendar/EventBlock.js.map +2 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js +3 -2
- package/dist/modules/customers/components/calendar/EventPeekPopover.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/PeopleField.js +40 -5
- package/dist/modules/customers/components/calendar/editor/PeopleField.js.map +2 -2
- package/dist/modules/customers/components/calendar/editor/hooks.js +2 -1
- package/dist/modules/customers/components/calendar/editor/hooks.js.map +2 -2
- package/dist/modules/customers/components/calendar/types.js +1 -1
- package/dist/modules/customers/components/calendar/types.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyKpiBar.js +5 -4
- package/dist/modules/customers/components/detail/CompanyKpiBar.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js +3 -1
- package/dist/modules/customers/components/detail/CompanyPeopleSection.js.map +2 -2
- package/dist/modules/customers/components/detail/DealForm.js +19 -7
- package/dist/modules/customers/components/detail/DealForm.js.map +2 -2
- package/dist/modules/customers/components/detail/DealsSection.js +6 -5
- package/dist/modules/customers/components/detail/DealsSection.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +35 -11
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js +3 -1
- package/dist/modules/customers/components/detail/PersonCompaniesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js +19 -13
- package/dist/modules/customers/components/detail/hooks/usePersonTasks.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js +4 -4
- package/dist/modules/customers/components/detail/schedule/ParticipantsField.js.map +2 -2
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js +3 -3
- package/dist/modules/customers/components/detail/schedule/useScheduleFormState.js.map +2 -2
- package/dist/modules/customers/components/detail/utils.js +5 -5
- package/dist/modules/customers/components/detail/utils.js.map +2 -2
- package/dist/modules/customers/data/entities.js.map +1 -1
- package/dist/modules/customers/data/validators.js +32 -3
- package/dist/modules/customers/data/validators.js.map +2 -2
- package/dist/modules/customers/events.js +5 -0
- package/dist/modules/customers/events.js.map +2 -2
- package/dist/modules/customers/lib/calendar/conflicts.js +8 -2
- package/dist/modules/customers/lib/calendar/conflicts.js.map +2 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js +3 -2
- package/dist/modules/customers/lib/calendar/editorPayload.js.map +2 -2
- package/dist/modules/customers/lib/calendar/mapItem.js +8 -3
- package/dist/modules/customers/lib/calendar/mapItem.js.map +2 -2
- package/dist/modules/customers/lib/calendar/participantIdentity.js +9 -0
- package/dist/modules/customers/lib/calendar/participantIdentity.js.map +7 -0
- package/dist/modules/customers/lib/closureStage.js +45 -0
- package/dist/modules/customers/lib/closureStage.js.map +7 -0
- package/dist/modules/customers/lib/dealStatus.js +30 -0
- package/dist/modules/customers/lib/dealStatus.js.map +2 -2
- package/dist/modules/customers/lib/detailReadAccess.js +12 -0
- package/dist/modules/customers/lib/detailReadAccess.js.map +7 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js +1 -0
- package/dist/modules/customers/lib/findPeopleByAddresses.js.map +2 -2
- package/dist/modules/customers/lib/personCompanies.js +100 -2
- package/dist/modules/customers/lib/personCompanies.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +12 -0
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/dashboards/lib/widgetScope.js +6 -1
- package/dist/modules/dashboards/lib/widgetScope.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/page.js +3 -1
- package/dist/modules/data_sync/backend/data-sync/page.js.map +2 -2
- package/dist/modules/data_sync/lib/abandoned-run.js +19 -0
- package/dist/modules/data_sync/lib/abandoned-run.js.map +7 -0
- package/dist/modules/data_sync/lib/sync-engine.js +72 -6
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-export.js +5 -1
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +5 -1
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/devices/acl.js +9 -1
- package/dist/modules/devices/acl.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/[id]/route.js +5 -32
- package/dist/modules/devices/api/admin/devices/[id]/route.js.map +2 -2
- package/dist/modules/devices/api/admin/devices/route.js +14 -1
- package/dist/modules/devices/api/admin/devices/route.js.map +2 -2
- package/dist/modules/devices/api/deviceList.js +99 -12
- package/dist/modules/devices/api/deviceList.js.map +2 -2
- package/dist/modules/devices/api/deviceSerialization.js +46 -0
- package/dist/modules/devices/api/deviceSerialization.js.map +7 -0
- package/dist/modules/devices/api/openapi.js +3 -1
- package/dist/modules/devices/api/openapi.js.map +2 -2
- package/dist/modules/devices/api/route.js +9 -1
- package/dist/modules/devices/api/route.js.map +2 -2
- package/dist/modules/devices/backend/devices/[id]/page.js +10 -26
- package/dist/modules/devices/backend/devices/[id]/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/create/page.js +17 -1
- package/dist/modules/devices/backend/devices/create/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/page.js +28 -34
- package/dist/modules/devices/backend/devices/page.js.map +2 -2
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js +34 -0
- package/dist/modules/devices/backend/devices/useDeviceUserLabels.js.map +7 -0
- package/dist/modules/devices/backend/devices/userOptions.js +52 -0
- package/dist/modules/devices/backend/devices/userOptions.js.map +7 -0
- package/dist/modules/devices/setup.js +6 -2
- package/dist/modules/devices/setup.js.map +2 -2
- package/dist/modules/directory/backend/directory/organizations/page.js +2 -1
- package/dist/modules/directory/backend/directory/organizations/page.js.map +2 -2
- package/dist/modules/directory/backend/directory/tenants/page.js +2 -1
- package/dist/modules/directory/backend/directory/tenants/page.js.map +2 -2
- package/dist/modules/directory/di.js +9 -0
- package/dist/modules/directory/di.js.map +2 -2
- package/dist/modules/directory/services/organizationHierarchyService.js +29 -0
- package/dist/modules/directory/services/organizationHierarchyService.js.map +7 -0
- package/dist/modules/directory/services/organizationScopeService.js +56 -0
- package/dist/modules/directory/services/organizationScopeService.js.map +7 -0
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/records.js +4 -2
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js +3 -1
- package/dist/modules/entities/backend/entities/user/[entityId]/records/page.js.map +2 -2
- package/dist/modules/eudr/api/plots/route.js +5 -1
- package/dist/modules/eudr/api/plots/route.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/evidence-submissions/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/plots/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/plots/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/product-mappings/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/risk-assessments/page.js.map +2 -2
- package/dist/modules/eudr/backend/eudr/statements/page.js +3 -0
- package/dist/modules/eudr/backend/eudr/statements/page.js.map +2 -2
- package/dist/modules/eudr/commands/statements.js +0 -3
- package/dist/modules/eudr/commands/statements.js.map +2 -2
- package/dist/modules/eudr/data/validators.js +4 -1
- package/dist/modules/eudr/data/validators.js.map +2 -2
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js +1 -0
- package/dist/modules/feature_toggles/components/FeatureTogglesTable.js.map +2 -2
- package/dist/modules/feature_toggles/components/OverridesTable.js +1 -0
- package/dist/modules/feature_toggles/components/OverridesTable.js.map +2 -2
- package/dist/modules/feature_toggles/defaults.json +0 -8
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js +3 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/log/page.js.map +2 -2
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js +3 -0
- package/dist/modules/inbox_ops/backend/inbox-ops/page.js.map +2 -2
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js +1 -12
- package/dist/modules/inbox_ops/lib/htmlToPlainText.js.map +2 -2
- package/dist/modules/integrations/api/[id]/credentials/route.js +6 -1
- package/dist/modules/integrations/api/[id]/credentials/route.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/[id]/page.js +38 -29
- package/dist/modules/integrations/backend/integrations/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js +36 -9
- package/dist/modules/integrations/backend/integrations/bundle/[id]/page.js.map +2 -2
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js +55 -0
- package/dist/modules/integrations/backend/integrations/credential-secret-fields.js.map +7 -0
- package/dist/modules/integrations/data/validators.js +11 -3
- package/dist/modules/integrations/data/validators.js.map +2 -2
- package/dist/modules/integrations/lib/credentials-masking.js +6 -2
- package/dist/modules/integrations/lib/credentials-masking.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +29 -1
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/api/openapi.js +17 -2
- package/dist/modules/messages/api/openapi.js.map +2 -2
- package/dist/modules/messages/api/route.js +17 -2
- package/dist/modules/messages/api/route.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +7 -14
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/components/inboxFilters.js +15 -0
- package/dist/modules/messages/components/inboxFilters.js.map +2 -2
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js +11 -1
- package/dist/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js +3 -1
- package/dist/modules/messages/components/message-detail/panels/MessageHeader.js.map +2 -2
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js +2 -1
- package/dist/modules/messages/components/message-detail/panels/thread-panel.js.map +2 -2
- package/dist/modules/messages/components/messageListLabels.js +14 -2
- package/dist/modules/messages/components/messageListLabels.js.map +2 -2
- package/dist/modules/messages/data/validators.js +18 -4
- package/dist/modules/messages/data/validators.js.map +2 -2
- package/dist/modules/messages/lib/channel-sender-identity.js +26 -0
- package/dist/modules/messages/lib/channel-sender-identity.js.map +7 -0
- package/dist/modules/messages/lib/composeSourceChannelType.js +41 -0
- package/dist/modules/messages/lib/composeSourceChannelType.js.map +7 -0
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +4 -6
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js +3 -1
- package/dist/modules/payment_gateways/backend/payment-gateways/page.js.map +2 -2
- package/dist/modules/payment_gateways/lib/webhook-processor.js +7 -0
- package/dist/modules/payment_gateways/lib/webhook-processor.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +3 -0
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/page.js +3 -1
- package/dist/modules/push_notifications/backend/push_notifications/page.js.map +2 -2
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js +1 -1
- package/dist/modules/push_notifications/backend/push_notifications/send/page.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +150 -51
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/search-entity-policy.js +14 -0
- package/dist/modules/query_index/lib/search-entity-policy.js.map +7 -0
- package/dist/modules/query_index/lib/search-tokens.js +79 -3
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +3 -1
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resources/page.js +3 -1
- package/dist/modules/resources/backend/resources/resources/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +3 -0
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +3 -0
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +10 -8
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +79 -1
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/components/PriceWithCurrency.js +7 -5
- package/dist/modules/sales/components/PriceWithCurrency.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +3 -0
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +51 -22
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +83 -18
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +23 -9
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ReturnsSection.js +8 -6
- package/dist/modules/sales/components/documents/ReturnsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +68 -14
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js +6 -5
- package/dist/modules/sales/components/documents/SalesOrderDraftLines.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +5 -4
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +8 -6
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js +54 -0
- package/dist/modules/sales/components/documents/lineItemShipmentLock.js.map +7 -0
- package/dist/modules/sales/components/documents/lineItemUtils.js +2 -2
- package/dist/modules/sales/components/documents/lineItemUtils.js.map +2 -2
- package/dist/modules/sales/data/entities.js +8 -2
- package/dist/modules/sales/data/entities.js.map +2 -2
- package/dist/modules/sales/extension-points.js +1 -0
- package/dist/modules/sales/extension-points.js.map +2 -2
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js +33 -0
- package/dist/modules/sales/lib/salesChannelsToggleSeed.js.map +7 -0
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js +32 -0
- package/dist/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.js.map +7 -0
- package/dist/modules/sales/setup.js +2 -0
- package/dist/modules/sales/setup.js.map +2 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js +10 -2
- package/dist/modules/sales/widgets/injection/payment-gateway-status-column/widget.js.map +2 -2
- package/dist/modules/sales/widgets/injection-table.js +4 -7
- package/dist/modules/sales/widgets/injection-table.js.map +2 -2
- package/dist/modules/staff/backend/staff/leave-requests/page.js +3 -0
- package/dist/modules/staff/backend/staff/leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js +3 -0
- package/dist/modules/staff/backend/staff/my-leave-requests/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +3 -0
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +3 -0
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +3 -0
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +3 -0
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.js +29 -18
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +3 -0
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/sync_excel/lib/adapters/customers.js +1 -0
- package/dist/modules/sync_excel/lib/adapters/customers.js.map +2 -2
- package/dist/modules/warranty_claims/api/assignees/route.js +3 -3
- package/dist/modules/warranty_claims/api/assignees/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js +2 -2
- package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
- package/dist/modules/warranty_claims/api/portal/claims/route.js +12 -9
- package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
- package/dist/modules/warranty_claims/backend/page.js +3 -0
- package/dist/modules/warranty_claims/backend/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/registrations/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.js.map +2 -2
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js +3 -0
- package/dist/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.js.map +2 -2
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js +3 -0
- package/dist/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.js.map +2 -2
- package/dist/modules/wms/api/warehouseSearch.js +21 -0
- package/dist/modules/wms/api/warehouseSearch.js.map +7 -0
- package/dist/modules/wms/api/warehouses/route.js +13 -9
- package/dist/modules/wms/api/warehouses/route.js.map +2 -2
- package/dist/modules/wms/api/zones/route.js +15 -2
- package/dist/modules/wms/api/zones/route.js.map +2 -2
- package/dist/modules/wms/commands/configuration.js +121 -9
- package/dist/modules/wms/commands/configuration.js.map +2 -2
- package/dist/modules/wms/commands/inventory-actions.js +3 -0
- package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
- package/dist/modules/wms/commands/shared.js +9 -1
- package/dist/modules/wms/commands/shared.js.map +2 -2
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js +158 -0
- package/dist/modules/wms/components/backend/WarehouseEditDialog.js.map +7 -0
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js +76 -111
- package/dist/modules/wms/components/backend/WmsConfigurationPage.js.map +3 -3
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js +1 -0
- package/dist/modules/wms/components/backend/WmsInventoryConsolePage.js.map +2 -2
- package/dist/modules/wms/components/backend/WmsLotsListPage.js +1 -0
- package/dist/modules/wms/components/backend/WmsLotsListPage.js.map +2 -2
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js +27 -20
- package/dist/modules/wms/components/backend/inventoryMutationLoaders.js.map +2 -2
- package/dist/modules/wms/components/backend/warehouseFormOptions.js +66 -0
- package/dist/modules/wms/components/backend/warehouseFormOptions.js.map +7 -0
- package/dist/modules/workflows/backend/definitions/page.js +3 -1
- package/dist/modules/workflows/backend/definitions/page.js.map +2 -2
- package/dist/modules/workflows/backend/events/page.js +3 -1
- package/dist/modules/workflows/backend/events/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/page.js +3 -1
- package/dist/modules/workflows/backend/instances/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/page.js +3 -1
- package/dist/modules/workflows/backend/tasks/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +4 -0
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/package.json +9 -10
- package/src/helpers/integration/api.ts +33 -1
- package/src/helpers/integration/communicationChannelsFixtures.ts +69 -1
- package/src/modules/api_keys/api/keys/route.ts +3 -1
- package/src/modules/api_keys/backend/api-keys/page.tsx +4 -1
- package/src/modules/api_keys/di.ts +7 -0
- package/src/modules/api_keys/services/principalService.ts +33 -0
- package/src/modules/attachments/AGENTS.md +15 -0
- package/src/modules/attachments/api/route.ts +2 -2
- package/src/modules/attachments/components/AttachmentLibrary.tsx +3 -0
- package/src/modules/attachments/di.ts +16 -0
- package/src/modules/attachments/i18n/de.json +5 -0
- package/src/modules/attachments/i18n/en.json +5 -0
- package/src/modules/attachments/i18n/es.json +5 -0
- package/src/modules/attachments/i18n/ko.json +5 -0
- package/src/modules/attachments/i18n/pl.json +5 -0
- package/src/modules/attachments/index.ts +10 -0
- package/src/modules/attachments/lib/attachment-service.ts +468 -0
- package/src/modules/attachments/lib/scoped-upload-service.ts +46 -0
- package/src/modules/attachments/lib/upload-limits.ts +6 -2
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +8 -0
- package/src/modules/audit_logs/lib/display-helpers.tsx +42 -32
- package/src/modules/audit_logs/services/actionLogService.ts +74 -11
- package/src/modules/auth/acl.ts +30 -6
- package/src/modules/auth/api/feature-check.ts +10 -3
- package/src/modules/auth/api/login.ts +9 -1
- package/src/modules/auth/api/reset/validate.ts +57 -0
- package/src/modules/auth/api/roles/acl/route.ts +42 -39
- package/src/modules/auth/api/session/refresh.ts +32 -1
- package/src/modules/auth/api/users/acl/route.ts +177 -53
- package/src/modules/auth/api/users/route.ts +18 -5
- package/src/modules/auth/backend/roles/[id]/edit/page.meta.ts +5 -2
- package/src/modules/auth/backend/roles/page.tsx +4 -2
- package/src/modules/auth/backend/users/[id]/edit/page.meta.ts +5 -2
- package/src/modules/auth/backend/users/page.tsx +3 -2
- package/src/modules/auth/commands/acl.ts +495 -0
- package/src/modules/auth/components/AclEditor.tsx +63 -33
- package/src/modules/auth/data/entities.ts +4 -1
- package/src/modules/auth/data/validators.ts +5 -0
- package/src/modules/auth/di.ts +46 -16
- package/src/modules/auth/frontend/reset/[token]/page.tsx +74 -0
- package/src/modules/auth/i18n/de.json +389 -0
- package/src/modules/auth/i18n/en.json +389 -0
- package/src/modules/auth/i18n/es.json +389 -0
- package/src/modules/auth/i18n/ko.json +389 -0
- package/src/modules/auth/i18n/pl.json +391 -2
- package/src/modules/auth/lib/grantChecks.ts +6 -0
- package/src/modules/auth/lib/userIdFilter.ts +31 -0
- package/src/modules/auth/services/authService.ts +18 -2
- package/src/modules/auth/services/principalService.ts +266 -0
- package/src/modules/auth/services/rbacService.ts +110 -24
- package/src/modules/auth/services/roleOrganizationScope.ts +70 -0
- package/src/modules/business_rules/backend/logs/page.tsx +4 -1
- package/src/modules/business_rules/backend/rules/page.tsx +4 -1
- package/src/modules/business_rules/backend/sets/page.tsx +4 -1
- package/src/modules/business_rules/lib/expression-evaluator.ts +9 -9
- package/src/modules/business_rules/lib/rule-evaluator.ts +10 -9
- package/src/modules/catalog/ai-agents.ts +1 -1
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +2 -2
- package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +12 -5
- package/src/modules/catalog/components/categories/CategoriesDataTable.tsx +3 -0
- package/src/modules/catalog/components/products/ProductsDataTable.tsx +7 -0
- package/src/modules/catalog/i18n/de.json +1 -0
- package/src/modules/catalog/i18n/en.json +1 -0
- package/src/modules/catalog/i18n/es.json +1 -0
- package/src/modules/catalog/i18n/ko.json +1 -0
- package/src/modules/catalog/i18n/pl.json +1 -0
- package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +4 -0
- package/src/modules/communication_channels/api/post/channels/[id]/test-send/route.ts +17 -1
- package/src/modules/communication_channels/api/post/test-seed/route.ts +155 -16
- package/src/modules/communication_channels/backend/communication_channels/channels/page.tsx +4 -0
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +38 -1
- package/src/modules/communication_channels/di.ts +7 -0
- package/src/modules/communication_channels/lib/adapter.ts +14 -0
- package/src/modules/communication_channels/lib/email-capabilities.ts +4 -0
- package/src/modules/communication_channels/lib/outbound-recipient.ts +71 -0
- package/src/modules/communication_channels/lib/resolve-channel-type.ts +99 -0
- package/src/modules/communication_channels/lib/test-seed.ts +64 -4
- package/src/modules/configs/i18n/de.json +2 -0
- package/src/modules/configs/i18n/en.json +2 -0
- package/src/modules/configs/i18n/es.json +2 -0
- package/src/modules/configs/i18n/ko.json +2 -0
- package/src/modules/configs/i18n/pl.json +2 -0
- package/src/modules/configs/lib/system-status.ts +9 -0
- package/src/modules/currencies/AGENTS.md +3 -0
- package/src/modules/currencies/backend/currencies/page.tsx +4 -1
- package/src/modules/currencies/backend/exchange-rates/page.tsx +4 -1
- package/src/modules/currencies/services/README.md +45 -0
- package/src/modules/currencies/services/rateFetchingService.ts +6 -1
- package/src/modules/customer_accounts/api/admin/domain-mappings.ts +7 -0
- package/src/modules/customer_accounts/backend/customer_accounts/roles/page.tsx +4 -1
- package/src/modules/customer_accounts/backend/customer_accounts/users/PortalUsersPageClient.tsx +4 -1
- package/src/modules/customer_accounts/services/domainMappingService.ts +10 -0
- package/src/modules/customers/ai-tools/companies-pack.ts +4 -1
- package/src/modules/customers/ai-tools/deal-analyzer-pack.ts +17 -2
- package/src/modules/customers/ai-tools/deals-pack.ts +66 -1
- package/src/modules/customers/ai-tools/people-pack.ts +4 -1
- package/src/modules/customers/api/activities/route.ts +45 -0
- package/src/modules/customers/api/companies/[id]/people/route.ts +24 -73
- package/src/modules/customers/api/companies/[id]/route.ts +24 -107
- package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +16 -8
- package/src/modules/customers/api/deals/[id]/companies/route.ts +4 -1
- package/src/modules/customers/api/deals/[id]/people/route.ts +4 -1
- package/src/modules/customers/api/deals/[id]/route.ts +17 -28
- package/src/modules/customers/api/deals/[id]/stats/route.ts +10 -25
- package/src/modules/customers/api/deals/aggregate/route.ts +14 -4
- package/src/modules/customers/api/deals/map/route.ts +1 -0
- package/src/modules/customers/api/deals/route.ts +50 -1
- package/src/modules/customers/api/entity-roles-factory.ts +8 -4
- package/src/modules/customers/api/interactions/route.ts +8 -2
- package/src/modules/customers/api/interactions/tasks/route.ts +24 -1
- package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +12 -4
- package/src/modules/customers/api/people/[id]/companies/context.ts +4 -1
- package/src/modules/customers/api/people/[id]/companies/enriched/route.ts +4 -1
- package/src/modules/customers/api/people/[id]/route.ts +16 -11
- package/src/modules/customers/api/people/check-phone/route.ts +75 -10
- package/src/modules/customers/api/todos/route.ts +27 -2
- package/src/modules/customers/backend/customers/companies/page.tsx +5 -2
- package/src/modules/customers/backend/customers/deals/[id]/page.tsx +7 -2
- package/src/modules/customers/backend/customers/deals/page.tsx +8 -2
- package/src/modules/customers/backend/customers/deals/pipeline/components/Lane.tsx +5 -14
- package/src/modules/customers/backend/customers/deals/pipeline/components/StatusFilterPopover.tsx +109 -27
- package/src/modules/customers/backend/customers/deals/pipeline/components/toneClasses.ts +21 -0
- package/src/modules/customers/backend/customers/people/page.tsx +5 -2
- package/src/modules/customers/backend/hooks/useEmailDuplicateCheck.ts +12 -4
- package/src/modules/customers/commands/deals.ts +34 -59
- package/src/modules/customers/commands/interactions.ts +1 -1
- package/src/modules/customers/commands/personCompanyLinks.ts +224 -8
- package/src/modules/customers/components/CustomerTodosTable.tsx +2 -0
- package/src/modules/customers/components/DealsKpiStrip.tsx +7 -7
- package/src/modules/customers/components/calendar/AgendaList.tsx +3 -3
- package/src/modules/customers/components/calendar/CalendarEventEditor.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarScreen.tsx +1 -1
- package/src/modules/customers/components/calendar/CalendarSettingsModal.tsx +1 -1
- package/src/modules/customers/components/calendar/EventBlock.tsx +2 -2
- package/src/modules/customers/components/calendar/EventPeekPopover.tsx +4 -2
- package/src/modules/customers/components/calendar/editor/PeopleField.tsx +61 -5
- package/src/modules/customers/components/calendar/editor/hooks.ts +7 -1
- package/src/modules/customers/components/calendar/types.ts +2 -2
- package/src/modules/customers/components/detail/CompanyKpiBar.tsx +5 -4
- package/src/modules/customers/components/detail/CompanyPeopleSection.tsx +8 -1
- package/src/modules/customers/components/detail/DealForm.tsx +22 -2
- package/src/modules/customers/components/detail/DealsSection.tsx +11 -5
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +58 -12
- package/src/modules/customers/components/detail/PersonCompaniesSection.tsx +8 -1
- package/src/modules/customers/components/detail/hooks/usePersonTasks.ts +23 -14
- package/src/modules/customers/components/detail/schedule/ParticipantsField.tsx +5 -5
- package/src/modules/customers/components/detail/schedule/useScheduleFormState.ts +5 -5
- package/src/modules/customers/components/detail/types.ts +1 -1
- package/src/modules/customers/components/detail/utils.ts +15 -5
- package/src/modules/customers/data/entities.ts +1 -1
- package/src/modules/customers/data/validators.ts +50 -9
- package/src/modules/customers/events.ts +5 -0
- package/src/modules/customers/i18n/de.json +9 -1
- package/src/modules/customers/i18n/en.json +9 -1
- package/src/modules/customers/i18n/es.json +9 -1
- package/src/modules/customers/i18n/ko.json +9 -1
- package/src/modules/customers/i18n/pl.json +9 -1
- package/src/modules/customers/lib/calendar/conflicts.ts +10 -2
- package/src/modules/customers/lib/calendar/editorPayload.ts +4 -3
- package/src/modules/customers/lib/calendar/mapItem.ts +8 -3
- package/src/modules/customers/lib/calendar/participantIdentity.ts +17 -0
- package/src/modules/customers/lib/closureStage.ts +76 -0
- package/src/modules/customers/lib/dealStatus.ts +42 -0
- package/src/modules/customers/lib/detailReadAccess.ts +35 -0
- package/src/modules/customers/lib/findPeopleByAddresses.ts +8 -6
- package/src/modules/customers/lib/personCompanies.ts +136 -1
- package/src/modules/customers/lib/todoCompatibility.ts +19 -1
- package/src/modules/dashboards/i18n/es.json +4 -4
- package/src/modules/dashboards/i18n/pl.json +7 -7
- package/src/modules/dashboards/lib/widgetScope.ts +6 -0
- package/src/modules/data_sync/AGENTS.md +1 -1
- package/src/modules/data_sync/backend/data-sync/page.tsx +4 -1
- package/src/modules/data_sync/lib/abandoned-run.ts +56 -0
- package/src/modules/data_sync/lib/adapter.ts +36 -0
- package/src/modules/data_sync/lib/sync-engine.ts +113 -5
- package/src/modules/data_sync/workers/sync-export.ts +4 -0
- package/src/modules/data_sync/workers/sync-import.ts +4 -0
- package/src/modules/devices/AGENTS.md +20 -1
- package/src/modules/devices/acl.ts +9 -1
- package/src/modules/devices/api/admin/devices/[id]/route.ts +5 -34
- package/src/modules/devices/api/admin/devices/route.ts +14 -1
- package/src/modules/devices/api/deviceList.ts +135 -11
- package/src/modules/devices/api/deviceSerialization.ts +50 -0
- package/src/modules/devices/api/openapi.ts +9 -1
- package/src/modules/devices/api/route.ts +9 -1
- package/src/modules/devices/backend/devices/[id]/page.tsx +18 -32
- package/src/modules/devices/backend/devices/create/page.tsx +17 -1
- package/src/modules/devices/backend/devices/page.tsx +44 -45
- package/src/modules/devices/backend/devices/useDeviceUserLabels.ts +45 -0
- package/src/modules/devices/backend/devices/userOptions.ts +99 -0
- package/src/modules/devices/i18n/de.json +3 -2
- package/src/modules/devices/i18n/en.json +3 -2
- package/src/modules/devices/i18n/es.json +3 -2
- package/src/modules/devices/i18n/ko.json +3 -2
- package/src/modules/devices/i18n/pl.json +3 -2
- package/src/modules/devices/setup.ts +6 -2
- package/src/modules/directory/backend/directory/organizations/page.tsx +3 -1
- package/src/modules/directory/backend/directory/tenants/page.tsx +3 -1
- package/src/modules/directory/di.ts +14 -2
- package/src/modules/directory/services/organizationHierarchyService.ts +34 -0
- package/src/modules/directory/services/organizationScopeService.ts +85 -0
- package/src/modules/directory/utils/organizationScope.ts +4 -15
- package/src/modules/entities/api/records.ts +2 -0
- package/src/modules/entities/backend/entities/user/[entityId]/records/page.tsx +4 -1
- package/src/modules/eudr/api/plots/route.ts +4 -0
- package/src/modules/eudr/backend/eudr/evidence-submissions/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/plots/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/product-mappings/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/risk-assessments/page.tsx +4 -0
- package/src/modules/eudr/backend/eudr/statements/page.tsx +4 -0
- package/src/modules/eudr/commands/statements.ts +0 -3
- package/src/modules/eudr/data/validators.ts +16 -1
- package/src/modules/feature_toggles/components/FeatureTogglesTable.tsx +2 -1
- package/src/modules/feature_toggles/components/OverridesTable.tsx +2 -1
- package/src/modules/feature_toggles/defaults.json +0 -8
- package/src/modules/inbox_ops/backend/inbox-ops/log/page.tsx +4 -0
- package/src/modules/inbox_ops/backend/inbox-ops/page.tsx +4 -0
- package/src/modules/inbox_ops/lib/htmlToPlainText.ts +1 -17
- package/src/modules/integrations/api/[id]/credentials/route.ts +6 -4
- package/src/modules/integrations/backend/integrations/[id]/page.tsx +61 -31
- package/src/modules/integrations/backend/integrations/bundle/[id]/page.tsx +44 -9
- package/src/modules/integrations/backend/integrations/credential-secret-fields.ts +84 -0
- package/src/modules/integrations/data/validators.ts +12 -2
- package/src/modules/integrations/i18n/de.json +1 -0
- package/src/modules/integrations/i18n/en.json +1 -0
- package/src/modules/integrations/i18n/es.json +1 -0
- package/src/modules/integrations/i18n/ko.json +1 -0
- package/src/modules/integrations/i18n/pl.json +1 -0
- package/src/modules/integrations/lib/credentials-masking.ts +10 -6
- package/src/modules/messages/api/[id]/route.ts +55 -1
- package/src/modules/messages/api/openapi.ts +20 -0
- package/src/modules/messages/api/route.ts +31 -2
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +9 -16
- package/src/modules/messages/components/inboxFilters.ts +36 -0
- package/src/modules/messages/components/message-detail/hooks/useMessageDetailsConversation.ts +14 -1
- package/src/modules/messages/components/message-detail/panels/MessageHeader.tsx +3 -1
- package/src/modules/messages/components/message-detail/panels/thread-panel.tsx +2 -1
- package/src/modules/messages/components/message-detail/types.ts +3 -0
- package/src/modules/messages/components/messageListLabels.ts +52 -3
- package/src/modules/messages/data/validators.ts +43 -4
- package/src/modules/messages/lib/channel-sender-identity.ts +55 -0
- package/src/modules/messages/lib/composeSourceChannelType.ts +95 -0
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -6
- package/src/modules/payment_gateways/backend/payment-gateways/page.tsx +4 -1
- package/src/modules/payment_gateways/lib/webhook-processor.ts +11 -0
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +4 -0
- package/src/modules/push_notifications/backend/push_notifications/page.tsx +4 -1
- package/src/modules/push_notifications/backend/push_notifications/send/page.tsx +4 -4
- package/src/modules/query_index/lib/engine.ts +249 -61
- package/src/modules/query_index/lib/search-entity-policy.ts +46 -0
- package/src/modules/query_index/lib/search-tokens.ts +135 -3
- package/src/modules/resources/backend/resources/resource-types/page.tsx +4 -1
- package/src/modules/resources/backend/resources/resources/page.tsx +4 -1
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +4 -0
- package/src/modules/sales/backend/sales/channels/page.tsx +4 -0
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +14 -8
- package/src/modules/sales/commands/documents.ts +93 -0
- package/src/modules/sales/components/PriceWithCurrency.tsx +17 -5
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +4 -0
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/ItemsSection.tsx +56 -18
- package/src/modules/sales/components/documents/LineItemDialog.tsx +137 -20
- package/src/modules/sales/components/documents/PaymentsSection.tsx +20 -9
- package/src/modules/sales/components/documents/ReturnsSection.tsx +6 -5
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +106 -14
- package/src/modules/sales/components/documents/SalesOrderDraftLines.tsx +6 -5
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +5 -4
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +6 -5
- package/src/modules/sales/components/documents/lineItemShipmentLock.ts +74 -0
- package/src/modules/sales/components/documents/lineItemUtils.ts +7 -2
- package/src/modules/sales/data/entities.ts +6 -0
- package/src/modules/sales/extension-points.ts +1 -0
- package/src/modules/sales/i18n/de.json +8 -0
- package/src/modules/sales/i18n/en.json +8 -0
- package/src/modules/sales/i18n/es.json +8 -0
- package/src/modules/sales/i18n/ko.json +8 -0
- package/src/modules/sales/i18n/pl.json +8 -0
- package/src/modules/sales/lib/salesChannelsToggleSeed.ts +42 -0
- package/src/modules/sales/migrations/.snapshot-open-mercato.json +60 -0
- package/src/modules/sales/migrations/Migration20260821120000_sales_notes_addresses_context_idx.ts +87 -0
- package/src/modules/sales/setup.ts +2 -0
- package/src/modules/sales/widgets/injection/payment-gateway-status-column/widget.ts +9 -1
- package/src/modules/sales/widgets/injection-table.ts +4 -7
- package/src/modules/staff/backend/staff/leave-requests/page.tsx +4 -0
- package/src/modules/staff/backend/staff/my-leave-requests/page.tsx +4 -0
- package/src/modules/staff/backend/staff/team-members/page.tsx +6 -0
- package/src/modules/staff/backend/staff/team-roles/page.tsx +4 -0
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +4 -0
- package/src/modules/staff/backend/staff/teams/page.tsx +4 -0
- package/src/modules/staff/backend/staff/timesheets/page.tsx +12 -1
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +4 -0
- package/src/modules/sync_excel/lib/adapters/customers.ts +5 -0
- package/src/modules/warranty_claims/api/assignees/route.ts +3 -3
- package/src/modules/warranty_claims/api/portal/attachments/route.ts +2 -2
- package/src/modules/warranty_claims/api/portal/claims/route.ts +12 -9
- package/src/modules/warranty_claims/backend/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/registrations/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/troubleshooting-guides/page.tsx +4 -0
- package/src/modules/warranty_claims/backend/warranty_claims/vendor-policies/page.tsx +4 -0
- package/src/modules/warranty_claims/frontend/[orgSlug]/portal/claims/page.tsx +4 -0
- package/src/modules/wms/api/warehouseSearch.ts +23 -0
- package/src/modules/wms/api/warehouses/route.ts +13 -9
- package/src/modules/wms/api/zones/route.ts +16 -0
- package/src/modules/wms/commands/configuration.ts +159 -10
- package/src/modules/wms/commands/inventory-actions.ts +3 -0
- package/src/modules/wms/commands/shared.ts +17 -0
- package/src/modules/wms/components/backend/WarehouseEditDialog.tsx +216 -0
- package/src/modules/wms/components/backend/WmsConfigurationPage.tsx +95 -141
- package/src/modules/wms/components/backend/WmsInventoryConsolePage.tsx +2 -0
- package/src/modules/wms/components/backend/WmsLotsListPage.tsx +2 -0
- package/src/modules/wms/components/backend/inventoryMutationLoaders.ts +36 -20
- package/src/modules/wms/components/backend/warehouseFormOptions.ts +72 -0
- package/src/modules/wms/i18n/de.json +2 -0
- package/src/modules/wms/i18n/en.json +2 -0
- package/src/modules/wms/i18n/es.json +2 -0
- package/src/modules/wms/i18n/ko.json +2 -0
- package/src/modules/wms/i18n/pl.json +2 -0
- package/src/modules/workflows/backend/definitions/page.tsx +4 -1
- package/src/modules/workflows/backend/events/page.tsx +4 -1
- package/src/modules/workflows/backend/instances/page.tsx +4 -1
- package/src/modules/workflows/backend/tasks/page.tsx +4 -1
- package/src/modules/workflows/lib/activity-executor.ts +9 -0
|
@@ -6,6 +6,7 @@ import { checkRateLimit, getClientIp, RATE_LIMIT_ERROR_FALLBACK } from '@open-me
|
|
|
6
6
|
import type { RateLimiterService } from '@open-mercato/shared/lib/ratelimit/service'
|
|
7
7
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
8
8
|
import { getWebhookHandler } from '@open-mercato/shared/modules/payment_gateways/types'
|
|
9
|
+
import { markQueueJobOrigin } from '@open-mercato/shared/lib/queue/dispatchOrigin'
|
|
9
10
|
import type { IntegrationLogService } from '../../../../integrations/lib/log-service'
|
|
10
11
|
import type { PaymentGatewayService } from '../../../lib/gateway-service'
|
|
11
12
|
import type { CredentialsService } from '../../../../integrations/lib/credentials-service'
|
|
@@ -111,18 +112,15 @@ export async function POST(req: Request, { params }: { params: Promise<{ provide
|
|
|
111
112
|
|
|
112
113
|
const scope = matchedScope
|
|
113
114
|
|
|
114
|
-
const jobPayload = {
|
|
115
|
+
const jobPayload = markQueueJobOrigin({
|
|
115
116
|
providerKey,
|
|
116
117
|
event,
|
|
117
118
|
transactionId: transaction.id,
|
|
118
119
|
scope,
|
|
119
|
-
}
|
|
120
|
+
}, 'inbound-webhook')
|
|
120
121
|
|
|
121
122
|
if (process.env.QUEUE_STRATEGY === 'async') {
|
|
122
|
-
await queue.enqueue(
|
|
123
|
-
name: 'payment-gateway-webhook',
|
|
124
|
-
payload: jobPayload,
|
|
125
|
-
})
|
|
123
|
+
await queue.enqueue(jobPayload)
|
|
126
124
|
} else {
|
|
127
125
|
await processPaymentGatewayWebhookJob(
|
|
128
126
|
{
|
|
@@ -89,6 +89,7 @@ type TransactionsResponse = {
|
|
|
89
89
|
page: number
|
|
90
90
|
pageSize: number
|
|
91
91
|
totalPages: number
|
|
92
|
+
totalIsCapped?: boolean
|
|
92
93
|
}
|
|
93
94
|
|
|
94
95
|
const STATUS_STYLES: Record<string, string> = {
|
|
@@ -183,6 +184,7 @@ export default function PaymentTransactionsPage() {
|
|
|
183
184
|
const [page, setPage] = React.useState(1)
|
|
184
185
|
const [total, setTotal] = React.useState(0)
|
|
185
186
|
const [totalPages, setTotalPages] = React.useState(1)
|
|
187
|
+
const [totalIsCapped, setTotalIsCapped] = React.useState(false)
|
|
186
188
|
const [search, setSearch] = React.useState('')
|
|
187
189
|
const [filterValues, setFilterValues] = React.useState<FilterValues>({})
|
|
188
190
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
@@ -229,6 +231,7 @@ export default function PaymentTransactionsPage() {
|
|
|
229
231
|
setRows(Array.isArray(call.result.items) ? call.result.items : [])
|
|
230
232
|
setTotal(call.result.total ?? 0)
|
|
231
233
|
setTotalPages(call.result.totalPages ?? 1)
|
|
234
|
+
setTotalIsCapped(call.result?.totalIsCapped === true)
|
|
232
235
|
} else {
|
|
233
236
|
flash(t('payment_gateways.transactions.error.load', 'Failed to load payment transactions'), 'error')
|
|
234
237
|
setRows([])
|
|
@@ -474,7 +477,7 @@ export default function PaymentTransactionsPage() {
|
|
|
474
477
|
onSearchChange={(value) => { setSearch(value); setPage(1) }}
|
|
475
478
|
searchPlaceholder={t('payment_gateways.transactions.searchPlaceholder', 'Search by payment, transaction, session, or gateway id')}
|
|
476
479
|
perspective={{ tableId: extensionPoints.hosts.transactionsTable.tableId }}
|
|
477
|
-
pagination={{ page, pageSize: 20, total, totalPages, onPageChange: setPage }}
|
|
480
|
+
pagination={{ page, pageSize: 20, total, totalPages, totalIsCapped, onPageChange: setPage }}
|
|
478
481
|
isLoading={isLoading}
|
|
479
482
|
onRowClick={(row) => setSelectedId((current) => current === row.id ? null : row.id)}
|
|
480
483
|
rowActions={(row) => (
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
2
|
import { getGatewayAdapter, type WebhookEvent } from '@open-mercato/shared/modules/payment_gateways/types'
|
|
3
|
+
import { isTrustedWebhookDispatch } from '@open-mercato/shared/lib/queue/dispatchOrigin'
|
|
4
|
+
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
3
5
|
import type { IntegrationLogService } from '../../integrations/lib/log-service'
|
|
4
6
|
import type { PaymentGatewayService } from './gateway-service'
|
|
5
7
|
import { claimWebhookProcessing, releaseWebhookClaim } from './webhook-utils'
|
|
6
8
|
|
|
9
|
+
const logger = createLogger('payment_gateways').child({ component: 'webhook-processor' })
|
|
10
|
+
|
|
7
11
|
export type PaymentGatewayWebhookJobPayload = {
|
|
8
12
|
providerKey: string
|
|
9
13
|
event: WebhookEvent
|
|
@@ -53,6 +57,13 @@ export async function processPaymentGatewayWebhookJob(
|
|
|
53
57
|
): Promise<void> {
|
|
54
58
|
const { em, paymentGatewayService, integrationLogService } = deps
|
|
55
59
|
const { providerKey, event } = payload
|
|
60
|
+
// Fail closed on untrusted dispatch origins (#5213): only jobs enqueued by the
|
|
61
|
+
// inbound webhook route (signature verified) may drive payment state. Jobs that
|
|
62
|
+
// arrive through any other path — e.g. a scheduled queue target — are dropped.
|
|
63
|
+
if (!isTrustedWebhookDispatch(payload)) {
|
|
64
|
+
logger.error('Dropping webhook job with missing or untrusted dispatch origin', { providerKey, eventType: event?.eventType ?? null })
|
|
65
|
+
return
|
|
66
|
+
}
|
|
56
67
|
// Scope MUST come from the trusted route layer (derived from a verified GatewayTransaction).
|
|
57
68
|
// Never fall back to attacker-controlled metadata on `event.data.metadata` — that was the
|
|
58
69
|
// vector that allowed forged mock webhooks to mutate another tenant's payment state.
|
|
@@ -39,6 +39,7 @@ type RuleSetResponse = {
|
|
|
39
39
|
items?: Array<Record<string, unknown>>
|
|
40
40
|
total?: number
|
|
41
41
|
totalPages?: number
|
|
42
|
+
totalIsCapped?: boolean
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export default function PlannerAvailabilityRuleSetsPage() {
|
|
@@ -67,6 +68,7 @@ export default function PlannerAvailabilityRuleSetsPage() {
|
|
|
67
68
|
const [page, setPage] = React.useState(1)
|
|
68
69
|
const [total, setTotal] = React.useState(0)
|
|
69
70
|
const [totalPages, setTotalPages] = React.useState(1)
|
|
71
|
+
const [totalIsCapped, setTotalIsCapped] = React.useState(false)
|
|
70
72
|
const [sorting, setSorting] = React.useState<SortingState>([{ id: 'name', desc: false }])
|
|
71
73
|
const [search, setSearch] = React.useState('')
|
|
72
74
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
@@ -120,6 +122,7 @@ export default function PlannerAvailabilityRuleSetsPage() {
|
|
|
120
122
|
setRows(items.map(mapRuleSet))
|
|
121
123
|
setTotal(typeof payload.total === 'number' ? payload.total : items.length)
|
|
122
124
|
setTotalPages(typeof payload.totalPages === 'number' ? payload.totalPages : Math.max(1, Math.ceil(items.length / PAGE_SIZE)))
|
|
125
|
+
setTotalIsCapped(payload?.totalIsCapped === true)
|
|
123
126
|
} catch (error) {
|
|
124
127
|
logger.error('Failed to list availability rule sets', { err: error })
|
|
125
128
|
flash(labels.errors.load, 'error')
|
|
@@ -229,6 +232,7 @@ export default function PlannerAvailabilityRuleSetsPage() {
|
|
|
229
232
|
pageSize: PAGE_SIZE,
|
|
230
233
|
total,
|
|
231
234
|
totalPages,
|
|
235
|
+
totalIsCapped,
|
|
232
236
|
onPageChange: setPage,
|
|
233
237
|
}}
|
|
234
238
|
rowActions={(row) => (
|
|
@@ -32,6 +32,7 @@ type ResponsePayload = {
|
|
|
32
32
|
page?: number
|
|
33
33
|
pageSize?: number
|
|
34
34
|
totalPages: number
|
|
35
|
+
totalIsCapped?: boolean
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
const statusVariant: StatusMap<PushDeliveryStatus> = {
|
|
@@ -59,6 +60,7 @@ export default function PushDeliveriesListPage() {
|
|
|
59
60
|
const [page, setPage] = React.useState(1)
|
|
60
61
|
const [total, setTotal] = React.useState(0)
|
|
61
62
|
const [totalPages, setTotalPages] = React.useState(1)
|
|
63
|
+
const [totalIsCapped, setTotalIsCapped] = React.useState(false)
|
|
62
64
|
const [isLoading, setIsLoading] = React.useState(true)
|
|
63
65
|
const scopeVersion = useOrganizationScopeVersion()
|
|
64
66
|
const t = useT()
|
|
@@ -156,6 +158,7 @@ export default function PushDeliveriesListPage() {
|
|
|
156
158
|
setRows(Array.isArray(payload.items) ? payload.items : [])
|
|
157
159
|
setTotal(payload.total || 0)
|
|
158
160
|
setTotalPages(payload.totalPages || 1)
|
|
161
|
+
setTotalIsCapped(payload?.totalIsCapped === true)
|
|
159
162
|
}
|
|
160
163
|
} catch (error) {
|
|
161
164
|
if (!cancelled) {
|
|
@@ -229,7 +232,7 @@ export default function PushDeliveriesListPage() {
|
|
|
229
232
|
onFiltersClear={() => { setFilterValues({}); setPage(1) }}
|
|
230
233
|
perspective={{ tableId: 'push_notifications.deliveries' }}
|
|
231
234
|
onRowClick={(row) => { window.location.href = `/backend/push_notifications/${row.id}` }}
|
|
232
|
-
pagination={{ page, pageSize: 50, total, totalPages, onPageChange: setPage }}
|
|
235
|
+
pagination={{ page, pageSize: 50, total, totalPages, totalIsCapped, onPageChange: setPage }}
|
|
233
236
|
isLoading={isLoading}
|
|
234
237
|
emptyState={t('push_notifications.deliveries.empty')}
|
|
235
238
|
/>
|
|
@@ -70,7 +70,7 @@ function DeviceField({ value, setValue, setFormValue, values, onState }: CrudCus
|
|
|
70
70
|
let cancelled = false
|
|
71
71
|
setLoading(true)
|
|
72
72
|
const params = new URLSearchParams({ userId, pageSize: '50' })
|
|
73
|
-
apiCall<{ items?: Array<{ id: string;
|
|
73
|
+
apiCall<{ items?: Array<{ id: string; deviceId: string; platform: string; pushProvider?: string | null }> }>(
|
|
74
74
|
`/api/devices/admin/devices?${params.toString()}`,
|
|
75
75
|
{ headers: { 'x-om-forbidden-redirect': '0' } },
|
|
76
76
|
{ fallback: null },
|
|
@@ -80,9 +80,9 @@ function DeviceField({ value, setValue, setFormValue, values, onState }: CrudCus
|
|
|
80
80
|
if (cancelled) return
|
|
81
81
|
const items = (call && call.ok ? call.result?.items : []) ?? []
|
|
82
82
|
const opts = items
|
|
83
|
-
.filter((d): d is { id: string;
|
|
84
|
-
!!d && typeof d.id === 'string' && !!d.
|
|
85
|
-
.map((d) => ({ id: d.id, label: `${d.
|
|
83
|
+
.filter((d): d is { id: string; deviceId: string; platform: string; pushProvider?: string | null } =>
|
|
84
|
+
!!d && typeof d.id === 'string' && !!d.pushProvider)
|
|
85
|
+
.map((d) => ({ id: d.id, label: `${d.deviceId} · ${d.platform}${d.pushProvider ? ` · ${d.pushProvider}` : ''}`, platform: d.platform }))
|
|
86
86
|
setDevices(opts)
|
|
87
87
|
if (selected && !opts.some((o) => o.id === selected)) setValue('')
|
|
88
88
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { QueryEngine, QueryOptions, QueryResult, QueryResultMeta, EncryptedSortRowCapWarning, FilterOp, Filter, QueryCustomFieldSource, PartialIndexWarning, QueryExtensionsConfig, Sort } from '@open-mercato/shared/lib/query/types'
|
|
1
|
+
import type { QueryEngine, QueryOptions, QueryResult, QueryResultMeta, EncryptedSortRowCapWarning, ListCountCapWarning, FilterOp, Filter, QueryCustomFieldSource, PartialIndexWarning, QueryExtensionsConfig, Sort } from '@open-mercato/shared/lib/query/types'
|
|
2
2
|
import { SortDir } from '@open-mercato/shared/lib/query/types'
|
|
3
3
|
import type { EntityId } from '@open-mercato/shared/modules/entities'
|
|
4
4
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
@@ -20,6 +20,8 @@ import {
|
|
|
20
20
|
type ResolvedJoin,
|
|
21
21
|
} from '@open-mercato/shared/lib/query/join-utils'
|
|
22
22
|
import { resolveSearchConfig, type SearchConfig } from '@open-mercato/shared/lib/search/config'
|
|
23
|
+
import { isEncryptedLikeField, resolveEncryptedLikeFieldSet } from '@open-mercato/shared/lib/query/engine'
|
|
24
|
+
import { isTenantDataEncryptionEnabled } from '@open-mercato/shared/lib/encryption/toggles'
|
|
23
25
|
import {
|
|
24
26
|
createSearchTokenAvailability,
|
|
25
27
|
isSearchFilterOp,
|
|
@@ -32,6 +34,7 @@ import { tokenizeText } from '@open-mercato/shared/lib/search/tokenize'
|
|
|
32
34
|
import { runBeforeQueryPipeline, runAfterQueryPipeline, type QueryExtensionContext } from '@open-mercato/shared/lib/query/query-extension-runner'
|
|
33
35
|
import { warnOnCiphertextLikeFallback } from '@open-mercato/shared/lib/query/ciphertext-search-warning'
|
|
34
36
|
import { resolveEncryptedSortFields, resolveEncryptedSortMaxRows, sortRowsInMemory } from '@open-mercato/shared/lib/query/encrypted-sort'
|
|
37
|
+
import { resolveListCountCap } from '@open-mercato/shared/lib/query/count-cap'
|
|
35
38
|
import { mapWithConcurrency } from '@open-mercato/shared/lib/query/bounded-decrypt'
|
|
36
39
|
import { createLogger } from '@open-mercato/shared/lib/logger'
|
|
37
40
|
import { parseNumberWithDefault } from '@open-mercato/shared/lib/number'
|
|
@@ -148,13 +151,25 @@ type SearchRuntime = {
|
|
|
148
151
|
organizationScope?: { ids: string[]; includeNull: boolean } | null
|
|
149
152
|
tenantId?: string | null
|
|
150
153
|
searchSources?: SearchTokenSource[]
|
|
154
|
+
/**
|
|
155
|
+
* Base-column fields whose stored value is ciphertext, so a like/ilike on them can only be
|
|
156
|
+
* answered via search tokens. A plaintext column keeps exact SQL ILIKE instead: the token
|
|
157
|
+
* rewrite is approximate -- it splits on non-alphanumerics and drops tokens shorter than
|
|
158
|
+
* minTokenLength, so a document-number search like "ZK 1/2026" degrades to the tokens
|
|
159
|
+
* {202, 2026} and matches every record from that year, and an all-short term like "ZK"
|
|
160
|
+
* produces no tokens and silently drops the predicate. `null`/absent = the encryption
|
|
161
|
+
* service could not answer (or a caller predates this field); keep the old rewrite then,
|
|
162
|
+
* because guessing "plaintext" would turn encrypted-column search into an
|
|
163
|
+
* ILIKE-on-ciphertext that matches nothing.
|
|
164
|
+
*/
|
|
165
|
+
encryptedFields?: Set<string> | null
|
|
151
166
|
/** Per-`query()` alias minter for `search_tokens` subqueries (see #2738). */
|
|
152
167
|
mintAlias: () => string
|
|
153
168
|
}
|
|
154
169
|
|
|
155
170
|
type EncryptionResolver = () => {
|
|
156
171
|
decryptEntityPayload?: (entityId: EntityId, payload: Record<string, unknown>, tenantId?: string | null, organizationId?: string | null) => Promise<Record<string, unknown>>
|
|
157
|
-
getEncryptedFieldNames?: (entityId: EntityId, tenantId?: string | null, organizationId?: string | null) => Promise<readonly string[]>
|
|
172
|
+
getEncryptedFieldNames?: (entityId: EntityId, tenantId?: string | null, organizationId?: string | null, options?: { ignoreRuntimeHealth?: boolean }) => Promise<readonly string[]>
|
|
158
173
|
isEnabled?: () => boolean
|
|
159
174
|
} | null
|
|
160
175
|
|
|
@@ -496,6 +511,51 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
496
511
|
? await this.searchAvailability().anySourceHasTokens(searchSources, opts.tenantId ?? null, orgScope)
|
|
497
512
|
: false
|
|
498
513
|
const searchRuntime: SearchRuntime = { ...searchRuntimeBase, searchSources, enabled: searchEnabled && hasSearchTokens }
|
|
514
|
+
if (
|
|
515
|
+
searchRuntime.enabled &&
|
|
516
|
+
searchConfig.useIlikeForNonEncryptedFields === true &&
|
|
517
|
+
sourceSearchFilters.some((filter) => !String(filter.field).startsWith('cf:'))
|
|
518
|
+
) {
|
|
519
|
+
// `ignoreRuntimeHealth` asks the on-disk question -- a column holds ciphertext even while
|
|
520
|
+
// the KMS is down -- so an outage keeps encrypted columns on the token path (#4622).
|
|
521
|
+
// `organizationId: null` is deliberate, not an omission: the service then unions in every
|
|
522
|
+
// organization's map (`fetchAllOrganizationFieldNames`), so a field any org encrypts stays
|
|
523
|
+
// on the token path -- a wider set fails safe. Passing the request's org instead would
|
|
524
|
+
// silently break encrypted-column search for orgs without their own map. That union is an
|
|
525
|
+
// UNCACHED `encryption_maps` read, one extra round-trip per searched list request.
|
|
526
|
+
try {
|
|
527
|
+
const encryptionService = this.getEncryptionService()
|
|
528
|
+
const readEncryptedFieldNames = encryptionService?.getEncryptedFieldNames?.bind(encryptionService)
|
|
529
|
+
if (readEncryptedFieldNames) {
|
|
530
|
+
searchRuntime.encryptedFields = await resolveEncryptedLikeFieldSet(
|
|
531
|
+
() => readEncryptedFieldNames(
|
|
532
|
+
entity as EntityId,
|
|
533
|
+
opts.tenantId ?? null,
|
|
534
|
+
null,
|
|
535
|
+
{ ignoreRuntimeHealth: true },
|
|
536
|
+
),
|
|
537
|
+
String(entity),
|
|
538
|
+
opts.tenantId ?? null,
|
|
539
|
+
)
|
|
540
|
+
} else if (isTenantDataEncryptionEnabled()) {
|
|
541
|
+
// Encryption is on but the service is unreachable (a swallowed DI failure looks
|
|
542
|
+
// exactly like "no service"): treat the map as UNKNOWN and keep the token rewrite,
|
|
543
|
+
// rather than guessing "plaintext" and running ILIKE against ciphertext.
|
|
544
|
+
searchRuntime.encryptedFields = null
|
|
545
|
+
} else {
|
|
546
|
+
// Encryption disabled: nothing is ciphertext at rest, exact ILIKE is always right.
|
|
547
|
+
searchRuntime.encryptedFields = new Set()
|
|
548
|
+
}
|
|
549
|
+
} catch (err) {
|
|
550
|
+
// The fallback is safe (the old rewrite-everything behavior), but taking it silently
|
|
551
|
+
// would hide that the gate has stopped working.
|
|
552
|
+
logger.warn('search: encrypted-field map unavailable; keeping the token rewrite for all columns', {
|
|
553
|
+
entity: String(entity),
|
|
554
|
+
error: err instanceof Error ? err.message : String(err),
|
|
555
|
+
})
|
|
556
|
+
searchRuntime.encryptedFields = null
|
|
557
|
+
}
|
|
558
|
+
}
|
|
499
559
|
if (searchFilters.length) {
|
|
500
560
|
this.logSearchDebug('search:init', {
|
|
501
561
|
entity,
|
|
@@ -785,10 +845,9 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
785
845
|
return next
|
|
786
846
|
}
|
|
787
847
|
|
|
788
|
-
// Also used by the
|
|
789
|
-
//
|
|
790
|
-
//
|
|
791
|
-
// `canOptimizeCount` is false whenever any cf filter exists (see `hasCustomFieldFilters`).
|
|
848
|
+
// Also used by the count shape: cf leaves (and doc-based base leaves) reference
|
|
849
|
+
// the index rowset aliases, so any group containing one routes the whole
|
|
850
|
+
// disjunction inside the seeded-rowset EXISTS, where those aliases exist.
|
|
792
851
|
const applyOrGroupedBaseFilters = (q: AnyBuilder): AnyBuilder => {
|
|
793
852
|
if (orGroupFilters.length === 0 && orGroupCfFilters.length === 0) return q
|
|
794
853
|
// `BaseFilter` here is just the normalized-leaf shape; the `cf` bucket holds
|
|
@@ -937,6 +996,101 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
937
996
|
const hasCustomFieldFilters = cfFilters.length > 0
|
|
938
997
|
const canOptimizeCount = !hasCustomFieldFilters && !hasNonBaseSearchSource
|
|
939
998
|
|
|
999
|
+
// ── Count shape (#4552 Phase 2) ─────────────────────────────────
|
|
1000
|
+
// The count query is rebuilt rather than derived from the display shape:
|
|
1001
|
+
// base scope + filters only. Predicates that read the left-joined index
|
|
1002
|
+
// rowset (cf filters, doc filters, or-groups with doc members, token
|
|
1003
|
+
// searches over joined sources) evaluate against the *same* rowset as the
|
|
1004
|
+
// display query, but confined inside a single correlated EXISTS built
|
|
1005
|
+
// from a one-row seed — so the per-base-row rowset is identical, yet a
|
|
1006
|
+
// semi-join cannot multiply base rows. The count therefore needs no
|
|
1007
|
+
// DISTINCT or GROUP BY, and an outer LIMIT actually bounds the scan (no
|
|
1008
|
+
// blocking aggregate between the LIMIT and the scan).
|
|
1009
|
+
const isRowsetDependentFilter = (filter: BaseFilter): boolean => {
|
|
1010
|
+
const baseField = resolveBaseColumn(String(filter.field))
|
|
1011
|
+
if (!baseField) return true
|
|
1012
|
+
// Token search may probe joined sources' record ids, which only exist
|
|
1013
|
+
// on the index rowset.
|
|
1014
|
+
if (
|
|
1015
|
+
(filter.op === 'like' || filter.op === 'ilike') &&
|
|
1016
|
+
searchRuntime.enabled &&
|
|
1017
|
+
typeof filter.value === 'string' &&
|
|
1018
|
+
hasNonBaseSearchSource
|
|
1019
|
+
) return true
|
|
1020
|
+
return false
|
|
1021
|
+
}
|
|
1022
|
+
const rowsetRegularFilters = regularBaseFilters.filter((filter) => isRowsetDependentFilter(filter))
|
|
1023
|
+
const outerRegularFilters = regularBaseFilters.filter((filter) => !isRowsetDependentFilter(filter))
|
|
1024
|
+
// Or-groups are disjuncts of one $or, so they cannot be split between the
|
|
1025
|
+
// outer query and the rowset — one doc-dependent member moves them all in.
|
|
1026
|
+
const orGroupsRowsetDependent =
|
|
1027
|
+
orGroupCfFilters.length > 0 ||
|
|
1028
|
+
orGroupFilters.some((filter) => isRowsetDependentFilter(filter))
|
|
1029
|
+
const hasInnerTypedCfSource = preparedCfSources.some((source) =>
|
|
1030
|
+
((opts.customFieldSources ?? []).find((s) => s && (s.alias ?? undefined) === source.alias)?.join?.type ?? 'left') === 'inner')
|
|
1031
|
+
const needsIndexRowset =
|
|
1032
|
+
hasCustomFieldFilters ||
|
|
1033
|
+
rowsetRegularFilters.length > 0 ||
|
|
1034
|
+
orGroupsRowsetDependent ||
|
|
1035
|
+
hasInnerTypedCfSource
|
|
1036
|
+
|
|
1037
|
+
const applyCountShape = async (q: AnyBuilder): Promise<AnyBuilder> => {
|
|
1038
|
+
let next = applyBaseScope(q)
|
|
1039
|
+
for (const filter of outerRegularFilters) {
|
|
1040
|
+
const baseField = resolveBaseColumn(String(filter.field))
|
|
1041
|
+
if (!baseField) continue
|
|
1042
|
+
next = this.applyColumnFilter(next, qualify(baseField), filter, {
|
|
1043
|
+
...searchRuntime, entity, field: String(filter.field), recordIdColumn: qualify('id'),
|
|
1044
|
+
})
|
|
1045
|
+
}
|
|
1046
|
+
if (!orGroupsRowsetDependent) next = applyOrGroupedBaseFilters(next)
|
|
1047
|
+
if (needsIndexRowset) {
|
|
1048
|
+
// One seed row per base row, then the display query's own join
|
|
1049
|
+
// builders — the rowset inside the EXISTS is the display rowset.
|
|
1050
|
+
// Built from `db` (not the where-callback's expression builder),
|
|
1051
|
+
// matching applyJoinFilters' detached-subquery idiom; correlated
|
|
1052
|
+
// references to the outer base alias resolve at compile time.
|
|
1053
|
+
let rowset: AnyBuilder = db
|
|
1054
|
+
.selectFrom(sql`(select 1)`.as('om_count_seed'))
|
|
1055
|
+
.select(sql<number>`1`.as('one'))
|
|
1056
|
+
rowset = applyEntityIndexesJoin(rowset)
|
|
1057
|
+
rowset = applyCustomFieldSourceJoins(rowset)
|
|
1058
|
+
rowset = applyCfFilters(rowset)
|
|
1059
|
+
for (const filter of rowsetRegularFilters) {
|
|
1060
|
+
const baseField = resolveBaseColumn(String(filter.field))
|
|
1061
|
+
if (!baseField) {
|
|
1062
|
+
rowset = this.applyIndexDocFilterFromAlias(
|
|
1063
|
+
rowset, 'ei', entity, String(filter.field), filter.op, filter.value, qualify('id'), searchRuntime,
|
|
1064
|
+
)
|
|
1065
|
+
continue
|
|
1066
|
+
}
|
|
1067
|
+
rowset = this.applyColumnFilter(rowset, qualify(baseField), filter, {
|
|
1068
|
+
...searchRuntime, entity, field: String(filter.field), recordIdColumn: qualify('id'),
|
|
1069
|
+
})
|
|
1070
|
+
}
|
|
1071
|
+
if (orGroupsRowsetDependent) rowset = applyOrGroupedBaseFilters(rowset)
|
|
1072
|
+
const capturedRowset = rowset
|
|
1073
|
+
next = next.where((eb: any) => eb.exists(capturedRowset))
|
|
1074
|
+
}
|
|
1075
|
+
next = await applyJoinFilters({
|
|
1076
|
+
db,
|
|
1077
|
+
baseTable,
|
|
1078
|
+
builder: next,
|
|
1079
|
+
joinMap,
|
|
1080
|
+
joinFilters,
|
|
1081
|
+
aliasTables,
|
|
1082
|
+
qualifyBase: (column) => qualify(column),
|
|
1083
|
+
applyAliasScope: async (target: any, alias: string) => applyAliasScopes(target as AnyBuilder, alias),
|
|
1084
|
+
applyFilterOp: (target, column, op, value) => applyJoinFilterOpFn(target as AnyBuilder, column, op, value),
|
|
1085
|
+
applyJoinFilterOp: async (target, filter, qualified, join) => {
|
|
1086
|
+
const applied = await applyJoinSearchFilterOp(target as AnyBuilder, filter, qualified, join)
|
|
1087
|
+
return { applied, builder: target }
|
|
1088
|
+
},
|
|
1089
|
+
columnExists: (tbl, column) => this.columnExists(tbl, column),
|
|
1090
|
+
})
|
|
1091
|
+
return next
|
|
1092
|
+
}
|
|
1093
|
+
|
|
940
1094
|
// Selection (for data query)
|
|
941
1095
|
const selectFieldSet = new Set<string>((opts.fields && opts.fields.length) ? opts.fields.map(String) : Array.from(columns.keys()))
|
|
942
1096
|
if (requiresPlaintextSort) {
|
|
@@ -997,33 +1151,23 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
997
1151
|
const pageSize = opts.page?.pageSize ?? 20
|
|
998
1152
|
const sqlDebugEnabled = this.isSqlDebugEnabled()
|
|
999
1153
|
|
|
1000
|
-
|
|
1154
|
+
const countCap = resolveListCountCap()
|
|
1001
1155
|
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
applyAliasScope: async (target: any, alias: string) => applyAliasScopes(target as AnyBuilder, alias),
|
|
1018
|
-
applyFilterOp: (target, column, op, value) => applyJoinFilterOpFn(target as AnyBuilder, column, op, value),
|
|
1019
|
-
applyJoinFilterOp: async (target, filter, qualified, join) => {
|
|
1020
|
-
const applied = await applyJoinSearchFilterOp(target as AnyBuilder, filter, qualified, join)
|
|
1021
|
-
return { applied, builder: target }
|
|
1022
|
-
},
|
|
1023
|
-
columnExists: (tbl, column) => this.columnExists(tbl, column),
|
|
1024
|
-
})
|
|
1025
|
-
const sub = countCore.select(sql.ref(qualify('id')).as('id')).groupBy(qualify('id')).as('sq')
|
|
1026
|
-
const countQuery = db.selectFrom(sub as any).select(sql<string>`count(*)`.as('count'))
|
|
1156
|
+
// Both count paths build the same rebuilt shape; for `canOptimizeCount`
|
|
1157
|
+
// queries `needsIndexRowset` is false, so the shape degenerates to
|
|
1158
|
+
// base-scope + filters exactly as the optimized path always had.
|
|
1159
|
+
// `wasOptimizable` carries NO control flow — it only labels the debug
|
|
1160
|
+
// timing so operators can keep telling the two shapes apart.
|
|
1161
|
+
// TODO(2026-08-12): remove `canOptimizeCount` once the shape convergence
|
|
1162
|
+
// has soaked (tracked in the #4552 spec as the Phase 2 follow-up).
|
|
1163
|
+
const runBoundedCount = async (wasOptimizable: boolean): Promise<{ total: number; warning?: ListCountCapWarning }> => {
|
|
1164
|
+
const countRoot = db.selectFrom(`${baseTable} as b` as any)
|
|
1165
|
+
const shape = await applyCountShape(countRoot)
|
|
1166
|
+
const countQuery = countCap !== null
|
|
1167
|
+
? db
|
|
1168
|
+
.selectFrom(shape.select(sql<number>`1`.as('one')).limit(countCap + 1).as('om_count_probe') as any)
|
|
1169
|
+
.select(sql<string>`count(*)`.as('count'))
|
|
1170
|
+
: shape.select(sql<string>`count(*)`.as('count'))
|
|
1027
1171
|
if (debugEnabled && sqlDebugEnabled) {
|
|
1028
1172
|
const compiled = countQuery.compile()
|
|
1029
1173
|
this.debug('query:sql:count', { entity, sql: compiled.sql, bindings: compiled.parameters })
|
|
@@ -1031,25 +1175,19 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1031
1175
|
const countRow = await this.captureSqlTiming(
|
|
1032
1176
|
'query:sql:count', entity,
|
|
1033
1177
|
() => countQuery.executeTakeFirst(),
|
|
1034
|
-
{ optimized:
|
|
1178
|
+
{ optimized: wasOptimizable }, profiler,
|
|
1035
1179
|
)
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
const countBuilder = (await applyQueryShape(countRoot))
|
|
1040
|
-
.select(sql<string>`count(distinct ${sql.ref(qualify('id'))})`.as('count'))
|
|
1041
|
-
if (debugEnabled && sqlDebugEnabled) {
|
|
1042
|
-
const compiled = countBuilder.compile()
|
|
1043
|
-
this.debug('query:sql:count', { entity, sql: compiled.sql, bindings: compiled.parameters })
|
|
1180
|
+
const probed = this.parseCount(countRow)
|
|
1181
|
+
if (countCap !== null && probed > countCap) {
|
|
1182
|
+
return { total: countCap, warning: { entity, cap: countCap } }
|
|
1044
1183
|
}
|
|
1045
|
-
|
|
1046
|
-
'query:sql:count', entity,
|
|
1047
|
-
() => countBuilder.executeTakeFirst(),
|
|
1048
|
-
{ optimized: false }, profiler,
|
|
1049
|
-
)
|
|
1050
|
-
total = this.parseCount(countRow)
|
|
1184
|
+
return { total: probed }
|
|
1051
1185
|
}
|
|
1052
1186
|
|
|
1187
|
+
const counted = await runBoundedCount(canOptimizeCount)
|
|
1188
|
+
const total: number = counted.total
|
|
1189
|
+
const listCountCapWarning: ListCountCapWarning | undefined = counted.warning
|
|
1190
|
+
|
|
1053
1191
|
const dekKeyCache = new Map<string | null, string | null>()
|
|
1054
1192
|
|
|
1055
1193
|
const decryptRow = async (item: Record<string, unknown>): Promise<Record<string, unknown>> => {
|
|
@@ -1100,24 +1238,29 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1100
1238
|
const sortFieldNames = Array.from(new Set(['id', ...scopeFieldNames, ...resolvedSorts.map((s) => String(s.field))]))
|
|
1101
1239
|
phase1 = applySelection(phase1, sortFieldNames)
|
|
1102
1240
|
if (cap !== null) {
|
|
1103
|
-
|
|
1241
|
+
// Probe one row past the cap: truncation is detected from the candidate
|
|
1242
|
+
// scan itself, not by comparing against `total` — which may itself be
|
|
1243
|
+
// capped (`OM_LIST_COUNT_CAP`) and would then never exceed the sort cap.
|
|
1244
|
+
phase1 = phase1.limit(cap + 1).orderBy(qualify('id'), 'asc' as any)
|
|
1104
1245
|
}
|
|
1105
1246
|
if (debugEnabled && sqlDebugEnabled) {
|
|
1106
1247
|
const compiled = phase1.compile()
|
|
1107
1248
|
this.debug('query:sql:data:phase1', { entity, sql: compiled.sql, bindings: compiled.parameters })
|
|
1108
1249
|
}
|
|
1109
|
-
const
|
|
1250
|
+
const candidateRowsRaw = await this.captureSqlTiming(
|
|
1110
1251
|
'query:sql:data:phase1', entity,
|
|
1111
1252
|
() => phase1.execute(),
|
|
1112
1253
|
{ phase: 1 }, profiler,
|
|
1113
1254
|
) as Record<string, unknown>[]
|
|
1255
|
+
const sortTruncated = cap !== null && candidateRowsRaw.length > cap
|
|
1256
|
+
const candidateRows = sortTruncated && cap !== null ? candidateRowsRaw.slice(0, cap) : candidateRowsRaw
|
|
1114
1257
|
const decryptedCandidates = await mapWithConcurrency(candidateRows, DECRYPT_CONCURRENCY, decryptRow)
|
|
1115
1258
|
const orderedCandidates = sortRowsInMemory(decryptedCandidates, resolvedSorts)
|
|
1116
1259
|
const pageIds = orderedCandidates
|
|
1117
1260
|
.slice((page - 1) * pageSize, page * pageSize)
|
|
1118
1261
|
.map((row) => row.id)
|
|
1119
1262
|
|
|
1120
|
-
if (cap !== null
|
|
1263
|
+
if (sortTruncated && cap !== null) {
|
|
1121
1264
|
encryptedSortRowCapWarning = {
|
|
1122
1265
|
entity,
|
|
1123
1266
|
sortFields: resolvedSorts.map((s) => String(s.field)),
|
|
@@ -1173,10 +1316,11 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1173
1316
|
|
|
1174
1317
|
const typedItems = items as unknown as T[]
|
|
1175
1318
|
let result: QueryResult<T> = { items: typedItems, page, pageSize, total }
|
|
1176
|
-
if (partialIndexWarning || encryptedSortRowCapWarning) {
|
|
1319
|
+
if (partialIndexWarning || encryptedSortRowCapWarning || listCountCapWarning) {
|
|
1177
1320
|
const meta: QueryResultMeta = {}
|
|
1178
1321
|
if (partialIndexWarning) meta.partialIndexWarning = partialIndexWarning
|
|
1179
1322
|
if (encryptedSortRowCapWarning) meta.encryptedSortRowCapWarning = encryptedSortRowCapWarning
|
|
1323
|
+
if (listCountCapWarning) meta.listCountCapWarning = listCountCapWarning
|
|
1180
1324
|
result.meta = meta
|
|
1181
1325
|
}
|
|
1182
1326
|
|
|
@@ -1743,11 +1887,13 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1743
1887
|
return this.buildIndexDocFilterExpression(eb, 'ei', entity, fieldName, filter.op, filter.value, 'b.id', searchRuntime)
|
|
1744
1888
|
}
|
|
1745
1889
|
// For like/ilike with active search-tokens, route through hashed-token EXISTS subquery
|
|
1746
|
-
// so encrypted-at-rest columns can still be searched.
|
|
1890
|
+
// so encrypted-at-rest columns can still be searched. Plaintext base columns keep exact
|
|
1891
|
+
// SQL ILIKE -- see SearchRuntime.encryptedFields.
|
|
1747
1892
|
if (
|
|
1748
1893
|
(filter.op === 'like' || filter.op === 'ilike') &&
|
|
1749
1894
|
searchRuntime?.enabled &&
|
|
1750
|
-
typeof filter.value === 'string'
|
|
1895
|
+
typeof filter.value === 'string' &&
|
|
1896
|
+
(searchRuntime.encryptedFields == null || isEncryptedLikeField(searchRuntime.encryptedFields, fieldName))
|
|
1751
1897
|
) {
|
|
1752
1898
|
const tokens = tokenizeText(String(filter.value), searchRuntime.config)
|
|
1753
1899
|
if (tokens.hashes.length) {
|
|
@@ -1771,10 +1917,14 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1771
1917
|
)
|
|
1772
1918
|
}
|
|
1773
1919
|
}
|
|
1774
|
-
// Tokenizer produced no hashes (e.g. value too short).
|
|
1775
|
-
//
|
|
1776
|
-
//
|
|
1777
|
-
|
|
1920
|
+
// Tokenizer produced no hashes (e.g. value too short) or no source is usable. For a
|
|
1921
|
+
// column KNOWN to be encrypted, `false` is the honest answer for an OR leaf -- `true`
|
|
1922
|
+
// would widen the whole disjunction to match everything, on exactly the columns ILIKE
|
|
1923
|
+
// cannot serve. Every other case (gate off, custom-entity runtime, resolution failure)
|
|
1924
|
+
// keeps the legacy predicate-skipping `true`.
|
|
1925
|
+
return searchRuntime?.encryptedFields != null && isEncryptedLikeField(searchRuntime.encryptedFields, fieldName)
|
|
1926
|
+
? sql<boolean>`false`
|
|
1927
|
+
: sql<boolean>`true`
|
|
1778
1928
|
}
|
|
1779
1929
|
return this.buildColumnFilterExpression(eb, qualify(baseField), filter.op, filter.value)
|
|
1780
1930
|
}
|
|
@@ -1853,6 +2003,9 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1853
2003
|
const hasSearchTokens = searchEnabled && hasSearchFilter(normalizedFilters)
|
|
1854
2004
|
? await this.searchAvailability().hasTokens(entity, opts.tenantId ?? null, orgScope)
|
|
1855
2005
|
: false
|
|
2006
|
+
// `encryptedFields` is deliberately NOT resolved here: custom-entity rows live in the
|
|
2007
|
+
// `entity_indexes` doc store, whose fields the base-column encryption map does not describe,
|
|
2008
|
+
// so the ILIKE gate stays inert on this path and like/ilike keeps its previous semantics.
|
|
1856
2009
|
const searchRuntime: SearchRuntime = {
|
|
1857
2010
|
enabled: searchEnabled && hasSearchTokens,
|
|
1858
2011
|
config: searchConfig,
|
|
@@ -1968,17 +2121,35 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
1968
2121
|
const page = opts.page?.page ?? 1
|
|
1969
2122
|
const pageSize = opts.page?.pageSize ?? 20
|
|
1970
2123
|
|
|
2124
|
+
// Single-table count: `applyScope` adds only WHERE predicates (cf filters
|
|
2125
|
+
// included — no join), and doc storage holds one row per record within a
|
|
2126
|
+
// scope, so `count(*)` needs no DISTINCT and — when the cap is active — a
|
|
2127
|
+
// LIMIT on the row-producing inner query bounds the scan (#4552 Phase 2).
|
|
2128
|
+
const countCap = resolveListCountCap()
|
|
1971
2129
|
const root = db.selectFrom(`custom_entities_storage as ${alias}`)
|
|
1972
|
-
const
|
|
2130
|
+
const countShape = applyScope(root)
|
|
2131
|
+
const countQuery = countCap !== null
|
|
2132
|
+
? db
|
|
2133
|
+
.selectFrom(countShape.select(sql<number>`1`.as('one')).limit(countCap + 1).as('om_count_probe') as any)
|
|
2134
|
+
.select(sql<string>`count(*)`.as('count'))
|
|
2135
|
+
: countShape.select(sql<string>`count(*)`.as('count'))
|
|
1973
2136
|
const countRow = await countQuery.executeTakeFirst()
|
|
1974
|
-
const
|
|
2137
|
+
const probed = this.parseCount(countRow)
|
|
2138
|
+
let total = probed
|
|
2139
|
+
let listCountCapWarning: ListCountCapWarning | undefined
|
|
2140
|
+
if (countCap !== null && probed > countCap) {
|
|
2141
|
+
total = countCap
|
|
2142
|
+
listCountCapWarning = { entity: entity as EntityId, cap: countCap }
|
|
2143
|
+
}
|
|
1975
2144
|
|
|
1976
2145
|
let dataQuery = applyScope(db.selectFrom(`custom_entities_storage as ${alias}`))
|
|
1977
2146
|
dataQuery = applySelection(dataQuery)
|
|
1978
2147
|
dataQuery = applySort(dataQuery)
|
|
1979
2148
|
dataQuery = dataQuery.limit(pageSize).offset((page - 1) * pageSize)
|
|
1980
2149
|
const items = await dataQuery.execute()
|
|
1981
|
-
|
|
2150
|
+
const result: QueryResult<T> = { items, page, pageSize, total }
|
|
2151
|
+
if (listCountCapWarning) result.meta = { listCountCapWarning }
|
|
2152
|
+
return result
|
|
1982
2153
|
}
|
|
1983
2154
|
|
|
1984
2155
|
private async tableExists(table: string): Promise<boolean> {
|
|
@@ -2384,7 +2555,11 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
2384
2555
|
if (
|
|
2385
2556
|
(filter.op === 'like' || filter.op === 'ilike') &&
|
|
2386
2557
|
search?.enabled &&
|
|
2387
|
-
typeof filter.value === 'string'
|
|
2558
|
+
typeof filter.value === 'string' &&
|
|
2559
|
+
// Plaintext base columns keep exact SQL ILIKE -- see SearchRuntime.encryptedFields.
|
|
2560
|
+
// Membership runs across name-shape candidates: maps may declare `displayName` while the
|
|
2561
|
+
// filter carries the column name `display_name`.
|
|
2562
|
+
(search.encryptedFields == null || isEncryptedLikeField(search.encryptedFields, search.field))
|
|
2388
2563
|
) {
|
|
2389
2564
|
const tokens = tokenizeText(String(filter.value), search.config)
|
|
2390
2565
|
const hashes = tokens.hashes
|
|
@@ -2413,10 +2588,23 @@ export class HybridQueryEngine implements QueryEngine {
|
|
|
2413
2588
|
})
|
|
2414
2589
|
return q
|
|
2415
2590
|
}
|
|
2591
|
+
// Hashes exist but no usable search source: same reasoning as the no-hash branch below --
|
|
2592
|
+
// a KNOWN-encrypted column must fail closed rather than drop the predicate (which would
|
|
2593
|
+
// return the full list on exactly the columns ILIKE cannot serve).
|
|
2594
|
+
if (search.encryptedFields != null && isEncryptedLikeField(search.encryptedFields, search.field)) {
|
|
2595
|
+
return q.where(sql<boolean>`false`)
|
|
2596
|
+
}
|
|
2416
2597
|
} else {
|
|
2417
2598
|
this.logSearchDebug('search:skip-empty-hashes', {
|
|
2418
2599
|
entity: search.entity, field: search.field, value: filter.value,
|
|
2419
2600
|
})
|
|
2601
|
+
// A column KNOWN to be encrypted has no way to match the term except the token index:
|
|
2602
|
+
// dropping the predicate would return every row for a term merely too short to tokenize.
|
|
2603
|
+
// Every other case (gate off, custom-entity runtime, resolution failure) keeps the
|
|
2604
|
+
// legacy behavior of skipping the predicate.
|
|
2605
|
+
if (search.encryptedFields != null && isEncryptedLikeField(search.encryptedFields, search.field)) {
|
|
2606
|
+
return q.where(sql<boolean>`false`)
|
|
2607
|
+
}
|
|
2420
2608
|
}
|
|
2421
2609
|
return q
|
|
2422
2610
|
}
|