@open-mercato/core 0.6.6-develop.6465.1.019f0fb26f → 0.6.6-develop.6471.1.9ab5bdd79a
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/dist/bootstrap.js +13 -11
- package/dist/bootstrap.js.map +2 -2
- package/dist/helpers/integration/queue-runner.js +4 -2
- package/dist/helpers/integration/queue-runner.js.map +2 -2
- package/dist/modules/api_keys/services/apiKeyService.js +5 -3
- package/dist/modules/api_keys/services/apiKeyService.js.map +2 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js +4 -2
- package/dist/modules/attachments/api/image/[id]/[[...slug]]/route.js.map +2 -2
- package/dist/modules/attachments/api/library/[id]/route.js +3 -1
- package/dist/modules/attachments/api/library/[id]/route.js.map +2 -2
- package/dist/modules/attachments/api/route.js +8 -6
- package/dist/modules/attachments/api/route.js.map +2 -2
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js +5 -3
- package/dist/modules/attachments/components/AttachmentPartitionSettings.js.map +2 -2
- package/dist/modules/attachments/lib/assignmentDetails.js +3 -1
- package/dist/modules/attachments/lib/assignmentDetails.js.map +2 -2
- package/dist/modules/attachments/lib/drivers/driverFactory.js +3 -1
- package/dist/modules/attachments/lib/drivers/driverFactory.js.map +2 -2
- package/dist/modules/attachments/lib/ocrQueue.js +10 -11
- package/dist/modules/attachments/lib/ocrQueue.js.map +2 -2
- package/dist/modules/attachments/lib/ocrService.js +6 -4
- package/dist/modules/attachments/lib/ocrService.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +3 -1
- package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
- package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js +3 -1
- package/dist/modules/audit_logs/api/audit-logs/actions/undo/route.js.map +2 -2
- package/dist/modules/audit_logs/backend/audit-logs/page.js +3 -1
- package/dist/modules/audit_logs/backend/audit-logs/page.js.map +2 -2
- package/dist/modules/audit_logs/components/AuditLogsActions.js +4 -2
- package/dist/modules/audit_logs/components/AuditLogsActions.js.map +2 -2
- package/dist/modules/audit_logs/services/accessLogService.js +4 -2
- package/dist/modules/audit_logs/services/accessLogService.js.map +2 -2
- package/dist/modules/audit_logs/services/actionLogService.js +7 -5
- package/dist/modules/audit_logs/services/actionLogService.js.map +3 -3
- package/dist/modules/auth/api/profile/route.js +4 -2
- package/dist/modules/auth/api/profile/route.js.map +2 -2
- package/dist/modules/auth/api/reset/confirm.js +3 -1
- package/dist/modules/auth/api/reset/confirm.js.map +2 -2
- package/dist/modules/auth/api/reset.js +4 -2
- package/dist/modules/auth/api/reset.js.map +2 -2
- package/dist/modules/auth/api/roles/route.js +3 -1
- package/dist/modules/auth/api/roles/route.js.map +2 -2
- package/dist/modules/auth/api/sidebar/variants/route.js +3 -1
- package/dist/modules/auth/api/sidebar/variants/route.js.map +2 -2
- package/dist/modules/auth/api/users/resend-invite/route.js +4 -2
- package/dist/modules/auth/api/users/resend-invite/route.js.map +2 -2
- package/dist/modules/auth/api/users/route.js +3 -1
- package/dist/modules/auth/api/users/route.js.map +2 -2
- package/dist/modules/auth/backend/auth/profile/page.js +3 -1
- package/dist/modules/auth/backend/auth/profile/page.js.map +2 -2
- package/dist/modules/auth/backend/profile/change-password/page.js +3 -1
- package/dist/modules/auth/backend/profile/change-password/page.js.map +2 -2
- package/dist/modules/auth/backend/users/[id]/edit/page.js +5 -3
- package/dist/modules/auth/backend/users/[id]/edit/page.js.map +2 -2
- package/dist/modules/auth/backend/users/create/page.js +4 -2
- package/dist/modules/auth/backend/users/create/page.js.map +2 -2
- package/dist/modules/auth/cli.js +10 -0
- package/dist/modules/auth/cli.js.map +2 -2
- package/dist/modules/auth/commands/users.js +4 -2
- package/dist/modules/auth/commands/users.js.map +2 -2
- package/dist/modules/auth/lib/consentIntegrity.js +3 -3
- package/dist/modules/auth/lib/consentIntegrity.js.map +2 -2
- package/dist/modules/auth/lib/setup-app.js +9 -10
- package/dist/modules/auth/lib/setup-app.js.map +2 -2
- package/dist/modules/auth/lib/tenantAccess.js +3 -1
- package/dist/modules/auth/lib/tenantAccess.js.map +2 -2
- package/dist/modules/auth/lib/tokenHash.js +3 -3
- package/dist/modules/auth/lib/tokenHash.js.map +2 -2
- package/dist/modules/business_rules/api/rules/route.js +4 -2
- package/dist/modules/business_rules/api/rules/route.js.map +2 -2
- package/dist/modules/business_rules/lib/expression-evaluator.js +7 -5
- package/dist/modules/business_rules/lib/expression-evaluator.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-engine.js +5 -3
- package/dist/modules/business_rules/lib/rule-engine.js.map +2 -2
- package/dist/modules/business_rules/lib/rule-evaluator.js +11 -12
- package/dist/modules/business_rules/lib/rule-evaluator.js.map +2 -2
- package/dist/modules/business_rules/subscribers/crud-rule-trigger.js +3 -1
- package/dist/modules/business_rules/subscribers/crud-rule-trigger.js.map +2 -2
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js +3 -1
- package/dist/modules/business_rules/subscribers/rule-execution-failed-notification.js.map +2 -2
- package/dist/modules/catalog/ai-agents-context.js +4 -5
- package/dist/modules/catalog/ai-agents-context.js.map +2 -2
- package/dist/modules/catalog/ai-tools/_shared.js +3 -1
- package/dist/modules/catalog/ai-tools/_shared.js.map +2 -2
- package/dist/modules/catalog/ai-tools/authoring-pack.js +3 -1
- package/dist/modules/catalog/ai-tools/authoring-pack.js.map +2 -2
- package/dist/modules/catalog/ai-tools/merchandising-pack.js +3 -1
- package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +2 -2
- package/dist/modules/catalog/api/dictionaries/[key]/route.js +3 -1
- package/dist/modules/catalog/api/dictionaries/[key]/route.js.map +2 -2
- package/dist/modules/catalog/api/prices/route.js +3 -1
- package/dist/modules/catalog/api/prices/route.js.map +2 -2
- package/dist/modules/catalog/api/products/route.js +5 -3
- package/dist/modules/catalog/api/products/route.js.map +2 -2
- package/dist/modules/catalog/api/settings/route.js +4 -2
- package/dist/modules/catalog/api/settings/route.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js +14 -12
- package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js +8 -6
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js +5 -3
- package/dist/modules/catalog/backend/catalog/products/[productId]/variants/create/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/create/page.js +5 -6
- package/dist/modules/catalog/backend/catalog/products/create/page.js.map +2 -2
- package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js +3 -1
- package/dist/modules/catalog/backend/catalog/products/optionSchemaClient.js.map +2 -2
- package/dist/modules/catalog/components/PriceKindSettings.js +5 -3
- package/dist/modules/catalog/components/PriceKindSettings.js.map +2 -2
- package/dist/modules/catalog/components/UnitPriceDisplaySettings.js +4 -2
- package/dist/modules/catalog/components/UnitPriceDisplaySettings.js.map +2 -2
- package/dist/modules/catalog/inbox-actions.js +4 -2
- package/dist/modules/catalog/inbox-actions.js.map +2 -2
- package/dist/modules/catalog/seed/examples.js +4 -5
- package/dist/modules/catalog/seed/examples.js.map +2 -2
- package/dist/modules/catalog/subscribers/low-stock-notification.js +3 -1
- package/dist/modules/catalog/subscribers/low-stock-notification.js.map +2 -2
- package/dist/modules/catalog/widgets/injection/product-seo/state.js +3 -1
- package/dist/modules/catalog/widgets/injection/product-seo/state.js.map +2 -2
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js +12 -11
- package/dist/modules/communication_channels/api/get/oauth/[provider]/callback/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js +3 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/import-history/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js +3 -1
- package/dist/modules/communication_channels/api/post/channels/[id]/push/register/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js +5 -3
- package/dist/modules/communication_channels/api/post/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js +6 -6
- package/dist/modules/communication_channels/api/post/webhooks/gmail/route.js.map +2 -2
- package/dist/modules/communication_channels/commands/connect-credential-channel.js +4 -6
- package/dist/modules/communication_channels/commands/connect-credential-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/delete-channel.js +3 -3
- package/dist/modules/communication_channels/commands/delete-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js +4 -5
- package/dist/modules/communication_channels/commands/deliver-outbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/disconnect-channel.js +3 -3
- package/dist/modules/communication_channels/commands/disconnect-channel.js.map +2 -2
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js +5 -12
- package/dist/modules/communication_channels/commands/ingest-inbound-message.js.map +2 -2
- package/dist/modules/communication_channels/commands/push-unregister.js +3 -3
- package/dist/modules/communication_channels/commands/push-unregister.js.map +2 -2
- package/dist/modules/communication_channels/commands/reassign-conversation.js +5 -3
- package/dist/modules/communication_channels/commands/reassign-conversation.js.map +2 -2
- package/dist/modules/communication_channels/lib/dead-letter.js +3 -3
- package/dist/modules/communication_channels/lib/dead-letter.js.map +2 -2
- package/dist/modules/communication_channels/lib/email-mime.js +4 -6
- package/dist/modules/communication_channels/lib/email-mime.js.map +2 -2
- package/dist/modules/communication_channels/lib/oauth-client-config.js +5 -3
- package/dist/modules/communication_channels/lib/oauth-client-config.js.map +2 -2
- package/dist/modules/communication_channels/lib/send-as-user.js +3 -1
- package/dist/modules/communication_channels/lib/send-as-user.js.map +2 -2
- package/dist/modules/communication_channels/lib/thread-token.js +4 -2
- package/dist/modules/communication_channels/lib/thread-token.js.map +2 -2
- package/dist/modules/communication_channels/setup.js +3 -4
- package/dist/modules/communication_channels/setup.js.map +2 -2
- package/dist/modules/communication_channels/workers/channel-import-history.js +4 -6
- package/dist/modules/communication_channels/workers/channel-import-history.js.map +2 -2
- package/dist/modules/communication_channels/workers/gmail-history-sync.js +8 -16
- package/dist/modules/communication_channels/workers/gmail-history-sync.js.map +2 -2
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js +5 -7
- package/dist/modules/communication_channels/workers/gmail-renew-watch.js.map +2 -2
- package/dist/modules/communication_channels/workers/outbound-delivery.js +6 -12
- package/dist/modules/communication_channels/workers/outbound-delivery.js.map +2 -2
- package/dist/modules/communication_channels/workers/poll-channel.js +7 -15
- package/dist/modules/communication_channels/workers/poll-channel.js.map +2 -2
- package/dist/modules/communication_channels/workers/poll-tick.js +5 -6
- package/dist/modules/communication_channels/workers/poll-tick.js.map +2 -2
- package/dist/modules/communication_channels/workers/reaction-processor.js +5 -9
- package/dist/modules/communication_channels/workers/reaction-processor.js.map +2 -2
- package/dist/modules/configs/api/cache/route.js +4 -2
- package/dist/modules/configs/api/cache/route.js.map +2 -2
- package/dist/modules/configs/api/system-status/route.js +4 -2
- package/dist/modules/configs/api/system-status/route.js.map +2 -2
- package/dist/modules/configs/api/upgrade-actions/route.js +4 -2
- package/dist/modules/configs/api/upgrade-actions/route.js.map +2 -2
- package/dist/modules/configs/components/ModuleTelemetryPanel.js +3 -1
- package/dist/modules/configs/components/ModuleTelemetryPanel.js.map +2 -2
- package/dist/modules/configs/lib/touchGeneratedBarrels.js +2 -1
- package/dist/modules/configs/lib/touchGeneratedBarrels.js.map +2 -2
- package/dist/modules/configs/services/upgradeActionsService.js +6 -3
- package/dist/modules/configs/services/upgradeActionsService.js.map +2 -2
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js +3 -1
- package/dist/modules/currencies/components/CurrencyFetchingConfig.js.map +2 -2
- package/dist/modules/currencies/lib/exchangeRateFormConfig.js +3 -1
- package/dist/modules/currencies/lib/exchangeRateFormConfig.js.map +2 -2
- package/dist/modules/currencies/services/providers/nbp.js +6 -4
- package/dist/modules/currencies/services/providers/nbp.js.map +2 -2
- package/dist/modules/currencies/services/providers/raiffeisen.js +7 -7
- package/dist/modules/currencies/services/providers/raiffeisen.js.map +2 -2
- package/dist/modules/customer_accounts/api/domain-resolve/all.js +6 -5
- package/dist/modules/customer_accounts/api/domain-resolve/all.js.map +2 -2
- package/dist/modules/customer_accounts/api/signup.js +4 -2
- package/dist/modules/customer_accounts/api/signup.js.map +2 -2
- package/dist/modules/customer_accounts/lib/customerAuthServer.js +3 -1
- package/dist/modules/customer_accounts/lib/customerAuthServer.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js +3 -1
- package/dist/modules/customer_accounts/subscribers/autoLinkCrm.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js +3 -1
- package/dist/modules/customer_accounts/subscribers/autoLinkCrmReverse.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/unlinkOnCompanyDelete.js +3 -1
- package/dist/modules/customer_accounts/subscribers/unlinkOnCompanyDelete.js.map +2 -2
- package/dist/modules/customer_accounts/subscribers/unlinkOnPersonDelete.js +3 -1
- package/dist/modules/customer_accounts/subscribers/unlinkOnPersonDelete.js.map +2 -2
- package/dist/modules/customers/ai-agents-context.js +4 -5
- package/dist/modules/customers/ai-agents-context.js.map +2 -2
- package/dist/modules/customers/api/activities/route.js +6 -4
- package/dist/modules/customers/api/activities/route.js.map +2 -2
- package/dist/modules/customers/api/comments/route.js +3 -1
- package/dist/modules/customers/api/comments/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/people/route.js +3 -1
- package/dist/modules/customers/api/companies/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/companies/[id]/route.js +5 -3
- package/dist/modules/customers/api/companies/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/companies/route.js +5 -3
- package/dist/modules/customers/api/companies/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js +3 -1
- package/dist/modules/customers/api/dashboard/widgets/customer-todos/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/new-customers/route.js +3 -1
- package/dist/modules/customers/api/dashboard/widgets/new-customers/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/new-deals/route.js +3 -1
- package/dist/modules/customers/api/dashboard/widgets/new-deals/route.js.map +2 -2
- package/dist/modules/customers/api/dashboard/widgets/next-interactions/route.js +3 -1
- package/dist/modules/customers/api/dashboard/widgets/next-interactions/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/companies/route.js +3 -1
- package/dist/modules/customers/api/deals/[id]/companies/route.js.map +2 -2
- package/dist/modules/customers/api/deals/[id]/people/route.js +3 -1
- package/dist/modules/customers/api/deals/[id]/people/route.js.map +2 -2
- package/dist/modules/customers/api/deals/aggregate/route.js +3 -1
- package/dist/modules/customers/api/deals/aggregate/route.js.map +2 -2
- package/dist/modules/customers/api/deals/bulk-update-owner/route.js +7 -5
- package/dist/modules/customers/api/deals/bulk-update-owner/route.js.map +2 -2
- package/dist/modules/customers/api/deals/bulk-update-stage/route.js +7 -5
- package/dist/modules/customers/api/deals/bulk-update-stage/route.js.map +2 -2
- package/dist/modules/customers/api/deals/route.js +3 -1
- package/dist/modules/customers/api/deals/route.js.map +2 -2
- package/dist/modules/customers/api/deals/summary/route.js +3 -1
- package/dist/modules/customers/api/deals/summary/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +4 -2
- package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -3
- package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/cache.js +3 -1
- package/dist/modules/customers/api/dictionaries/cache.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/context.js +3 -1
- package/dist/modules/customers/api/dictionaries/context.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/currency/route.js +3 -1
- package/dist/modules/customers/api/dictionaries/currency/route.js.map +2 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js +4 -2
- package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
- package/dist/modules/customers/api/entity-roles-factory.js +7 -5
- package/dist/modules/customers/api/entity-roles-factory.js.map +2 -2
- package/dist/modules/customers/api/interactions/cancel/route.js +3 -1
- package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/complete/route.js +3 -1
- package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/conflicts/route.js +3 -1
- package/dist/modules/customers/api/interactions/conflicts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/counts/route.js +3 -1
- package/dist/modules/customers/api/interactions/counts/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/route.js +7 -4
- package/dist/modules/customers/api/interactions/route.js.map +2 -2
- package/dist/modules/customers/api/interactions/tasks/route.js +3 -1
- package/dist/modules/customers/api/interactions/tasks/route.js.map +2 -2
- package/dist/modules/customers/api/labels/assign/route.js +3 -1
- package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
- package/dist/modules/customers/api/labels/route.js +6 -3
- package/dist/modules/customers/api/labels/route.js.map +2 -2
- package/dist/modules/customers/api/labels/unassign/route.js +3 -1
- package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js +3 -1
- package/dist/modules/customers/api/people/[id]/companies/enriched/route.js.map +2 -2
- package/dist/modules/customers/api/people/[id]/route.js +5 -3
- package/dist/modules/customers/api/people/[id]/route.js.map +2 -2
- package/dist/modules/customers/api/people/route.js +5 -3
- package/dist/modules/customers/api/people/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js +3 -1
- package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
- package/dist/modules/customers/api/pipeline-stages/route.js +6 -4
- package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
- package/dist/modules/customers/api/pipelines/route.js +6 -4
- package/dist/modules/customers/api/pipelines/route.js.map +2 -2
- package/dist/modules/customers/api/settings/address-format/route.js +4 -2
- package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +4 -2
- package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js +4 -2
- package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
- package/dist/modules/customers/api/tags/assign/route.js +3 -1
- package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
- package/dist/modules/customers/api/tags/unassign/route.js +3 -1
- package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
- package/dist/modules/customers/api/todos/route.js +6 -4
- package/dist/modules/customers/api/todos/route.js.map +2 -2
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js +5 -3
- package/dist/modules/customers/backend/customers/companies-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js +3 -1
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js +3 -1
- package/dist/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js +4 -2
- package/dist/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.js.map +2 -2
- package/dist/modules/customers/backend/customers/deals/page.js +4 -2
- package/dist/modules/customers/backend/customers/deals/page.js.map +2 -2
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js +5 -3
- package/dist/modules/customers/backend/customers/people-v2/[id]/page.js.map +2 -2
- package/dist/modules/customers/commands/deals.js +3 -1
- package/dist/modules/customers/commands/deals.js.map +2 -2
- package/dist/modules/customers/commands/dictionaries.js +3 -1
- package/dist/modules/customers/commands/dictionaries.js.map +2 -2
- package/dist/modules/customers/commands/interactions.js +4 -2
- package/dist/modules/customers/commands/interactions.js.map +2 -2
- package/dist/modules/customers/commands/shared.js +4 -2
- package/dist/modules/customers/commands/shared.js.map +2 -2
- package/dist/modules/customers/commands/todos.js +3 -1
- package/dist/modules/customers/commands/todos.js.map +2 -2
- package/dist/modules/customers/components/DictionarySettings.js +5 -3
- package/dist/modules/customers/components/DictionarySettings.js.map +2 -2
- package/dist/modules/customers/components/DictionarySortSettings.js +3 -1
- package/dist/modules/customers/components/DictionarySortSettings.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivitiesCard.js +3 -1
- package/dist/modules/customers/components/detail/ActivitiesCard.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivitiesDayStrip.js +3 -1
- package/dist/modules/customers/components/detail/ActivitiesDayStrip.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivitiesSection.js +6 -4
- package/dist/modules/customers/components/detail/ActivitiesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/ActivityCard.js +3 -1
- package/dist/modules/customers/components/detail/ActivityCard.js.map +2 -2
- package/dist/modules/customers/components/detail/ChangelogTab.js +3 -1
- package/dist/modules/customers/components/detail/ChangelogTab.js.map +2 -2
- package/dist/modules/customers/components/detail/CompanyCard.js +3 -1
- package/dist/modules/customers/components/detail/CompanyCard.js.map +2 -2
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js +3 -1
- package/dist/modules/customers/components/detail/ConfirmDealLostDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/EntityTagsDialog.js +3 -1
- package/dist/modules/customers/components/detail/EntityTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/InlineActivityComposer.js +3 -1
- package/dist/modules/customers/components/detail/InlineActivityComposer.js.map +2 -2
- package/dist/modules/customers/components/detail/ManageTagsDialog.js +3 -1
- package/dist/modules/customers/components/detail/ManageTagsDialog.js.map +2 -2
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js +5 -3
- package/dist/modules/customers/components/detail/RoleAssignmentRow.js.map +2 -2
- package/dist/modules/customers/components/detail/RolesSection.js +4 -2
- package/dist/modules/customers/components/detail/RolesSection.js.map +2 -2
- package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js +5 -3
- package/dist/modules/customers/components/detail/hooks/useInteractionMutations.js.map +2 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js +4 -2
- package/dist/modules/customers/components/linking/LinkEntityDialog.js.map +2 -2
- package/dist/modules/customers/inbox-actions.js +3 -1
- package/dist/modules/customers/inbox-actions.js.map +2 -2
- package/dist/modules/customers/lib/bulkDeals.js +3 -1
- package/dist/modules/customers/lib/bulkDeals.js.map +2 -2
- package/dist/modules/customers/lib/dealClosureNotification.js +4 -2
- package/dist/modules/customers/lib/dealClosureNotification.js.map +2 -2
- package/dist/modules/customers/lib/dealStageTransitionTable.js +3 -1
- package/dist/modules/customers/lib/dealStageTransitionTable.js.map +2 -2
- package/dist/modules/customers/lib/interactionFeatureFlags.js +3 -1
- package/dist/modules/customers/lib/interactionFeatureFlags.js.map +2 -2
- package/dist/modules/customers/lib/todoCompatibility.js +3 -1
- package/dist/modules/customers/lib/todoCompatibility.js.map +2 -2
- package/dist/modules/customers/search.js +14 -7
- package/dist/modules/customers/search.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js +3 -1
- package/dist/modules/customers/widgets/dashboard/customer-todos/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js +3 -1
- package/dist/modules/customers/widgets/dashboard/new-customers/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js +3 -1
- package/dist/modules/customers/widgets/dashboard/new-deals/widget.client.js.map +2 -2
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js +3 -1
- package/dist/modules/customers/widgets/dashboard/next-interactions/widget.client.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/batch/route.js +3 -1
- package/dist/modules/dashboards/api/widgets/data/batch/route.js.map +2 -2
- package/dist/modules/dashboards/api/widgets/data/route.js +3 -1
- package/dist/modules/dashboards/api/widgets/data/route.js.map +2 -2
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js +5 -3
- package/dist/modules/dashboards/components/WidgetVisibilityEditor.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/top-customers/widget.client.js.map +2 -2
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js +3 -1
- package/dist/modules/dashboards/widgets/dashboard/top-products/widget.client.js.map +2 -2
- package/dist/modules/data_sync/api/run.js +3 -1
- package/dist/modules/data_sync/api/run.js.map +2 -2
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js +1 -1
- package/dist/modules/data_sync/backend/data-sync/runs/[id]/page.js.map +2 -2
- package/dist/modules/data_sync/lib/adapter-registry.js +11 -4
- package/dist/modules/data_sync/lib/adapter-registry.js.map +2 -2
- package/dist/modules/data_sync/lib/sync-engine.js +23 -2
- package/dist/modules/data_sync/lib/sync-engine.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-export.js +3 -1
- package/dist/modules/data_sync/workers/sync-export.js.map +2 -2
- package/dist/modules/data_sync/workers/sync-import.js +3 -1
- package/dist/modules/data_sync/workers/sync-import.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +4 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +3 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +4 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +3 -1
- package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js +5 -3
- package/dist/modules/dictionaries/api/[dictionaryId]/route.js.map +2 -2
- package/dist/modules/dictionaries/api/context.js +3 -1
- package/dist/modules/dictionaries/api/context.js.map +2 -2
- package/dist/modules/dictionaries/api/route.js +4 -2
- package/dist/modules/dictionaries/api/route.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionariesManager.js +5 -3
- package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js +4 -2
- package/dist/modules/dictionaries/components/DictionaryEntriesEditor.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js +4 -2
- package/dist/modules/dictionaries/components/DictionaryEntrySelect.js.map +2 -2
- package/dist/modules/dictionaries/components/DictionarySelectControl.js +3 -1
- package/dist/modules/dictionaries/components/DictionarySelectControl.js.map +2 -2
- package/dist/modules/dictionaries/fields/dictionary.js +3 -1
- package/dist/modules/dictionaries/fields/dictionary.js.map +2 -2
- package/dist/modules/directory/api/organization-branding/route.js +3 -1
- package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
- package/dist/modules/directory/api/organization-switcher/route.js +3 -1
- package/dist/modules/directory/api/organization-switcher/route.js.map +2 -2
- package/dist/modules/directory/commands/tenants.js +5 -3
- package/dist/modules/directory/commands/tenants.js.map +2 -2
- package/dist/modules/directory/utils/organizationScope.js +6 -4
- package/dist/modules/directory/utils/organizationScope.js.map +2 -2
- package/dist/modules/entities/api/definitions.cache.js +3 -1
- package/dist/modules/entities/api/definitions.cache.js.map +2 -2
- package/dist/modules/entities/api/definitions.js +7 -5
- package/dist/modules/entities/api/definitions.js.map +2 -2
- package/dist/modules/entities/api/records.js +4 -8
- package/dist/modules/entities/api/records.js.map +2 -2
- package/dist/modules/entities/components/EncryptionManager.js +3 -1
- package/dist/modules/entities/components/EncryptionManager.js.map +2 -2
- package/dist/modules/entities/lib/makeActivityRoute.js +3 -1
- package/dist/modules/entities/lib/makeActivityRoute.js.map +2 -2
- package/dist/modules/feature_toggles/api/overrides/route.js +3 -1
- package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
- package/dist/modules/feature_toggles/lib/feature-flag-check.js +5 -9
- package/dist/modules/feature_toggles/lib/feature-flag-check.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js +4 -2
- package/dist/modules/inbox_ops/api/emails/[id]/reprocess/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/[id]/route.js +4 -2
- package/dist/modules/inbox_ops/api/emails/[id]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/emails/route.js +3 -1
- package/dist/modules/inbox_ops/api/emails/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/extract/route.js +3 -1
- package/dist/modules/inbox_ops/api/extract/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js +3 -1
- package/dist/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/route.js +4 -2
- package/dist/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/counts/route.js +4 -2
- package/dist/modules/inbox_ops/api/proposals/counts/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/proposals/route.js +3 -1
- package/dist/modules/inbox_ops/api/proposals/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/routeHelpers.js +3 -1
- package/dist/modules/inbox_ops/api/routeHelpers.js.map +2 -2
- package/dist/modules/inbox_ops/api/settings/route.js +3 -1
- package/dist/modules/inbox_ops/api/settings/route.js.map +2 -2
- package/dist/modules/inbox_ops/api/webhook/inbound.js +7 -5
- package/dist/modules/inbox_ops/api/webhook/inbound.js.map +2 -2
- package/dist/modules/inbox_ops/lib/cache.js +4 -2
- package/dist/modules/inbox_ops/lib/cache.js.map +2 -2
- package/dist/modules/inbox_ops/lib/catalogLookup.js +3 -1
- package/dist/modules/inbox_ops/lib/catalogLookup.js.map +2 -2
- package/dist/modules/inbox_ops/lib/eventBus.js +3 -4
- package/dist/modules/inbox_ops/lib/eventBus.js.map +2 -2
- package/dist/modules/inbox_ops/lib/messagesIntegration.js +4 -8
- package/dist/modules/inbox_ops/lib/messagesIntegration.js.map +2 -2
- package/dist/modules/inbox_ops/subscribers/executionAuditor.js +3 -1
- package/dist/modules/inbox_ops/subscribers/executionAuditor.js.map +2 -2
- package/dist/modules/inbox_ops/subscribers/extractionWorker.js +10 -8
- package/dist/modules/inbox_ops/subscribers/extractionWorker.js.map +2 -2
- package/dist/modules/inbox_ops/subscribers/proposalNotifier.js +3 -1
- package/dist/modules/inbox_ops/subscribers/proposalNotifier.js.map +2 -2
- package/dist/modules/messages/api/[id]/route.js +3 -4
- package/dist/modules/messages/api/[id]/route.js.map +2 -2
- package/dist/modules/messages/commands/actions.js +3 -1
- package/dist/modules/messages/commands/actions.js.map +2 -2
- package/dist/modules/messages/components/MessagesInboxPageClient.js +3 -1
- package/dist/modules/messages/components/MessagesInboxPageClient.js.map +2 -2
- package/dist/modules/messages/lib/email-sender.js +3 -7
- package/dist/modules/messages/lib/email-sender.js.map +2 -2
- package/dist/modules/messages/lib/message-objects-registry.js +3 -1
- package/dist/modules/messages/lib/message-objects-registry.js.map +2 -2
- package/dist/modules/messages/lib/message-types-registry.js +3 -1
- package/dist/modules/messages/lib/message-types-registry.js.map +2 -2
- package/dist/modules/messages/workers/send-email.worker.js +6 -4
- package/dist/modules/messages/workers/send-email.worker.js.map +2 -2
- package/dist/modules/notifications/lib/notificationMapper.js +3 -4
- package/dist/modules/notifications/lib/notificationMapper.js.map +2 -2
- package/dist/modules/notifications/lib/notificationService.js +4 -5
- package/dist/modules/notifications/lib/notificationService.js.map +2 -2
- package/dist/modules/notifications/subscribers/deliver-notification.js +8 -13
- package/dist/modules/notifications/subscribers/deliver-notification.js.map +2 -2
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js +3 -1
- package/dist/modules/payment_gateways/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/perspectives/services/perspectiveService.js +3 -3
- package/dist/modules/perspectives/services/perspectiveService.js.map +2 -2
- package/dist/modules/planner/api/access.js +3 -3
- package/dist/modules/planner/api/access.js.map +2 -2
- package/dist/modules/planner/api/availability-date-specific.js +3 -1
- package/dist/modules/planner/api/availability-date-specific.js.map +2 -2
- package/dist/modules/planner/api/availability-weekly.js +3 -1
- package/dist/modules/planner/api/availability-weekly.js.map +2 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js +4 -2
- package/dist/modules/planner/backend/planner/availability-rulesets/page.js.map +2 -2
- package/dist/modules/planner/components/AvailabilityRulesEditor.js +3 -1
- package/dist/modules/planner/components/AvailabilityRulesEditor.js.map +2 -2
- package/dist/modules/progress/api/jobs/route.js +3 -1
- package/dist/modules/progress/api/jobs/route.js.map +2 -2
- package/dist/modules/query_index/components/QueryIndexesTable.js +5 -3
- package/dist/modules/query_index/components/QueryIndexesTable.js.map +2 -2
- package/dist/modules/query_index/lib/batch.js +4 -2
- package/dist/modules/query_index/lib/batch.js.map +2 -2
- package/dist/modules/query_index/lib/coverage.js +3 -1
- package/dist/modules/query_index/lib/coverage.js.map +2 -2
- package/dist/modules/query_index/lib/engine.js +13 -18
- package/dist/modules/query_index/lib/engine.js.map +2 -2
- package/dist/modules/query_index/lib/reindexer.js +8 -6
- package/dist/modules/query_index/lib/reindexer.js.map +2 -2
- package/dist/modules/query_index/lib/search-tokens.js +3 -1
- package/dist/modules/query_index/lib/search-tokens.js.map +2 -2
- package/dist/modules/query_index/subscribers/coverage_refresh.js +3 -1
- package/dist/modules/query_index/subscribers/coverage_refresh.js.map +2 -2
- package/dist/modules/resources/api/comments.js +3 -1
- package/dist/modules/resources/api/comments.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/assign/route.js +3 -1
- package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
- package/dist/modules/resources/api/resources/tags/unassign/route.js +3 -1
- package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js +3 -1
- package/dist/modules/resources/backend/resources/resource-types/[id]/edit/page.js.map +2 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js +4 -2
- package/dist/modules/resources/backend/resources/resource-types/page.js.map +2 -2
- package/dist/modules/resources/commands/activities.js +6 -5
- package/dist/modules/resources/commands/activities.js.map +2 -2
- package/dist/modules/resources/commands/comments.js +6 -4
- package/dist/modules/resources/commands/comments.js.map +2 -2
- package/dist/modules/resources/commands/shared.js +24 -1
- package/dist/modules/resources/commands/shared.js.map +2 -2
- package/dist/modules/sales/api/channels/route.js +3 -1
- package/dist/modules/sales/api/channels/route.js.map +2 -2
- package/dist/modules/sales/api/document-history/route.js +3 -1
- package/dist/modules/sales/api/document-history/route.js.map +2 -2
- package/dist/modules/sales/api/document-numbers/route.js +3 -1
- package/dist/modules/sales/api/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/accept/route.js +4 -2
- package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/convert/route.js +3 -1
- package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/public/[token]/route.js +3 -1
- package/dist/modules/sales/api/quotes/public/[token]/route.js.map +2 -2
- package/dist/modules/sales/api/quotes/send/route.js +3 -1
- package/dist/modules/sales/api/quotes/send/route.js.map +2 -2
- package/dist/modules/sales/api/returns/[id]/route.js +3 -1
- package/dist/modules/sales/api/returns/[id]/route.js.map +2 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js +4 -2
- package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
- package/dist/modules/sales/api/settings/order-editing/route.js +4 -2
- package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js +3 -1
- package/dist/modules/sales/backend/sales/channels/[channelId]/edit/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/offers/page.js +6 -4
- package/dist/modules/sales/backend/sales/channels/offers/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/channels/page.js +4 -2
- package/dist/modules/sales/backend/sales/channels/page.js.map +2 -2
- package/dist/modules/sales/backend/sales/documents/[id]/page.js +20 -18
- package/dist/modules/sales/backend/sales/documents/[id]/page.js.map +2 -2
- package/dist/modules/sales/commands/documents.js +4 -8
- package/dist/modules/sales/commands/documents.js.map +2 -2
- package/dist/modules/sales/commands/payments.js +3 -1
- package/dist/modules/sales/commands/payments.js.map +2 -2
- package/dist/modules/sales/commands/shipments.js +3 -1
- package/dist/modules/sales/commands/shipments.js.map +2 -2
- package/dist/modules/sales/components/AdjustmentKindSettings.js +5 -3
- package/dist/modules/sales/components/AdjustmentKindSettings.js.map +2 -2
- package/dist/modules/sales/components/DocumentNumberSettings.js +4 -2
- package/dist/modules/sales/components/DocumentNumberSettings.js.map +2 -2
- package/dist/modules/sales/components/OrderEditingSettings.js +4 -2
- package/dist/modules/sales/components/OrderEditingSettings.js.map +2 -2
- package/dist/modules/sales/components/PaymentMethodsSettings.js +5 -3
- package/dist/modules/sales/components/PaymentMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/ShippingMethodsSettings.js +5 -3
- package/dist/modules/sales/components/ShippingMethodsSettings.js.map +2 -2
- package/dist/modules/sales/components/StatusSettings.js +5 -3
- package/dist/modules/sales/components/StatusSettings.js.map +2 -2
- package/dist/modules/sales/components/TaxRatesSettings.js +5 -3
- package/dist/modules/sales/components/TaxRatesSettings.js.map +2 -2
- package/dist/modules/sales/components/channels/ChannelOfferForm.js +16 -14
- package/dist/modules/sales/components/channels/ChannelOfferForm.js.map +2 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js +4 -2
- package/dist/modules/sales/components/channels/SalesChannelOffersPanel.js.map +2 -2
- package/dist/modules/sales/components/documents/AddressesSection.js +5 -3
- package/dist/modules/sales/components/documents/AddressesSection.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentDialog.js +3 -1
- package/dist/modules/sales/components/documents/AdjustmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/AdjustmentsSection.js +5 -3
- package/dist/modules/sales/components/documents/AdjustmentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/ItemsSection.js +6 -4
- package/dist/modules/sales/components/documents/ItemsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/LineItemDialog.js +8 -6
- package/dist/modules/sales/components/documents/LineItemDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentDialog.js +5 -3
- package/dist/modules/sales/components/documents/PaymentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js +4 -2
- package/dist/modules/sales/components/documents/PaymentsSection.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentForm.js +14 -12
- package/dist/modules/sales/components/documents/SalesDocumentForm.js.map +2 -2
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js +5 -3
- package/dist/modules/sales/components/documents/SalesDocumentsTable.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentDialog.js +9 -7
- package/dist/modules/sales/components/documents/ShipmentDialog.js.map +2 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js +4 -2
- package/dist/modules/sales/components/documents/ShipmentsSection.js.map +2 -2
- package/dist/modules/sales/frontend/quote/[token]/page.js +4 -2
- package/dist/modules/sales/frontend/quote/[token]/page.js.map +2 -2
- package/dist/modules/sales/seed/examples.js +7 -9
- package/dist/modules/sales/seed/examples.js.map +2 -2
- package/dist/modules/sales/subscribers/quote-expiring-notification.js +3 -1
- package/dist/modules/sales/subscribers/quote-expiring-notification.js.map +2 -2
- package/dist/modules/sales/subscribers/reconcileOnAddressDelete.js +3 -1
- package/dist/modules/sales/subscribers/reconcileOnAddressDelete.js.map +2 -2
- package/dist/modules/sales/subscribers/reconcileOnCompanyDelete.js +3 -1
- package/dist/modules/sales/subscribers/reconcileOnCompanyDelete.js.map +2 -2
- package/dist/modules/sales/subscribers/reconcileOnCustomerDelete.js +3 -1
- package/dist/modules/sales/subscribers/reconcileOnCustomerDelete.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js +5 -3
- package/dist/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js +3 -1
- package/dist/modules/sales/widgets/dashboard/new-orders/widget.client.js.map +2 -2
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js +3 -1
- package/dist/modules/sales/widgets/dashboard/new-quotes/widget.client.js.map +2 -2
- package/dist/modules/shipping_carriers/api/tracking/route.js +5 -0
- package/dist/modules/shipping_carriers/api/tracking/route.js.map +2 -2
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js +3 -1
- package/dist/modules/shipping_carriers/api/webhook/[provider]/route.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/shipping-service.js +41 -19
- package/dist/modules/shipping_carriers/lib/shipping-service.js.map +2 -2
- package/dist/modules/shipping_carriers/lib/upstream-error-response.js +3 -1
- package/dist/modules/shipping_carriers/lib/upstream-error-response.js.map +2 -2
- package/dist/modules/shipping_carriers/workers/webhook-processor.js +3 -5
- package/dist/modules/shipping_carriers/workers/webhook-processor.js.map +2 -2
- package/dist/modules/staff/api/comments.js +3 -1
- package/dist/modules/staff/api/comments.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/accept/route.js +3 -1
- package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
- package/dist/modules/staff/api/leave-requests/reject/route.js +3 -1
- package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/assignable/route.js +3 -1
- package/dist/modules/staff/api/team-members/assignable/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/self/route.js +4 -2
- package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/assign/route.js +3 -1
- package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
- package/dist/modules/staff/api/team-members/tags/unassign/route.js +3 -1
- package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js +3 -1
- package/dist/modules/staff/api/timesheets/my-projects/[projectId]/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/my-projects/route.js +3 -1
- package/dist/modules/staff/api/timesheets/my-projects/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js +3 -1
- package/dist/modules/staff/api/timesheets/projects/kpis/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js +3 -1
- package/dist/modules/staff/api/timesheets/time-entries/[id]/segments/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js +4 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js +4 -2
- package/dist/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js +3 -1
- package/dist/modules/staff/api/timesheets/time-entries/bulk/route.js.map +2 -2
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +3 -1
- package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-members/page.js +4 -2
- package/dist/modules/staff/backend/staff/team-members/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js +4 -2
- package/dist/modules/staff/backend/staff/team-roles/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js +4 -2
- package/dist/modules/staff/backend/staff/teams/[id]/edit/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/teams/page.js +4 -2
- package/dist/modules/staff/backend/staff/teams/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/page.js +7 -5
- package/dist/modules/staff/backend/staff/timesheets/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js +3 -1
- package/dist/modules/staff/backend/staff/timesheets/projects/[id]/page.js.map +2 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js +4 -2
- package/dist/modules/staff/backend/staff/timesheets/projects/page.js.map +2 -2
- package/dist/modules/staff/commands/timesheets-entries.js +3 -1
- package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js +3 -1
- package/dist/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.js.map +2 -2
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js +5 -3
- package/dist/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.js.map +2 -2
- package/dist/modules/sync_excel/api/import/route.js +3 -1
- package/dist/modules/sync_excel/api/import/route.js.map +2 -2
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js +5 -3
- package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
- package/dist/modules/translations/api/get/locales.js +3 -1
- package/dist/modules/translations/api/get/locales.js.map +2 -2
- package/dist/modules/translations/api/put/locales.js +3 -1
- package/dist/modules/translations/api/put/locales.js.map +2 -2
- package/dist/modules/translations/components/TranslationManager.js +3 -1
- package/dist/modules/translations/components/TranslationManager.js.map +2 -2
- package/dist/modules/translations/subscribers/cleanup.js +3 -1
- package/dist/modules/translations/subscribers/cleanup.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js +4 -2
- package/dist/modules/workflows/api/definitions/[id]/customize/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js +4 -2
- package/dist/modules/workflows/api/definitions/[id]/reset-to-code/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/[id]/route.js +6 -4
- package/dist/modules/workflows/api/definitions/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/definitions/route.js +4 -2
- package/dist/modules/workflows/api/definitions/route.js.map +2 -2
- package/dist/modules/workflows/api/events/[id]/route.js +3 -1
- package/dist/modules/workflows/api/events/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/events/route.js +3 -1
- package/dist/modules/workflows/api/events/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/advance/route.js +3 -1
- package/dist/modules/workflows/api/instances/[id]/advance/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/cancel/route.js +3 -1
- package/dist/modules/workflows/api/instances/[id]/cancel/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/events/route.js +3 -1
- package/dist/modules/workflows/api/instances/[id]/events/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/retry/route.js +3 -1
- package/dist/modules/workflows/api/instances/[id]/retry/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/route.js +3 -1
- package/dist/modules/workflows/api/instances/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/[id]/signal/route.js +5 -7
- package/dist/modules/workflows/api/instances/[id]/signal/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/route.js +5 -3
- package/dist/modules/workflows/api/instances/route.js.map +2 -2
- package/dist/modules/workflows/api/instances/validate-start/route.js +3 -1
- package/dist/modules/workflows/api/instances/validate-start/route.js.map +2 -2
- package/dist/modules/workflows/api/signals/route.js +3 -1
- package/dist/modules/workflows/api/signals/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/claim/route.js +3 -1
- package/dist/modules/workflows/api/tasks/[id]/claim/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/complete/route.js +3 -1
- package/dist/modules/workflows/api/tasks/[id]/complete/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/[id]/route.js +3 -1
- package/dist/modules/workflows/api/tasks/[id]/route.js.map +2 -2
- package/dist/modules/workflows/api/tasks/route.js +3 -1
- package/dist/modules/workflows/api/tasks/route.js.map +2 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js +4 -2
- package/dist/modules/workflows/backend/definitions/visual-editor/page.js.map +2 -2
- package/dist/modules/workflows/backend/instances/[id]/page.js +5 -3
- package/dist/modules/workflows/backend/instances/[id]/page.js.map +2 -2
- package/dist/modules/workflows/backend/tasks/[id]/page.js +3 -1
- package/dist/modules/workflows/backend/tasks/[id]/page.js.map +2 -2
- package/dist/modules/workflows/components/BusinessRulesSelector.js +5 -3
- package/dist/modules/workflows/components/BusinessRulesSelector.js.map +2 -2
- package/dist/modules/workflows/components/WorkflowSelector.js +5 -3
- package/dist/modules/workflows/components/WorkflowSelector.js.map +2 -2
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js +3 -1
- package/dist/modules/workflows/components/fields/BusinessRuleConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js +3 -1
- package/dist/modules/workflows/components/fields/StartPreConditionsEditor.js.map +2 -2
- package/dist/modules/workflows/components/fields/WorkflowSelectorField.js +3 -1
- package/dist/modules/workflows/components/fields/WorkflowSelectorField.js.map +2 -2
- package/dist/modules/workflows/frontend/checkout-demo/page.js +8 -7
- package/dist/modules/workflows/frontend/checkout-demo/page.js.map +2 -2
- package/dist/modules/workflows/lib/activity-executor.js +25 -21
- package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
- package/dist/modules/workflows/lib/activity-worker-handler.js +8 -15
- package/dist/modules/workflows/lib/activity-worker-handler.js.map +2 -2
- package/dist/modules/workflows/lib/code-registry.js +7 -7
- package/dist/modules/workflows/lib/code-registry.js.map +2 -2
- package/dist/modules/workflows/lib/compensation-handler.js +4 -2
- package/dist/modules/workflows/lib/compensation-handler.js.map +2 -2
- package/dist/modules/workflows/lib/edgeFormTransforms.js +3 -1
- package/dist/modules/workflows/lib/edgeFormTransforms.js.map +2 -2
- package/dist/modules/workflows/lib/event-trigger-service.js +5 -3
- package/dist/modules/workflows/lib/event-trigger-service.js.map +2 -2
- package/dist/modules/workflows/lib/nodeFormTransforms.js +3 -1
- package/dist/modules/workflows/lib/nodeFormTransforms.js.map +2 -2
- package/dist/modules/workflows/lib/seeds.js +16 -2
- package/dist/modules/workflows/lib/seeds.js.map +2 -2
- package/dist/modules/workflows/lib/signal-handler.js +3 -1
- package/dist/modules/workflows/lib/signal-handler.js.map +2 -2
- package/dist/modules/workflows/lib/start-validator.js +9 -3
- package/dist/modules/workflows/lib/start-validator.js.map +2 -2
- package/dist/modules/workflows/lib/step-handler.js +3 -1
- package/dist/modules/workflows/lib/step-handler.js.map +2 -2
- package/dist/modules/workflows/lib/task-handler.js +3 -1
- package/dist/modules/workflows/lib/task-handler.js.map +2 -2
- package/dist/modules/workflows/lib/transition-handler.js +5 -3
- package/dist/modules/workflows/lib/transition-handler.js.map +2 -2
- package/dist/modules/workflows/lib/workflow-executor.js +27 -15
- package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
- package/dist/modules/workflows/subscribers/event-trigger.js +21 -18
- package/dist/modules/workflows/subscribers/event-trigger.js.map +2 -2
- package/dist/modules/workflows/subscribers/task-assigned-notification.js +3 -1
- package/dist/modules/workflows/subscribers/task-assigned-notification.js.map +2 -2
- package/dist/modules/workflows/workers/workflow-activities.worker.js +30 -20
- package/dist/modules/workflows/workers/workflow-activities.worker.js.map +2 -2
- package/package.json +7 -7
- package/src/bootstrap.ts +14 -11
- package/src/helpers/integration/queue-runner.ts +2 -2
- package/src/modules/api_keys/services/apiKeyService.ts +6 -3
- package/src/modules/attachments/api/image/[id]/[[...slug]]/route.ts +5 -2
- package/src/modules/attachments/api/library/[id]/route.ts +4 -1
- package/src/modules/attachments/api/route.ts +9 -6
- package/src/modules/attachments/components/AttachmentPartitionSettings.tsx +6 -3
- package/src/modules/attachments/lib/assignmentDetails.ts +4 -1
- package/src/modules/attachments/lib/drivers/driverFactory.ts +4 -1
- package/src/modules/attachments/lib/ocrQueue.ts +11 -11
- package/src/modules/attachments/lib/ocrService.ts +7 -4
- package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +4 -1
- package/src/modules/audit_logs/api/audit-logs/actions/undo/route.ts +4 -1
- package/src/modules/audit_logs/backend/audit-logs/page.tsx +4 -1
- package/src/modules/audit_logs/components/AuditLogsActions.tsx +5 -2
- package/src/modules/audit_logs/services/accessLogService.ts +5 -4
- package/src/modules/audit_logs/services/actionLogService.ts +5 -2
- package/src/modules/auth/api/profile/route.ts +5 -2
- package/src/modules/auth/api/reset/confirm.ts +4 -1
- package/src/modules/auth/api/reset.ts +5 -2
- package/src/modules/auth/api/roles/route.ts +4 -1
- package/src/modules/auth/api/sidebar/variants/route.ts +4 -2
- package/src/modules/auth/api/users/resend-invite/route.ts +5 -2
- package/src/modules/auth/api/users/route.ts +4 -1
- package/src/modules/auth/backend/auth/profile/page.tsx +4 -1
- package/src/modules/auth/backend/profile/change-password/page.tsx +4 -1
- package/src/modules/auth/backend/users/[id]/edit/page.tsx +6 -3
- package/src/modules/auth/backend/users/create/page.tsx +5 -2
- package/src/modules/auth/cli.ts +10 -0
- package/src/modules/auth/commands/users.ts +5 -2
- package/src/modules/auth/lib/consentIntegrity.ts +4 -4
- package/src/modules/auth/lib/setup-app.ts +10 -10
- package/src/modules/auth/lib/tenantAccess.ts +4 -1
- package/src/modules/auth/lib/tokenHash.ts +4 -4
- package/src/modules/business_rules/api/rules/route.ts +5 -2
- package/src/modules/business_rules/lib/expression-evaluator.ts +8 -6
- package/src/modules/business_rules/lib/rule-engine.ts +6 -3
- package/src/modules/business_rules/lib/rule-evaluator.ts +11 -11
- package/src/modules/business_rules/subscribers/crud-rule-trigger.ts +4 -1
- package/src/modules/business_rules/subscribers/rule-execution-failed-notification.ts +4 -1
- package/src/modules/catalog/ai-agents-context.ts +5 -5
- package/src/modules/catalog/ai-tools/_shared.ts +4 -1
- package/src/modules/catalog/ai-tools/authoring-pack.ts +4 -1
- package/src/modules/catalog/ai-tools/merchandising-pack.ts +4 -1
- package/src/modules/catalog/api/dictionaries/[key]/route.ts +4 -1
- package/src/modules/catalog/api/prices/route.ts +4 -1
- package/src/modules/catalog/api/products/route.ts +6 -3
- package/src/modules/catalog/api/settings/route.ts +5 -2
- package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +15 -12
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/[variantId]/page.tsx +9 -6
- package/src/modules/catalog/backend/catalog/products/[productId]/variants/create/page.tsx +6 -3
- package/src/modules/catalog/backend/catalog/products/create/page.tsx +6 -6
- package/src/modules/catalog/backend/catalog/products/optionSchemaClient.ts +4 -1
- package/src/modules/catalog/components/PriceKindSettings.tsx +6 -3
- package/src/modules/catalog/components/UnitPriceDisplaySettings.tsx +5 -2
- package/src/modules/catalog/inbox-actions.ts +5 -2
- package/src/modules/catalog/seed/examples.ts +5 -5
- package/src/modules/catalog/subscribers/low-stock-notification.ts +4 -1
- package/src/modules/catalog/widgets/injection/product-seo/state.ts +4 -1
- package/src/modules/communication_channels/api/get/oauth/[provider]/callback/route.ts +13 -13
- package/src/modules/communication_channels/api/post/channels/[id]/import-history/route.ts +4 -1
- package/src/modules/communication_channels/api/post/channels/[id]/push/register/route.ts +4 -1
- package/src/modules/communication_channels/api/post/webhook/[provider]/route.ts +6 -3
- package/src/modules/communication_channels/api/post/webhooks/gmail/route.ts +7 -6
- package/src/modules/communication_channels/commands/connect-credential-channel.ts +5 -10
- package/src/modules/communication_channels/commands/delete-channel.ts +4 -5
- package/src/modules/communication_channels/commands/deliver-outbound-message.ts +5 -5
- package/src/modules/communication_channels/commands/disconnect-channel.ts +4 -5
- package/src/modules/communication_channels/commands/ingest-inbound-message.ts +6 -12
- package/src/modules/communication_channels/commands/push-unregister.ts +4 -3
- package/src/modules/communication_channels/commands/reassign-conversation.ts +6 -3
- package/src/modules/communication_channels/lib/dead-letter.ts +4 -5
- package/src/modules/communication_channels/lib/email-mime.ts +5 -6
- package/src/modules/communication_channels/lib/oauth-client-config.ts +7 -3
- package/src/modules/communication_channels/lib/send-as-user.ts +4 -1
- package/src/modules/communication_channels/lib/thread-token.ts +5 -2
- package/src/modules/communication_channels/setup.ts +4 -4
- package/src/modules/communication_channels/workers/channel-import-history.ts +5 -8
- package/src/modules/communication_channels/workers/gmail-history-sync.ts +9 -18
- package/src/modules/communication_channels/workers/gmail-renew-watch.ts +6 -9
- package/src/modules/communication_channels/workers/outbound-delivery.ts +7 -12
- package/src/modules/communication_channels/workers/poll-channel.ts +8 -15
- package/src/modules/communication_channels/workers/poll-tick.ts +6 -6
- package/src/modules/communication_channels/workers/reaction-processor.ts +6 -9
- package/src/modules/configs/api/cache/route.ts +5 -2
- package/src/modules/configs/api/system-status/route.ts +5 -2
- package/src/modules/configs/api/upgrade-actions/route.ts +5 -2
- package/src/modules/configs/components/ModuleTelemetryPanel.tsx +4 -1
- package/src/modules/configs/lib/touchGeneratedBarrels.ts +1 -1
- package/src/modules/configs/services/upgradeActionsService.ts +7 -3
- package/src/modules/currencies/components/CurrencyFetchingConfig.tsx +4 -1
- package/src/modules/currencies/lib/exchangeRateFormConfig.ts +4 -1
- package/src/modules/currencies/services/providers/nbp.ts +7 -4
- package/src/modules/currencies/services/providers/raiffeisen.ts +8 -7
- package/src/modules/customer_accounts/api/domain-resolve/all.ts +7 -5
- package/src/modules/customer_accounts/api/signup.ts +5 -2
- package/src/modules/customer_accounts/lib/customerAuthServer.ts +4 -1
- package/src/modules/customer_accounts/subscribers/autoLinkCrm.ts +4 -1
- package/src/modules/customer_accounts/subscribers/autoLinkCrmReverse.ts +4 -1
- package/src/modules/customer_accounts/subscribers/unlinkOnCompanyDelete.ts +4 -1
- package/src/modules/customer_accounts/subscribers/unlinkOnPersonDelete.ts +4 -1
- package/src/modules/customers/ai-agents-context.ts +5 -5
- package/src/modules/customers/api/activities/route.ts +7 -4
- package/src/modules/customers/api/comments/route.ts +4 -1
- package/src/modules/customers/api/companies/[id]/people/route.ts +4 -1
- package/src/modules/customers/api/companies/[id]/route.ts +6 -3
- package/src/modules/customers/api/companies/route.ts +6 -3
- package/src/modules/customers/api/dashboard/widgets/customer-todos/route.ts +4 -1
- package/src/modules/customers/api/dashboard/widgets/new-customers/route.ts +4 -1
- package/src/modules/customers/api/dashboard/widgets/new-deals/route.ts +4 -1
- package/src/modules/customers/api/dashboard/widgets/next-interactions/route.ts +4 -1
- 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/aggregate/route.ts +4 -1
- package/src/modules/customers/api/deals/bulk-update-owner/route.ts +8 -5
- package/src/modules/customers/api/deals/bulk-update-stage/route.ts +8 -5
- package/src/modules/customers/api/deals/route.ts +4 -1
- package/src/modules/customers/api/deals/summary/route.ts +4 -1
- package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +5 -2
- package/src/modules/customers/api/dictionaries/[kind]/route.ts +6 -3
- package/src/modules/customers/api/dictionaries/cache.ts +4 -1
- package/src/modules/customers/api/dictionaries/context.ts +4 -1
- package/src/modules/customers/api/dictionaries/currency/route.ts +4 -1
- package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -2
- package/src/modules/customers/api/entity-roles-factory.ts +8 -5
- package/src/modules/customers/api/interactions/cancel/route.ts +4 -1
- package/src/modules/customers/api/interactions/complete/route.ts +4 -1
- package/src/modules/customers/api/interactions/conflicts/route.ts +4 -1
- package/src/modules/customers/api/interactions/counts/route.ts +4 -1
- package/src/modules/customers/api/interactions/route.ts +8 -4
- package/src/modules/customers/api/interactions/tasks/route.ts +4 -1
- package/src/modules/customers/api/labels/assign/route.ts +4 -1
- package/src/modules/customers/api/labels/route.ts +7 -3
- package/src/modules/customers/api/labels/unassign/route.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 +6 -3
- package/src/modules/customers/api/people/route.ts +6 -3
- package/src/modules/customers/api/pipeline-stages/reorder/route.ts +4 -1
- package/src/modules/customers/api/pipeline-stages/route.ts +7 -4
- package/src/modules/customers/api/pipelines/route.ts +7 -4
- package/src/modules/customers/api/settings/address-format/route.ts +5 -2
- package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -2
- package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -2
- package/src/modules/customers/api/tags/assign/route.ts +4 -1
- package/src/modules/customers/api/tags/unassign/route.ts +4 -1
- package/src/modules/customers/api/todos/route.ts +7 -4
- package/src/modules/customers/backend/customers/companies-v2/[id]/page.tsx +6 -3
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealActivities.ts +4 -1
- package/src/modules/customers/backend/customers/deals/[id]/hooks/useDealClosure.ts +4 -1
- package/src/modules/customers/backend/customers/deals/map/components/DealsMapCanvasImpl.tsx +5 -3
- package/src/modules/customers/backend/customers/deals/page.tsx +5 -2
- package/src/modules/customers/backend/customers/people-v2/[id]/page.tsx +6 -3
- package/src/modules/customers/commands/deals.ts +4 -1
- package/src/modules/customers/commands/dictionaries.ts +4 -1
- package/src/modules/customers/commands/interactions.ts +5 -2
- package/src/modules/customers/commands/shared.ts +5 -2
- package/src/modules/customers/commands/todos.ts +4 -1
- package/src/modules/customers/components/DictionarySettings.tsx +6 -3
- package/src/modules/customers/components/DictionarySortSettings.tsx +4 -1
- package/src/modules/customers/components/detail/ActivitiesCard.tsx +4 -1
- package/src/modules/customers/components/detail/ActivitiesDayStrip.tsx +4 -1
- package/src/modules/customers/components/detail/ActivitiesSection.tsx +7 -4
- package/src/modules/customers/components/detail/ActivityCard.tsx +4 -1
- package/src/modules/customers/components/detail/ChangelogTab.tsx +4 -1
- package/src/modules/customers/components/detail/CompanyCard.tsx +4 -1
- package/src/modules/customers/components/detail/ConfirmDealLostDialog.tsx +4 -1
- package/src/modules/customers/components/detail/EntityTagsDialog.tsx +4 -1
- package/src/modules/customers/components/detail/InlineActivityComposer.tsx +4 -1
- package/src/modules/customers/components/detail/ManageTagsDialog.tsx +4 -1
- package/src/modules/customers/components/detail/RoleAssignmentRow.tsx +6 -3
- package/src/modules/customers/components/detail/RolesSection.tsx +5 -2
- package/src/modules/customers/components/detail/hooks/useInteractionMutations.ts +6 -3
- package/src/modules/customers/components/linking/LinkEntityDialog.tsx +5 -2
- package/src/modules/customers/inbox-actions.ts +4 -1
- package/src/modules/customers/lib/bulkDeals.ts +4 -1
- package/src/modules/customers/lib/dealClosureNotification.ts +5 -2
- package/src/modules/customers/lib/dealStageTransitionTable.ts +4 -1
- package/src/modules/customers/lib/interactionFeatureFlags.ts +5 -1
- package/src/modules/customers/lib/todoCompatibility.ts +4 -1
- package/src/modules/customers/search.ts +15 -7
- package/src/modules/customers/widgets/dashboard/customer-todos/widget.client.tsx +4 -1
- package/src/modules/customers/widgets/dashboard/new-customers/widget.client.tsx +4 -1
- package/src/modules/customers/widgets/dashboard/new-deals/widget.client.tsx +4 -1
- package/src/modules/customers/widgets/dashboard/next-interactions/widget.client.tsx +4 -1
- package/src/modules/dashboards/api/widgets/data/batch/route.ts +4 -1
- package/src/modules/dashboards/api/widgets/data/route.ts +4 -1
- package/src/modules/dashboards/components/WidgetVisibilityEditor.tsx +6 -3
- package/src/modules/dashboards/widgets/dashboard/aov-kpi/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/new-customers-kpi/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/orders-by-status/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/orders-kpi/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/pipeline-summary/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/revenue-kpi/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/revenue-trend/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/sales-by-region/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/top-customers/widget.client.tsx +4 -1
- package/src/modules/dashboards/widgets/dashboard/top-products/widget.client.tsx +4 -1
- package/src/modules/data_sync/api/run.ts +4 -1
- package/src/modules/data_sync/backend/data-sync/runs/[id]/page.tsx +3 -1
- package/src/modules/data_sync/lib/adapter-registry.ts +16 -4
- package/src/modules/data_sync/lib/sync-engine.ts +31 -2
- package/src/modules/data_sync/workers/sync-export.ts +4 -1
- package/src/modules/data_sync/workers/sync-import.ts +4 -1
- package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +5 -2
- package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +4 -1
- package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -2
- package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +4 -1
- package/src/modules/dictionaries/api/[dictionaryId]/route.ts +6 -3
- package/src/modules/dictionaries/api/context.ts +4 -1
- package/src/modules/dictionaries/api/route.ts +5 -2
- package/src/modules/dictionaries/components/DictionariesManager.tsx +6 -3
- package/src/modules/dictionaries/components/DictionaryEntriesEditor.tsx +5 -2
- package/src/modules/dictionaries/components/DictionaryEntrySelect.tsx +5 -2
- package/src/modules/dictionaries/components/DictionarySelectControl.tsx +4 -1
- package/src/modules/dictionaries/fields/dictionary.tsx +4 -1
- package/src/modules/directory/api/organization-branding/route.ts +4 -1
- package/src/modules/directory/api/organization-switcher/route.ts +4 -1
- package/src/modules/directory/commands/tenants.ts +6 -3
- package/src/modules/directory/utils/organizationScope.ts +7 -4
- package/src/modules/entities/api/definitions.cache.ts +4 -1
- package/src/modules/entities/api/definitions.ts +8 -5
- package/src/modules/entities/api/records.ts +5 -2
- package/src/modules/entities/components/EncryptionManager.tsx +4 -1
- package/src/modules/entities/lib/makeActivityRoute.ts +4 -1
- package/src/modules/feature_toggles/api/overrides/route.ts +4 -1
- package/src/modules/feature_toggles/lib/feature-flag-check.ts +6 -9
- package/src/modules/inbox_ops/api/emails/[id]/reprocess/route.ts +5 -2
- package/src/modules/inbox_ops/api/emails/[id]/route.ts +5 -2
- package/src/modules/inbox_ops/api/emails/route.ts +4 -1
- package/src/modules/inbox_ops/api/extract/route.ts +4 -1
- package/src/modules/inbox_ops/api/proposals/[id]/actions/[actionId]/route.ts +4 -1
- package/src/modules/inbox_ops/api/proposals/[id]/replies/[replyId]/send/route.ts +5 -2
- package/src/modules/inbox_ops/api/proposals/counts/route.ts +5 -2
- package/src/modules/inbox_ops/api/proposals/route.ts +4 -1
- package/src/modules/inbox_ops/api/routeHelpers.ts +4 -1
- package/src/modules/inbox_ops/api/settings/route.ts +4 -1
- package/src/modules/inbox_ops/api/webhook/inbound.ts +8 -5
- package/src/modules/inbox_ops/lib/cache.ts +5 -2
- package/src/modules/inbox_ops/lib/catalogLookup.ts +4 -1
- package/src/modules/inbox_ops/lib/eventBus.ts +4 -4
- package/src/modules/inbox_ops/lib/messagesIntegration.ts +5 -8
- package/src/modules/inbox_ops/subscribers/executionAuditor.ts +5 -1
- package/src/modules/inbox_ops/subscribers/extractionWorker.ts +11 -8
- package/src/modules/inbox_ops/subscribers/proposalNotifier.ts +4 -1
- package/src/modules/messages/api/[id]/route.ts +4 -4
- package/src/modules/messages/commands/actions.ts +4 -1
- package/src/modules/messages/components/MessagesInboxPageClient.tsx +4 -1
- package/src/modules/messages/lib/email-sender.ts +4 -7
- package/src/modules/messages/lib/message-objects-registry.ts +4 -1
- package/src/modules/messages/lib/message-types-registry.ts +4 -1
- package/src/modules/messages/workers/send-email.worker.ts +7 -4
- package/src/modules/notifications/lib/notificationMapper.ts +4 -4
- package/src/modules/notifications/lib/notificationService.ts +4 -5
- package/src/modules/notifications/subscribers/deliver-notification.ts +9 -14
- package/src/modules/payment_gateways/api/webhook/[provider]/route.ts +4 -1
- package/src/modules/perspectives/services/perspectiveService.ts +4 -3
- package/src/modules/planner/api/access.ts +4 -3
- package/src/modules/planner/api/availability-date-specific.ts +4 -1
- package/src/modules/planner/api/availability-weekly.ts +4 -1
- package/src/modules/planner/backend/planner/availability-rulesets/page.tsx +5 -2
- package/src/modules/planner/components/AvailabilityRulesEditor.tsx +4 -1
- package/src/modules/progress/api/jobs/route.ts +4 -1
- package/src/modules/query_index/components/QueryIndexesTable.tsx +6 -3
- package/src/modules/query_index/lib/batch.ts +5 -2
- package/src/modules/query_index/lib/coverage.ts +4 -1
- package/src/modules/query_index/lib/engine.ts +14 -17
- package/src/modules/query_index/lib/reindexer.ts +9 -6
- package/src/modules/query_index/lib/search-tokens.ts +4 -2
- package/src/modules/query_index/subscribers/coverage_refresh.ts +4 -1
- package/src/modules/resources/api/comments.ts +4 -1
- package/src/modules/resources/api/resources/tags/assign/route.ts +4 -1
- package/src/modules/resources/api/resources/tags/unassign/route.ts +4 -1
- package/src/modules/resources/backend/resources/resource-types/[id]/edit/page.tsx +4 -1
- package/src/modules/resources/backend/resources/resource-types/page.tsx +5 -2
- package/src/modules/resources/commands/activities.ts +5 -4
- package/src/modules/resources/commands/comments.ts +6 -4
- package/src/modules/resources/commands/shared.ts +36 -0
- package/src/modules/sales/api/channels/route.ts +4 -1
- package/src/modules/sales/api/document-history/route.ts +4 -1
- package/src/modules/sales/api/document-numbers/route.ts +4 -1
- package/src/modules/sales/api/quotes/accept/route.ts +5 -2
- package/src/modules/sales/api/quotes/convert/route.ts +4 -1
- package/src/modules/sales/api/quotes/public/[token]/route.ts +4 -1
- package/src/modules/sales/api/quotes/send/route.ts +4 -1
- package/src/modules/sales/api/returns/[id]/route.ts +4 -1
- package/src/modules/sales/api/settings/document-numbers/route.ts +5 -2
- package/src/modules/sales/api/settings/order-editing/route.ts +5 -2
- package/src/modules/sales/backend/sales/channels/[channelId]/edit/page.tsx +4 -1
- package/src/modules/sales/backend/sales/channels/offers/page.tsx +7 -4
- package/src/modules/sales/backend/sales/channels/page.tsx +5 -2
- package/src/modules/sales/backend/sales/documents/[id]/page.tsx +21 -18
- package/src/modules/sales/commands/documents.ts +5 -8
- package/src/modules/sales/commands/payments.ts +4 -1
- package/src/modules/sales/commands/shipments.ts +4 -2
- package/src/modules/sales/components/AdjustmentKindSettings.tsx +6 -3
- package/src/modules/sales/components/DocumentNumberSettings.tsx +5 -2
- package/src/modules/sales/components/OrderEditingSettings.tsx +5 -2
- package/src/modules/sales/components/PaymentMethodsSettings.tsx +6 -3
- package/src/modules/sales/components/ShippingMethodsSettings.tsx +6 -3
- package/src/modules/sales/components/StatusSettings.tsx +6 -3
- package/src/modules/sales/components/TaxRatesSettings.tsx +6 -3
- package/src/modules/sales/components/channels/ChannelOfferForm.tsx +17 -14
- package/src/modules/sales/components/channels/SalesChannelOffersPanel.tsx +5 -2
- package/src/modules/sales/components/documents/AddressesSection.tsx +6 -3
- package/src/modules/sales/components/documents/AdjustmentDialog.tsx +4 -1
- package/src/modules/sales/components/documents/AdjustmentsSection.tsx +6 -3
- package/src/modules/sales/components/documents/ItemsSection.tsx +7 -4
- package/src/modules/sales/components/documents/LineItemDialog.tsx +9 -6
- package/src/modules/sales/components/documents/PaymentDialog.tsx +6 -3
- package/src/modules/sales/components/documents/PaymentsSection.tsx +5 -2
- package/src/modules/sales/components/documents/SalesDocumentForm.tsx +15 -12
- package/src/modules/sales/components/documents/SalesDocumentsTable.tsx +6 -3
- package/src/modules/sales/components/documents/ShipmentDialog.tsx +10 -7
- package/src/modules/sales/components/documents/ShipmentsSection.tsx +5 -2
- package/src/modules/sales/frontend/quote/[token]/page.tsx +5 -2
- package/src/modules/sales/seed/examples.ts +8 -9
- package/src/modules/sales/subscribers/quote-expiring-notification.ts +4 -1
- package/src/modules/sales/subscribers/reconcileOnAddressDelete.ts +4 -1
- package/src/modules/sales/subscribers/reconcileOnCompanyDelete.ts +4 -1
- package/src/modules/sales/subscribers/reconcileOnCustomerDelete.ts +4 -1
- package/src/modules/sales/widgets/dashboard/makeDashboardWidgetRoute.ts +6 -3
- package/src/modules/sales/widgets/dashboard/new-orders/widget.client.tsx +274 -271
- package/src/modules/sales/widgets/dashboard/new-quotes/widget.client.tsx +280 -277
- package/src/modules/shipping_carriers/api/tracking/route.ts +5 -1
- package/src/modules/shipping_carriers/api/webhook/[provider]/route.ts +4 -1
- package/src/modules/shipping_carriers/lib/shipping-service.ts +47 -22
- package/src/modules/shipping_carriers/lib/upstream-error-response.ts +4 -1
- package/src/modules/shipping_carriers/workers/webhook-processor.ts +4 -5
- package/src/modules/staff/api/comments.ts +4 -1
- package/src/modules/staff/api/leave-requests/accept/route.ts +4 -1
- package/src/modules/staff/api/leave-requests/reject/route.ts +4 -1
- package/src/modules/staff/api/team-members/assignable/route.ts +4 -1
- package/src/modules/staff/api/team-members/self/route.ts +5 -2
- package/src/modules/staff/api/team-members/tags/assign/route.ts +4 -1
- package/src/modules/staff/api/team-members/tags/unassign/route.ts +4 -1
- package/src/modules/staff/api/timesheets/my-projects/[projectId]/route.ts +4 -1
- package/src/modules/staff/api/timesheets/my-projects/route.ts +4 -1
- package/src/modules/staff/api/timesheets/projects/kpis/route.ts +4 -1
- package/src/modules/staff/api/timesheets/time-entries/[id]/segments/route.ts +4 -1
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-start/route.ts +5 -2
- package/src/modules/staff/api/timesheets/time-entries/[id]/timer-stop/route.ts +5 -2
- package/src/modules/staff/api/timesheets/time-entries/bulk/route.ts +4 -1
- package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +4 -1
- package/src/modules/staff/backend/staff/team-members/page.tsx +5 -2
- package/src/modules/staff/backend/staff/team-roles/page.tsx +5 -2
- package/src/modules/staff/backend/staff/teams/[id]/edit/page.tsx +5 -2
- package/src/modules/staff/backend/staff/teams/page.tsx +5 -2
- package/src/modules/staff/backend/staff/timesheets/page.tsx +8 -5
- package/src/modules/staff/backend/staff/timesheets/projects/[id]/page.tsx +4 -1
- package/src/modules/staff/backend/staff/timesheets/projects/page.tsx +5 -2
- package/src/modules/staff/commands/timesheets-entries.ts +4 -1
- package/src/modules/staff/widgets/dashboard/timesheets-hours-by-project/widget.client.tsx +4 -1
- package/src/modules/staff/widgets/dashboard/timesheets-time-reporting/widget.client.tsx +6 -3
- package/src/modules/sync_excel/api/import/route.ts +4 -1
- package/src/modules/translations/api/[entityType]/[entityId]/route.ts +6 -3
- package/src/modules/translations/api/get/locales.ts +4 -1
- package/src/modules/translations/api/put/locales.ts +4 -1
- package/src/modules/translations/components/TranslationManager.tsx +4 -1
- package/src/modules/translations/subscribers/cleanup.ts +4 -1
- package/src/modules/workflows/api/definitions/[id]/customize/route.ts +5 -2
- package/src/modules/workflows/api/definitions/[id]/reset-to-code/route.ts +5 -2
- package/src/modules/workflows/api/definitions/[id]/route.ts +7 -4
- package/src/modules/workflows/api/definitions/route.ts +5 -2
- package/src/modules/workflows/api/events/[id]/route.ts +4 -1
- package/src/modules/workflows/api/events/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/advance/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/cancel/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/events/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/retry/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/route.ts +4 -1
- package/src/modules/workflows/api/instances/[id]/signal/route.ts +6 -7
- package/src/modules/workflows/api/instances/route.ts +6 -3
- package/src/modules/workflows/api/instances/validate-start/route.ts +4 -1
- package/src/modules/workflows/api/signals/route.ts +4 -1
- package/src/modules/workflows/api/tasks/[id]/claim/route.ts +4 -1
- package/src/modules/workflows/api/tasks/[id]/complete/route.ts +4 -1
- package/src/modules/workflows/api/tasks/[id]/route.ts +4 -1
- package/src/modules/workflows/api/tasks/route.ts +4 -1
- package/src/modules/workflows/backend/definitions/visual-editor/page.tsx +5 -2
- package/src/modules/workflows/backend/instances/[id]/page.tsx +6 -3
- package/src/modules/workflows/backend/tasks/[id]/page.tsx +4 -1
- package/src/modules/workflows/components/BusinessRulesSelector.tsx +6 -3
- package/src/modules/workflows/components/WorkflowSelector.tsx +6 -3
- package/src/modules/workflows/components/fields/BusinessRuleConditionsEditor.tsx +4 -1
- package/src/modules/workflows/components/fields/StartPreConditionsEditor.tsx +4 -1
- package/src/modules/workflows/components/fields/WorkflowSelectorField.tsx +4 -1
- package/src/modules/workflows/frontend/checkout-demo/page.tsx +9 -7
- package/src/modules/workflows/lib/activity-executor.ts +26 -21
- package/src/modules/workflows/lib/activity-worker-handler.ts +9 -15
- package/src/modules/workflows/lib/code-registry.ts +8 -7
- package/src/modules/workflows/lib/compensation-handler.ts +5 -2
- package/src/modules/workflows/lib/edgeFormTransforms.ts +4 -1
- package/src/modules/workflows/lib/event-trigger-service.ts +6 -3
- package/src/modules/workflows/lib/nodeFormTransforms.ts +4 -1
- package/src/modules/workflows/lib/seeds.ts +17 -2
- package/src/modules/workflows/lib/signal-handler.ts +4 -1
- package/src/modules/workflows/lib/start-validator.ts +10 -3
- package/src/modules/workflows/lib/step-handler.ts +4 -1
- package/src/modules/workflows/lib/task-handler.ts +4 -1
- package/src/modules/workflows/lib/transition-handler.ts +6 -3
- package/src/modules/workflows/lib/workflow-executor.ts +28 -15
- package/src/modules/workflows/subscribers/event-trigger.ts +22 -24
- package/src/modules/workflows/subscribers/task-assigned-notification.ts +4 -1
- package/src/modules/workflows/workers/workflow-activities.worker.ts +31 -20
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/catalog/ai-tools/_shared.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Shared helpers for catalog AI tool packs (Phase 1 WS-C, Steps 3.10 / 3.11 / 3.12).\n *\n * Step 3.10/3.11 centralized the price-kind enumeration query used by both the\n * base tool (`catalog.list_price_kinds_base`) and the D18 spec-named tool\n * (`catalog.list_price_kinds`).\n *\n * Step 3.12 lifts the **product-bundle builder** (and the merged\n * attribute-schema resolver) here too, so the D18 AI-authoring tools\n * (`draft_description_from_attributes`, `extract_attributes_from_description`,\n * `draft_description_from_media`, `suggest_title_variants`,\n * `suggest_price_adjustment`) can reuse them verbatim without either\n * duplicating the logic or depending on an internal symbol in\n * `merchandising-pack.ts`. Both packs (`merchandising-pack.ts` and\n * `authoring-pack.ts`) consume these helpers; neither pack owns the bundle\n * loader any more.\n *\n * Keeping the shared pieces query-shaped (not tool-shaped) means each tool is\n * free to project its own output shape without leaking concerns between packs.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n findOneWithDecryption,\n findWithDecryption,\n} from '@open-mercato/shared/lib/encryption/find'\nimport {\n loadCustomFieldDefinitionIndex,\n loadCustomFieldValues,\n type CustomFieldDefinitionSummary,\n} from '@open-mercato/shared/lib/crud/custom-fields'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogPriceKind,\n CatalogProduct,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n CatalogProductUnitConversion,\n CatalogProductVariant,\n} from '../data/entities'\nimport type { CatalogPricingService } from '../services/catalogPricingService'\nimport type { PriceRow, PricingContext } from '../lib/pricing'\nimport type { CatalogToolContext } from './types'\n\n/* -------------------------------------------------------------------------- */\n/* Price-kind enumeration shared core */\n/* -------------------------------------------------------------------------- */\n\nexport type ListPriceKindsCoreInput = {\n limit?: number\n offset?: number\n}\n\nexport type ListPriceKindsCoreRow = {\n id: string\n code: string\n title: string\n displayMode: string\n currencyCode: string | null\n isPromotion: boolean\n isActive: boolean\n organizationId: string | null\n tenantId: string | null\n createdAt: string | null\n updatedAt: string | null\n}\n\nexport type ListPriceKindsCoreResult = {\n items: ListPriceKindsCoreRow[]\n total: number\n limit: number\n offset: number\n}\n\nexport function resolveEm(ctx: CatalogToolContext): EntityManager {\n return ctx.container.resolve<EntityManager>('em')\n}\n\nexport function buildScope(ctx: CatalogToolContext, tenantId: string) {\n return { tenantId, organizationId: ctx.organizationId }\n}\n\n/**\n * Shared tenant-scoped enumeration of `CatalogPriceKind` rows.\n *\n * Uses `findWithDecryption` + post-filter. Price kinds are tenant-owned and\n * can be either organization-scoped (match `ctx.organizationId`) or\n * null-scoped (shared across the tenant); the `$or` below mirrors the\n * filter the base tool used pre-refactor so behavior stays identical.\n */\nexport async function listPriceKindsCore(\n ctx: CatalogToolContext,\n input: ListPriceKindsCoreInput,\n tenantId: string,\n): Promise<ListPriceKindsCoreResult> {\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId, deletedAt: null }\n if (ctx.organizationId) {\n where.$or = [{ organizationId: ctx.organizationId }, { organizationId: null }]\n }\n const [rows, total] = await Promise.all([\n findWithDecryption<CatalogPriceKind>(\n em,\n CatalogPriceKind,\n where as any,\n { limit, offset, orderBy: { code: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CatalogPriceKind, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n items: filtered.map((row) => ({\n id: row.id,\n code: row.code,\n title: row.title,\n displayMode: row.displayMode,\n currencyCode: row.currencyCode ?? null,\n isPromotion: !!row.isPromotion,\n isActive: !!row.isActive,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null,\n })),\n total,\n limit,\n offset,\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Product summary + bundle builder */\n/* -------------------------------------------------------------------------- */\n\nexport type ProductSummary = {\n id: string\n title: string\n subtitle: string | null\n sku: string | null\n handle: string | null\n productType: string\n statusEntryId: string | null\n primaryCurrencyCode: string | null\n defaultMediaId: string | null\n defaultMediaUrl: string | null\n /**\n * Alias of `defaultMediaUrl`. Surfaced under the same key the\n * `open-mercato:product` record card consumes so the model can pass it\n * straight through without renaming. Null when the product has no\n * default media.\n */\n imageUrl: string | null\n isActive: boolean\n isConfigurable: boolean\n organizationId: string | null\n tenantId: string | null\n createdAt: string | null\n updatedAt: string | null\n description: string | null\n}\n\nexport function toProductSummary(row: CatalogProduct): ProductSummary {\n return {\n id: row.id,\n title: row.title,\n subtitle: row.subtitle ?? null,\n sku: row.sku ?? null,\n handle: row.handle ?? null,\n productType: row.productType,\n statusEntryId: row.statusEntryId ?? null,\n primaryCurrencyCode: row.primaryCurrencyCode ?? null,\n defaultMediaId: row.defaultMediaId ?? null,\n defaultMediaUrl: row.defaultMediaUrl ?? null,\n imageUrl: row.defaultMediaUrl ?? null,\n isActive: !!row.isActive,\n isConfigurable: !!row.isConfigurable,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null,\n // `description` is a product field used by D18 authoring tools to seed\n // extract-attributes-from-description; falls back to null when absent.\n description: (row as any).description ?? null,\n }\n}\n\nexport function toPriceNumeric(value: string | null | undefined): number | null {\n if (value === null || value === undefined) return null\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n}\n\nexport type AttributeSchemaField = {\n key: string\n label: string | null\n type: string | null\n required: boolean\n options: unknown | null\n scope: 'module' | 'category' | 'product'\n}\n\nexport type AttributeSchemaResult = {\n fields: AttributeSchemaField[]\n resolvedFor: { productId?: string; categoryId?: string }\n}\n\nfunction summarizeDefinitionAsField(\n summary: CustomFieldDefinitionSummary,\n scope: AttributeSchemaField['scope'],\n): AttributeSchemaField {\n return {\n key: summary.key,\n label: summary.label ?? null,\n type: summary.kind ?? null,\n required: false,\n options: null,\n scope,\n }\n}\n\nexport async function resolveAttributeSchema(\n ctx: CatalogToolContext,\n tenantId: string,\n productId?: string,\n categoryId?: string,\n): Promise<AttributeSchemaResult> {\n const em = resolveEm(ctx)\n const organizationIds = ctx.organizationId ? [ctx.organizationId] : []\n const moduleDefs = await loadCustomFieldDefinitionIndex({\n em,\n entityIds: [E.catalog.catalog_product, E.catalog.catalog_product_category],\n tenantId,\n organizationIds,\n })\n const fields: AttributeSchemaField[] = []\n moduleDefs.forEach((entries) => {\n const pick = entries[0]\n if (!pick) return\n const scope: AttributeSchemaField['scope'] = pick.organizationId ? 'product' : 'module'\n fields.push(summarizeDefinitionAsField(pick, scope))\n })\n return {\n fields,\n resolvedFor: {\n ...(productId ? { productId } : {}),\n ...(categoryId ? { categoryId } : {}),\n },\n }\n}\n\nexport type ProductBundleMediaEntry = {\n mediaId: string\n attachmentId: string\n fileName: string\n mediaType: string | null\n size: number | null\n altText: string | null\n sortOrder: number\n}\n\nexport type ProductBundle = {\n found: true\n id: string\n product: ProductSummary\n categories: Array<{ id: string; name: string | null; slug: string | null; path: string | null }>\n tags: Array<{ id: string; label: string; slug: string }>\n variants: Array<Record<string, unknown>>\n prices: {\n all: Array<Record<string, unknown>>\n best: Record<string, unknown> | null\n }\n media: ProductBundleMediaEntry[]\n customFields: Record<string, unknown>\n attributeSchema: AttributeSchemaResult\n translations: null\n}\n\nexport type ProductBundleResult = ProductBundle | { found: false; productId: string }\n\nfunction resolvePricingService(ctx: CatalogToolContext): CatalogPricingService | null {\n try {\n return ctx.container.resolve<CatalogPricingService>('catalogPricingService')\n } catch {\n return null\n }\n}\n\nexport async function buildProductBundle(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n productId: string,\n): Promise<ProductBundleResult> {\n const where: Record<string, unknown> = {\n id: productId,\n tenantId,\n deletedAt: null,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const product = await findOneWithDecryption<CatalogProduct>(\n em,\n CatalogProduct,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!product || product.tenantId !== tenantId) {\n return { found: false as const, productId }\n }\n const scope = buildScope(ctx, tenantId)\n const [\n categoryAssignments,\n tagAssignments,\n variants,\n prices,\n mediaAttachments,\n unitConversions,\n customFieldValues,\n attributeSchema,\n ] = await Promise.all([\n findWithDecryption<CatalogProductCategoryAssignment>(\n em,\n CatalogProductCategoryAssignment,\n { tenantId, product: product.id } as any,\n { limit: 100, populate: ['category'] as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductTagAssignment>(\n em,\n CatalogProductTagAssignment,\n { tenantId, product: product.id } as any,\n { limit: 100, populate: ['tag'] as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductVariant>(\n em,\n CatalogProductVariant,\n { tenantId, product: product.id, deletedAt: null } as any,\n { limit: 100, orderBy: { createdAt: 'asc' } as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductPrice>(\n em,\n CatalogProductPrice,\n { tenantId, product: product.id } as any,\n { limit: 100, orderBy: { createdAt: 'asc' } as any } as any,\n scope,\n ),\n findWithDecryption<Attachment>(\n em,\n Attachment,\n { tenantId, entityId: E.catalog.catalog_product, recordId: product.id } as any,\n { limit: 100, orderBy: { createdAt: 'asc' } as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductUnitConversion>(\n em,\n CatalogProductUnitConversion,\n { tenantId, product: product.id, deletedAt: null } as any,\n { limit: 100, orderBy: { sortOrder: 'asc', createdAt: 'asc' } as any } as any,\n scope,\n ),\n loadCustomFieldValues({\n em,\n entityId: E.catalog.catalog_product,\n recordIds: [product.id],\n tenantIdByRecord: { [product.id]: product.tenantId ?? null },\n organizationIdByRecord: { [product.id]: product.organizationId ?? null },\n tenantFallbacks: [product.tenantId ?? tenantId].filter((value): value is string => !!value),\n }),\n resolveAttributeSchema(ctx, tenantId, product.id, undefined),\n ])\n\n const categories = categoryAssignments\n .map((assignment) => {\n const category = (assignment as any).category\n if (!category || typeof category === 'string') {\n const fallbackId = typeof category === 'string' ? category : null\n return fallbackId ? { id: fallbackId, name: null, slug: null, path: null } : null\n }\n return {\n id: category.id,\n name: category.name ?? null,\n slug: category.slug ?? null,\n path: category.treePath ?? null,\n }\n })\n .filter((value): value is { id: string; name: string | null; slug: string | null; path: string | null } => value !== null)\n\n const tags = tagAssignments\n .map((assignment) => {\n const tag = (assignment as any).tag as CatalogProductTag | string | null\n if (!tag || typeof tag === 'string') return null\n return { id: tag.id, label: tag.label, slug: tag.slug }\n })\n .filter((value): value is { id: string; label: string; slug: string } => value !== null)\n\n const priceRows = prices.map((row) => ({\n id: row.id,\n priceKindId: (row as any).priceKind && typeof (row as any).priceKind === 'object'\n ? (row as any).priceKind.id\n : (row as any).priceKind ?? null,\n currencyCode: row.currencyCode,\n kind: row.kind,\n minQuantity: row.minQuantity,\n maxQuantity: row.maxQuantity ?? null,\n unitPriceNet: row.unitPriceNet ?? null,\n unitPriceGross: row.unitPriceGross ?? null,\n taxRate: row.taxRate ?? null,\n taxAmount: row.taxAmount ?? null,\n channelId: row.channelId ?? null,\n offerId: (row as any).offer && typeof (row as any).offer === 'object'\n ? (row as any).offer.id\n : (row as any).offer ?? null,\n variantId: (row as any).variant && typeof (row as any).variant === 'object'\n ? (row as any).variant.id\n : (row as any).variant ?? null,\n startsAt: row.startsAt ? new Date(row.startsAt).toISOString() : null,\n endsAt: row.endsAt ? new Date(row.endsAt).toISOString() : null,\n }))\n\n let bestPrice: Record<string, unknown> | null = null\n const pricingService = resolvePricingService(ctx)\n if (pricingService && prices.length > 0) {\n const pricingContext: PricingContext = {\n quantity: 1,\n date: new Date(),\n }\n try {\n const resolved = await pricingService.resolvePrice(prices as unknown as PriceRow[], pricingContext)\n if (resolved) {\n bestPrice = {\n id: (resolved as any).id,\n currencyCode: (resolved as any).currencyCode,\n kind: (resolved as any).kind,\n unitPriceNet: (resolved as any).unitPriceNet ?? null,\n unitPriceGross: (resolved as any).unitPriceGross ?? null,\n }\n }\n } catch (error) {\n console.warn('[catalog.get_product_bundle] resolvePrice failed, omitting best price', error)\n }\n }\n\n return {\n found: true,\n id: product.id,\n product: toProductSummary(product),\n categories,\n tags,\n variants: variants.map((variant) => ({\n id: variant.id,\n name: variant.name ?? null,\n sku: variant.sku ?? null,\n barcode: variant.barcode ?? null,\n optionValues: variant.optionValues ?? null,\n defaultMediaId: variant.defaultMediaId ?? null,\n defaultMediaUrl: variant.defaultMediaUrl ?? null,\n isDefault: !!variant.isDefault,\n isActive: !!variant.isActive,\n })),\n prices: {\n all: priceRows,\n best: bestPrice,\n },\n media: mediaAttachments.map((attachment) => ({\n mediaId: attachment.id,\n attachmentId: attachment.id,\n fileName: attachment.fileName,\n mediaType: attachment.mimeType,\n size: attachment.fileSize,\n altText: null,\n sortOrder: 0,\n })),\n customFields: customFieldValues[product.id] ?? {},\n attributeSchema,\n // No translation resolver exists for catalog (no `translations.ts` at\n // module root yet); returning null is an explicit null-surface contract\n // and a hint for Step 5+ to add the translations resolver.\n translations: null,\n }\n}\n"],
|
|
5
|
-
"mappings": "AAqBA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EACE;AAAA,EACA;AAAA,OAEK;AACP,SAAS,SAAS;AAClB,SAAS,kBAAkB;AAC3B;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,OACK;
|
|
4
|
+
"sourcesContent": ["/**\n * Shared helpers for catalog AI tool packs (Phase 1 WS-C, Steps 3.10 / 3.11 / 3.12).\n *\n * Step 3.10/3.11 centralized the price-kind enumeration query used by both the\n * base tool (`catalog.list_price_kinds_base`) and the D18 spec-named tool\n * (`catalog.list_price_kinds`).\n *\n * Step 3.12 lifts the **product-bundle builder** (and the merged\n * attribute-schema resolver) here too, so the D18 AI-authoring tools\n * (`draft_description_from_attributes`, `extract_attributes_from_description`,\n * `draft_description_from_media`, `suggest_title_variants`,\n * `suggest_price_adjustment`) can reuse them verbatim without either\n * duplicating the logic or depending on an internal symbol in\n * `merchandising-pack.ts`. Both packs (`merchandising-pack.ts` and\n * `authoring-pack.ts`) consume these helpers; neither pack owns the bundle\n * loader any more.\n *\n * Keeping the shared pieces query-shaped (not tool-shaped) means each tool is\n * free to project its own output shape without leaking concerns between packs.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport {\n findOneWithDecryption,\n findWithDecryption,\n} from '@open-mercato/shared/lib/encryption/find'\nimport {\n loadCustomFieldDefinitionIndex,\n loadCustomFieldValues,\n type CustomFieldDefinitionSummary,\n} from '@open-mercato/shared/lib/crud/custom-fields'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogPriceKind,\n CatalogProduct,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n CatalogProductUnitConversion,\n CatalogProductVariant,\n} from '../data/entities'\nimport type { CatalogPricingService } from '../services/catalogPricingService'\nimport type { PriceRow, PricingContext } from '../lib/pricing'\nimport type { CatalogToolContext } from './types'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\n/* -------------------------------------------------------------------------- */\n/* Price-kind enumeration shared core */\n/* -------------------------------------------------------------------------- */\n\nexport type ListPriceKindsCoreInput = {\n limit?: number\n offset?: number\n}\n\nexport type ListPriceKindsCoreRow = {\n id: string\n code: string\n title: string\n displayMode: string\n currencyCode: string | null\n isPromotion: boolean\n isActive: boolean\n organizationId: string | null\n tenantId: string | null\n createdAt: string | null\n updatedAt: string | null\n}\n\nexport type ListPriceKindsCoreResult = {\n items: ListPriceKindsCoreRow[]\n total: number\n limit: number\n offset: number\n}\n\nexport function resolveEm(ctx: CatalogToolContext): EntityManager {\n return ctx.container.resolve<EntityManager>('em')\n}\n\nexport function buildScope(ctx: CatalogToolContext, tenantId: string) {\n return { tenantId, organizationId: ctx.organizationId }\n}\n\n/**\n * Shared tenant-scoped enumeration of `CatalogPriceKind` rows.\n *\n * Uses `findWithDecryption` + post-filter. Price kinds are tenant-owned and\n * can be either organization-scoped (match `ctx.organizationId`) or\n * null-scoped (shared across the tenant); the `$or` below mirrors the\n * filter the base tool used pre-refactor so behavior stays identical.\n */\nexport async function listPriceKindsCore(\n ctx: CatalogToolContext,\n input: ListPriceKindsCoreInput,\n tenantId: string,\n): Promise<ListPriceKindsCoreResult> {\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = { tenantId, deletedAt: null }\n if (ctx.organizationId) {\n where.$or = [{ organizationId: ctx.organizationId }, { organizationId: null }]\n }\n const [rows, total] = await Promise.all([\n findWithDecryption<CatalogPriceKind>(\n em,\n CatalogPriceKind,\n where as any,\n { limit, offset, orderBy: { code: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(CatalogPriceKind, where as any),\n ])\n const filtered = rows.filter((row) => row.tenantId === tenantId)\n return {\n items: filtered.map((row) => ({\n id: row.id,\n code: row.code,\n title: row.title,\n displayMode: row.displayMode,\n currencyCode: row.currencyCode ?? null,\n isPromotion: !!row.isPromotion,\n isActive: !!row.isActive,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null,\n })),\n total,\n limit,\n offset,\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Product summary + bundle builder */\n/* -------------------------------------------------------------------------- */\n\nexport type ProductSummary = {\n id: string\n title: string\n subtitle: string | null\n sku: string | null\n handle: string | null\n productType: string\n statusEntryId: string | null\n primaryCurrencyCode: string | null\n defaultMediaId: string | null\n defaultMediaUrl: string | null\n /**\n * Alias of `defaultMediaUrl`. Surfaced under the same key the\n * `open-mercato:product` record card consumes so the model can pass it\n * straight through without renaming. Null when the product has no\n * default media.\n */\n imageUrl: string | null\n isActive: boolean\n isConfigurable: boolean\n organizationId: string | null\n tenantId: string | null\n createdAt: string | null\n updatedAt: string | null\n description: string | null\n}\n\nexport function toProductSummary(row: CatalogProduct): ProductSummary {\n return {\n id: row.id,\n title: row.title,\n subtitle: row.subtitle ?? null,\n sku: row.sku ?? null,\n handle: row.handle ?? null,\n productType: row.productType,\n statusEntryId: row.statusEntryId ?? null,\n primaryCurrencyCode: row.primaryCurrencyCode ?? null,\n defaultMediaId: row.defaultMediaId ?? null,\n defaultMediaUrl: row.defaultMediaUrl ?? null,\n imageUrl: row.defaultMediaUrl ?? null,\n isActive: !!row.isActive,\n isConfigurable: !!row.isConfigurable,\n organizationId: row.organizationId ?? null,\n tenantId: row.tenantId ?? null,\n createdAt: row.createdAt ? new Date(row.createdAt).toISOString() : null,\n updatedAt: row.updatedAt ? new Date(row.updatedAt).toISOString() : null,\n // `description` is a product field used by D18 authoring tools to seed\n // extract-attributes-from-description; falls back to null when absent.\n description: (row as any).description ?? null,\n }\n}\n\nexport function toPriceNumeric(value: string | null | undefined): number | null {\n if (value === null || value === undefined) return null\n const parsed = Number(value)\n return Number.isFinite(parsed) ? parsed : null\n}\n\nexport type AttributeSchemaField = {\n key: string\n label: string | null\n type: string | null\n required: boolean\n options: unknown | null\n scope: 'module' | 'category' | 'product'\n}\n\nexport type AttributeSchemaResult = {\n fields: AttributeSchemaField[]\n resolvedFor: { productId?: string; categoryId?: string }\n}\n\nfunction summarizeDefinitionAsField(\n summary: CustomFieldDefinitionSummary,\n scope: AttributeSchemaField['scope'],\n): AttributeSchemaField {\n return {\n key: summary.key,\n label: summary.label ?? null,\n type: summary.kind ?? null,\n required: false,\n options: null,\n scope,\n }\n}\n\nexport async function resolveAttributeSchema(\n ctx: CatalogToolContext,\n tenantId: string,\n productId?: string,\n categoryId?: string,\n): Promise<AttributeSchemaResult> {\n const em = resolveEm(ctx)\n const organizationIds = ctx.organizationId ? [ctx.organizationId] : []\n const moduleDefs = await loadCustomFieldDefinitionIndex({\n em,\n entityIds: [E.catalog.catalog_product, E.catalog.catalog_product_category],\n tenantId,\n organizationIds,\n })\n const fields: AttributeSchemaField[] = []\n moduleDefs.forEach((entries) => {\n const pick = entries[0]\n if (!pick) return\n const scope: AttributeSchemaField['scope'] = pick.organizationId ? 'product' : 'module'\n fields.push(summarizeDefinitionAsField(pick, scope))\n })\n return {\n fields,\n resolvedFor: {\n ...(productId ? { productId } : {}),\n ...(categoryId ? { categoryId } : {}),\n },\n }\n}\n\nexport type ProductBundleMediaEntry = {\n mediaId: string\n attachmentId: string\n fileName: string\n mediaType: string | null\n size: number | null\n altText: string | null\n sortOrder: number\n}\n\nexport type ProductBundle = {\n found: true\n id: string\n product: ProductSummary\n categories: Array<{ id: string; name: string | null; slug: string | null; path: string | null }>\n tags: Array<{ id: string; label: string; slug: string }>\n variants: Array<Record<string, unknown>>\n prices: {\n all: Array<Record<string, unknown>>\n best: Record<string, unknown> | null\n }\n media: ProductBundleMediaEntry[]\n customFields: Record<string, unknown>\n attributeSchema: AttributeSchemaResult\n translations: null\n}\n\nexport type ProductBundleResult = ProductBundle | { found: false; productId: string }\n\nfunction resolvePricingService(ctx: CatalogToolContext): CatalogPricingService | null {\n try {\n return ctx.container.resolve<CatalogPricingService>('catalogPricingService')\n } catch {\n return null\n }\n}\n\nexport async function buildProductBundle(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n productId: string,\n): Promise<ProductBundleResult> {\n const where: Record<string, unknown> = {\n id: productId,\n tenantId,\n deletedAt: null,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const product = await findOneWithDecryption<CatalogProduct>(\n em,\n CatalogProduct,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!product || product.tenantId !== tenantId) {\n return { found: false as const, productId }\n }\n const scope = buildScope(ctx, tenantId)\n const [\n categoryAssignments,\n tagAssignments,\n variants,\n prices,\n mediaAttachments,\n unitConversions,\n customFieldValues,\n attributeSchema,\n ] = await Promise.all([\n findWithDecryption<CatalogProductCategoryAssignment>(\n em,\n CatalogProductCategoryAssignment,\n { tenantId, product: product.id } as any,\n { limit: 100, populate: ['category'] as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductTagAssignment>(\n em,\n CatalogProductTagAssignment,\n { tenantId, product: product.id } as any,\n { limit: 100, populate: ['tag'] as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductVariant>(\n em,\n CatalogProductVariant,\n { tenantId, product: product.id, deletedAt: null } as any,\n { limit: 100, orderBy: { createdAt: 'asc' } as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductPrice>(\n em,\n CatalogProductPrice,\n { tenantId, product: product.id } as any,\n { limit: 100, orderBy: { createdAt: 'asc' } as any } as any,\n scope,\n ),\n findWithDecryption<Attachment>(\n em,\n Attachment,\n { tenantId, entityId: E.catalog.catalog_product, recordId: product.id } as any,\n { limit: 100, orderBy: { createdAt: 'asc' } as any } as any,\n scope,\n ),\n findWithDecryption<CatalogProductUnitConversion>(\n em,\n CatalogProductUnitConversion,\n { tenantId, product: product.id, deletedAt: null } as any,\n { limit: 100, orderBy: { sortOrder: 'asc', createdAt: 'asc' } as any } as any,\n scope,\n ),\n loadCustomFieldValues({\n em,\n entityId: E.catalog.catalog_product,\n recordIds: [product.id],\n tenantIdByRecord: { [product.id]: product.tenantId ?? null },\n organizationIdByRecord: { [product.id]: product.organizationId ?? null },\n tenantFallbacks: [product.tenantId ?? tenantId].filter((value): value is string => !!value),\n }),\n resolveAttributeSchema(ctx, tenantId, product.id, undefined),\n ])\n\n const categories = categoryAssignments\n .map((assignment) => {\n const category = (assignment as any).category\n if (!category || typeof category === 'string') {\n const fallbackId = typeof category === 'string' ? category : null\n return fallbackId ? { id: fallbackId, name: null, slug: null, path: null } : null\n }\n return {\n id: category.id,\n name: category.name ?? null,\n slug: category.slug ?? null,\n path: category.treePath ?? null,\n }\n })\n .filter((value): value is { id: string; name: string | null; slug: string | null; path: string | null } => value !== null)\n\n const tags = tagAssignments\n .map((assignment) => {\n const tag = (assignment as any).tag as CatalogProductTag | string | null\n if (!tag || typeof tag === 'string') return null\n return { id: tag.id, label: tag.label, slug: tag.slug }\n })\n .filter((value): value is { id: string; label: string; slug: string } => value !== null)\n\n const priceRows = prices.map((row) => ({\n id: row.id,\n priceKindId: (row as any).priceKind && typeof (row as any).priceKind === 'object'\n ? (row as any).priceKind.id\n : (row as any).priceKind ?? null,\n currencyCode: row.currencyCode,\n kind: row.kind,\n minQuantity: row.minQuantity,\n maxQuantity: row.maxQuantity ?? null,\n unitPriceNet: row.unitPriceNet ?? null,\n unitPriceGross: row.unitPriceGross ?? null,\n taxRate: row.taxRate ?? null,\n taxAmount: row.taxAmount ?? null,\n channelId: row.channelId ?? null,\n offerId: (row as any).offer && typeof (row as any).offer === 'object'\n ? (row as any).offer.id\n : (row as any).offer ?? null,\n variantId: (row as any).variant && typeof (row as any).variant === 'object'\n ? (row as any).variant.id\n : (row as any).variant ?? null,\n startsAt: row.startsAt ? new Date(row.startsAt).toISOString() : null,\n endsAt: row.endsAt ? new Date(row.endsAt).toISOString() : null,\n }))\n\n let bestPrice: Record<string, unknown> | null = null\n const pricingService = resolvePricingService(ctx)\n if (pricingService && prices.length > 0) {\n const pricingContext: PricingContext = {\n quantity: 1,\n date: new Date(),\n }\n try {\n const resolved = await pricingService.resolvePrice(prices as unknown as PriceRow[], pricingContext)\n if (resolved) {\n bestPrice = {\n id: (resolved as any).id,\n currencyCode: (resolved as any).currencyCode,\n kind: (resolved as any).kind,\n unitPriceNet: (resolved as any).unitPriceNet ?? null,\n unitPriceGross: (resolved as any).unitPriceGross ?? null,\n }\n }\n } catch (error) {\n logger.warn('catalog.get_product_bundle resolvePrice failed, omitting best price', { err: error })\n }\n }\n\n return {\n found: true,\n id: product.id,\n product: toProductSummary(product),\n categories,\n tags,\n variants: variants.map((variant) => ({\n id: variant.id,\n name: variant.name ?? null,\n sku: variant.sku ?? null,\n barcode: variant.barcode ?? null,\n optionValues: variant.optionValues ?? null,\n defaultMediaId: variant.defaultMediaId ?? null,\n defaultMediaUrl: variant.defaultMediaUrl ?? null,\n isDefault: !!variant.isDefault,\n isActive: !!variant.isActive,\n })),\n prices: {\n all: priceRows,\n best: bestPrice,\n },\n media: mediaAttachments.map((attachment) => ({\n mediaId: attachment.id,\n attachmentId: attachment.id,\n fileName: attachment.fileName,\n mediaType: attachment.mimeType,\n size: attachment.fileSize,\n altText: null,\n sortOrder: 0,\n })),\n customFields: customFieldValues[product.id] ?? {},\n attributeSchema,\n // No translation resolver exists for catalog (no `translations.ts` at\n // module root yet); returning null is an explicit null-surface contract\n // and a hint for Step 5+ to add the translations resolver.\n translations: null,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
resolveAttributeSchema,
|
|
10
10
|
resolveEm
|
|
11
11
|
} from "./_shared.js";
|
|
12
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
13
|
+
const logger = createLogger("catalog");
|
|
12
14
|
function toJsonSchema(schema, schemaName) {
|
|
13
15
|
const json = z.toJSONSchema(schema);
|
|
14
16
|
return { ...json, title: schemaName };
|
|
@@ -159,7 +161,7 @@ async function loadUserMedia(em, ctx, tenantId, attachmentIds) {
|
|
|
159
161
|
const tenantScoped = rows.filter((row) => (row.tenantId ?? null) === tenantId);
|
|
160
162
|
for (const dropped of unique) {
|
|
161
163
|
if (!tenantScoped.find((row) => row.id === dropped)) {
|
|
162
|
-
|
|
164
|
+
logger.warn("catalog.draft_description_from_media dropping attachment not in scope", { attachmentId: dropped });
|
|
163
165
|
}
|
|
164
166
|
}
|
|
165
167
|
return tenantScoped.map((row) => ({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/catalog/ai-tools/authoring-pack.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * D18 catalog AI-authoring tools (Phase 1 WS-C, Step 3.12).\n *\n * Five structured-output helpers the `catalog.merchandising_assistant`\n * agent (Step 4.9) whitelists verbatim. These tools never write to the\n * database and never open a fresh model call from inside their handlers.\n * Instead, each handler assembles the tenant-scoped context the model will\n * need (product bundle, attribute schema, media references) and returns\n * an `{ proposal, context, outputSchemaDescriptor }` contract:\n *\n * - `context` \u2014 tenant-validated input for the model's\n * structured-output step.\n * - `outputSchemaDescriptor` \u2014 a `{ schemaName, jsonSchema }` descriptor\n * the agent runtime passes into\n * `runAiAgentObject` (Step 3.5) so the SAME\n * agent turn that invoked the tool fills the\n * `proposal` via structured output.\n * - `proposal` \u2014 a zod-typed placeholder with the fields the\n * model is expected to populate. Handlers\n * return empty defaults; the model fills them.\n *\n * This preserves the \"no database writes + no extra model round trip from\n * a tool\" contract from the brief. Any downstream mutation (e.g.\n * `catalog.update_product` in Step 5.14) re-validates the proposal\n * authoritatively on the server before writing.\n *\n * Five tools:\n * - `catalog.draft_description_from_attributes`\n * - `catalog.extract_attributes_from_description`\n * - `catalog.draft_description_from_media`\n * - `catalog.suggest_title_variants`\n * - `catalog.suggest_price_adjustment`\n *\n * Every tool sets `isMutation: false` explicitly (spec \u00A77 line 536 calls\n * this out specifically for `suggest_price_adjustment`; the whole authoring\n * pack mirrors the flag for consistency).\n *\n * Tenant scoping: all lookups route through the shared `_shared.ts`\n * helpers (`buildProductBundle`, `resolveAttributeSchema`,\n * `listPriceKindsCore`) and the shared attachment + product-price readers.\n * Never touches raw `em.find` / `em.findOne`.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { assertTenantScope, type CatalogAiToolDefinition, type CatalogToolContext } from './types'\nimport {\n buildProductBundle,\n buildScope,\n listPriceKindsCore,\n resolveAttributeSchema,\n resolveEm,\n type AttributeSchemaResult,\n type ProductBundle,\n type ProductBundleResult,\n} from './_shared'\nimport type { CatalogPricingService } from '../services/catalogPricingService'\nimport type { PriceRow, PricingContext } from '../lib/pricing'\n\n/* -------------------------------------------------------------------------- */\n/* Shared helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction toJsonSchema<T>(schema: z.ZodType<T>, schemaName: string): Record<string, unknown> {\n const json = z.toJSONSchema(schema) as Record<string, unknown>\n return { ...json, title: schemaName }\n}\n\ntype ProductContextHit = {\n found: true\n productId: string\n bundle: ProductBundle\n}\n\ntype ProductContextMiss = {\n found: false\n productId: string\n}\n\nasync function loadProductContext(\n ctx: CatalogToolContext,\n tenantId: string,\n productId: string,\n): Promise<ProductContextHit | ProductContextMiss> {\n const em = resolveEm(ctx)\n const result: ProductBundleResult = await buildProductBundle(em, ctx, tenantId, productId)\n if (!result.found) {\n return { found: false as const, productId }\n }\n return { found: true as const, productId, bundle: result }\n}\n\nfunction resolvePricingService(ctx: CatalogToolContext): CatalogPricingService | null {\n try {\n return ctx.container.resolve<CatalogPricingService>('catalogPricingService')\n } catch {\n return null\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.draft_description_from_attributes */\n/* -------------------------------------------------------------------------- */\n\nconst draftDescriptionFromAttributesInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n tonePreference: z\n .enum(['neutral', 'marketing', 'technical', 'short'])\n .optional()\n .describe('Preferred tone for the drafted description (default `neutral`).'),\n})\n\nconst draftDescriptionFromAttributesProposal = z.object({\n description: z.string().describe('Drafted product description the model should author from the attribute bundle.'),\n rationale: z.string().describe('Why the model authored this description (which attributes drove it).'),\n attributesUsed: z.array(z.string()).describe('Attribute keys the model actually referenced.'),\n})\n\nconst DRAFT_DESCRIPTION_FROM_ATTRIBUTES_SCHEMA = 'DraftDescriptionFromAttributes'\n\nconst draftDescriptionFromAttributesTool: CatalogAiToolDefinition = {\n name: 'catalog.draft_description_from_attributes',\n displayName: 'Draft description from attributes',\n description:\n 'Structured-output helper for D18: returns the tenant-scoped product bundle (core fields + attributes + media metadata) alongside a JSON-Schema descriptor the surrounding agent turn uses via `runAiAgentObject` to draft a product description. The handler NEVER calls the model itself; it composes the context and the output shape.',\n inputSchema: draftDescriptionFromAttributesInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = draftDescriptionFromAttributesInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const tonePreference = input.tonePreference ?? 'neutral'\n return {\n found: true as const,\n proposal: {\n description: '',\n rationale: '',\n attributesUsed: [] as string[],\n },\n context: {\n product: hit.bundle,\n tonePreference,\n },\n outputSchemaDescriptor: {\n schemaName: DRAFT_DESCRIPTION_FROM_ATTRIBUTES_SCHEMA,\n jsonSchema: toJsonSchema(draftDescriptionFromAttributesProposal, DRAFT_DESCRIPTION_FROM_ATTRIBUTES_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.extract_attributes_from_description */\n/* -------------------------------------------------------------------------- */\n\nconst extractAttributesFromDescriptionInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n descriptionOverride: z\n .string()\n .min(1)\n .optional()\n .describe('Optional description text to analyze instead of the product\\'s stored description.'),\n})\n\n// `attributes` is modeled as a free-form `Record<string, unknown>` because\n// the attribute schema is tenant-defined and heterogeneous (enum / numeric /\n// boolean / string-with-unit). `z.record` emits JSON Schema\n// `additionalProperties: {}` which is the intended open-object surface for\n// the model; downstream validation (Step 5.14 `apply_attribute_extraction`)\n// re-checks each value against the resolved schema authoritatively.\nconst extractAttributesFromDescriptionProposal = z.object({\n attributes: z\n .record(z.string(), z.unknown())\n .describe('Attribute key \u2192 value pairs mapped from the description. Shape matches the resolved attribute schema; server re-validates before any write.'),\n confidence: z\n .number()\n .min(0)\n .max(1)\n .describe('Model confidence (0..1) in the extracted attribute set as a whole.'),\n unmapped: z\n .array(z.string())\n .describe('Description phrases that could not be mapped to any schema attribute.'),\n})\n\nconst EXTRACT_ATTRIBUTES_FROM_DESCRIPTION_SCHEMA = 'ExtractAttributesFromDescription'\n\nconst extractAttributesFromDescriptionTool: CatalogAiToolDefinition = {\n name: 'catalog.extract_attributes_from_description',\n displayName: 'Extract attributes from description',\n description:\n 'Structured-output helper for D18: returns the product bundle, the resolved attribute schema, and the description text the model should parse. The surrounding agent turn uses `runAiAgentObject` with the emitted JSON-Schema descriptor to produce the extracted attribute map. The `attributes` output shape intentionally uses `additionalProperties: true` because tenant attribute schemas are heterogeneous; any downstream write re-validates each value against the schema authoritatively.',\n inputSchema: extractAttributesFromDescriptionInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = extractAttributesFromDescriptionInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const attributeSchema: AttributeSchemaResult = await resolveAttributeSchema(\n ctx,\n tenantId,\n input.productId,\n undefined,\n )\n const description = input.descriptionOverride ?? hit.bundle.product.description ?? ''\n return {\n found: true as const,\n proposal: {\n attributes: {} as Record<string, unknown>,\n confidence: 0,\n unmapped: [] as string[],\n },\n context: {\n product: hit.bundle,\n attributeSchema,\n description,\n },\n outputSchemaDescriptor: {\n schemaName: EXTRACT_ATTRIBUTES_FROM_DESCRIPTION_SCHEMA,\n jsonSchema: toJsonSchema(\n extractAttributesFromDescriptionProposal,\n EXTRACT_ATTRIBUTES_FROM_DESCRIPTION_SCHEMA,\n ),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.draft_description_from_media */\n/* -------------------------------------------------------------------------- */\n\nconst draftDescriptionFromMediaInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n userUploadedAttachmentIds: z\n .array(z.string().uuid())\n .max(20)\n .optional()\n .describe('Additional attachment IDs the user just uploaded. Cross-tenant IDs are dropped with a warning (not surfaced).'),\n})\n\nconst draftDescriptionFromMediaProposal = z.object({\n description: z.string().describe('Drafted description based on the media references.'),\n features: z.array(z.string()).describe('Salient product features the model surfaced from the media.'),\n mediaReferences: z\n .array(\n z.object({\n attachmentId: z.string(),\n note: z.string().optional(),\n }),\n )\n .describe('Attachment IDs the model actually used, with an optional note per reference.'),\n})\n\nconst DRAFT_DESCRIPTION_FROM_MEDIA_SCHEMA = 'DraftDescriptionFromMedia'\n\ntype UserMediaEntry = {\n attachmentId: string\n fileName: string\n mediaType: string | null\n size: number | null\n}\n\nasync function loadUserMedia(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n attachmentIds: string[],\n): Promise<UserMediaEntry[]> {\n if (!attachmentIds.length) return []\n const unique = Array.from(new Set(attachmentIds))\n const where: Record<string, unknown> = {\n tenantId,\n id: { $in: unique },\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const rows = await findWithDecryption<Attachment>(\n em,\n Attachment,\n where as any,\n { limit: unique.length } as any,\n buildScope(ctx, tenantId),\n )\n const tenantScoped = rows.filter((row) => (row.tenantId ?? null) === tenantId)\n for (const dropped of unique) {\n if (!tenantScoped.find((row) => row.id === dropped)) {\n console.warn(`[catalog.draft_description_from_media] dropping attachment not in scope: ${dropped}`)\n }\n }\n return tenantScoped.map((row) => ({\n attachmentId: row.id,\n fileName: row.fileName,\n mediaType: row.mimeType ?? null,\n size: row.fileSize ?? null,\n }))\n}\n\nconst draftDescriptionFromMediaTool: CatalogAiToolDefinition = {\n name: 'catalog.draft_description_from_media',\n displayName: 'Draft description from media',\n description:\n 'Structured-output helper for D18. Returns the product bundle, the product\\'s media metadata, and any tenant-scoped user-uploaded attachment IDs (cross-tenant IDs are dropped with a warning). The handler does NOT fetch attachment bytes \u2014 the Step 3.7 attachment bridge resolves bytes when the surrounding agent turn dispatches this tool. Requires a vision-capable provider to use raw image bytes; OCR-text fallback for non-vision providers already flows through the Step 3.7 bridge.',\n inputSchema: draftDescriptionFromMediaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising', 'media'],\n isMutation: false,\n supportsAttachments: true,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = draftDescriptionFromMediaInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const em = resolveEm(ctx)\n const userMedia = await loadUserMedia(em, ctx, tenantId, input.userUploadedAttachmentIds ?? [])\n const productMedia = hit.bundle.media.map((entry) => ({\n attachmentId: entry.attachmentId,\n fileName: entry.fileName,\n mediaType: entry.mediaType,\n altText: entry.altText,\n sortOrder: entry.sortOrder,\n }))\n return {\n found: true as const,\n proposal: {\n description: '',\n features: [] as string[],\n mediaReferences: [] as Array<{ attachmentId: string; note?: string }>,\n },\n context: {\n product: hit.bundle,\n productMedia,\n userMedia,\n },\n outputSchemaDescriptor: {\n schemaName: DRAFT_DESCRIPTION_FROM_MEDIA_SCHEMA,\n jsonSchema: toJsonSchema(draftDescriptionFromMediaProposal, DRAFT_DESCRIPTION_FROM_MEDIA_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.suggest_title_variants */\n/* -------------------------------------------------------------------------- */\n\nconst suggestTitleVariantsInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n targetStyle: z\n .enum(['short', 'seo', 'marketplace'])\n .describe('Voice/style for the variants (short, seo-optimized, or marketplace-friendly).'),\n maxVariants: z\n .number()\n .int()\n .min(1)\n .max(5)\n .optional()\n .describe('Max number of variants to return. Default 3, hard-capped at 5.'),\n})\n\nconst suggestTitleVariantsProposal = z.object({\n variants: z\n .array(\n z.object({\n title: z.string(),\n style: z.string(),\n rationale: z.string().optional(),\n }),\n )\n .describe('Proposed title variants (respect the `maxVariants` bound).'),\n})\n\nconst SUGGEST_TITLE_VARIANTS_SCHEMA = 'SuggestTitleVariants'\n\nconst suggestTitleVariantsTool: CatalogAiToolDefinition = {\n name: 'catalog.suggest_title_variants',\n displayName: 'Suggest title variants',\n description:\n 'Structured-output helper for D18: returns the product bundle and the target style. The surrounding agent turn uses `runAiAgentObject` to propose `maxVariants` (default 3, capped at 5) title variants matching the style.',\n inputSchema: suggestTitleVariantsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = suggestTitleVariantsInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const maxVariants = Math.min(input.maxVariants ?? 3, 5)\n return {\n found: true as const,\n proposal: {\n variants: [] as Array<{ title: string; style: string; rationale?: string }>,\n },\n context: {\n product: hit.bundle,\n targetStyle: input.targetStyle,\n maxVariants,\n },\n outputSchemaDescriptor: {\n schemaName: SUGGEST_TITLE_VARIANTS_SCHEMA,\n jsonSchema: toJsonSchema(suggestTitleVariantsProposal, SUGGEST_TITLE_VARIANTS_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.suggest_price_adjustment */\n/* -------------------------------------------------------------------------- */\n\nconst suggestPriceAdjustmentInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n intent: z\n .string()\n .trim()\n .min(1)\n .describe('Human-readable intent (e.g. \"raise price by 10%\", \"match competitor at 29.99\").'),\n priceKindId: z\n .string()\n .uuid()\n .optional()\n .describe('Optional price-kind id to target. When omitted, the model picks the most relevant kind from `context.availablePriceKinds`.'),\n})\n\nconst suggestPriceAdjustmentProposal = z.object({\n currentPrice: z\n .union([\n z.object({\n amount: z.number(),\n currency: z.string(),\n priceKindId: z.string(),\n }),\n z.null(),\n ])\n .describe('Current price on the targeted kind (null if none is defined).'),\n proposedPrice: z.object({\n amount: z.number(),\n currency: z.string(),\n priceKindId: z.string(),\n }),\n rationale: z.string(),\n constraints: z.object({\n respectedPriceKindScope: z.boolean(),\n respectedCurrency: z.boolean(),\n }),\n})\n\nconst SUGGEST_PRICE_ADJUSTMENT_SCHEMA = 'SuggestPriceAdjustment'\n\ntype ResolvedCurrentPrice = {\n amount: number\n currency: string\n priceKindId: string\n} | null\n\nfunction currentPriceFromBundle(\n bundle: ProductBundle,\n priceKindId?: string,\n): ResolvedCurrentPrice {\n const priceRows = bundle.prices.all as Array<Record<string, unknown>>\n if (!priceRows.length) return null\n const candidates = priceKindId\n ? priceRows.filter((row) => row.priceKindId === priceKindId)\n : priceRows\n const pick = candidates[0] ?? null\n if (!pick) return null\n const rawAmount = (pick.unitPriceGross ?? pick.unitPriceNet) as string | null | undefined\n const amount = rawAmount === null || rawAmount === undefined ? null : Number(rawAmount)\n if (amount === null || !Number.isFinite(amount)) return null\n const currency = (pick.currencyCode as string | null) ?? null\n const resolvedKind = (pick.priceKindId as string | null) ?? null\n if (!currency || !resolvedKind) return null\n return { amount, currency, priceKindId: resolvedKind }\n}\n\nasync function currentPriceFromService(\n ctx: CatalogToolContext,\n bundle: ProductBundle,\n priceKindId?: string,\n): Promise<ResolvedCurrentPrice | 'resolver_unavailable'> {\n const pricingService = resolvePricingService(ctx)\n if (!pricingService) return 'resolver_unavailable'\n const priceRows = bundle.prices.all as Array<Record<string, unknown>>\n if (!priceRows.length) return null\n const candidates = priceKindId\n ? priceRows.filter((row) => row.priceKindId === priceKindId)\n : priceRows\n if (!candidates.length) return null\n try {\n const pricingContext: PricingContext = { quantity: 1, date: new Date() }\n const resolved = await pricingService.resolvePrice(\n candidates as unknown as PriceRow[],\n pricingContext,\n )\n if (!resolved) return null\n const rawAmount = ((resolved as any).unitPriceGross ?? (resolved as any).unitPriceNet) as\n | string\n | number\n | null\n | undefined\n const amount = rawAmount === null || rawAmount === undefined ? null : Number(rawAmount)\n if (amount === null || !Number.isFinite(amount)) return null\n const currency = ((resolved as any).currencyCode as string | null) ?? null\n const resolvedKind = ((resolved as any).priceKindId\n ?? ((resolved as any).priceKind && typeof (resolved as any).priceKind === 'object'\n ? (resolved as any).priceKind.id\n : (resolved as any).priceKind)) as string | null | undefined\n if (!currency || !resolvedKind) return null\n return { amount, currency, priceKindId: resolvedKind }\n } catch {\n // The pricing service may throw for reasons beyond our control (config,\n // resolver chain error). We fall back to the bundle view rather than\n // bubbling the error out of a read-only tool.\n return 'resolver_unavailable'\n }\n}\n\nconst suggestPriceAdjustmentTool: CatalogAiToolDefinition = {\n name: 'catalog.suggest_price_adjustment',\n displayName: 'Suggest price adjustment',\n description:\n 'Structured-output helper for D18. Returns the product bundle, the current tenant-resolved price (via `catalogPricingService.selectBestPrice` when available, otherwise a fallback projection from the bundle prices), and the list of available price kinds. The surrounding agent turn uses `runAiAgentObject` to propose a price adjustment. `isMutation: false` is explicit \u2014 this tool NEVER writes; `catalog.update_product` (Step 5.14) recalculates authoritatively on the server before any write.',\n inputSchema: suggestPriceAdjustmentInput,\n requiredFeatures: ['catalog.pricing.manage'],\n tags: ['read', 'catalog', 'authoring', 'merchandising', 'pricing'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = suggestPriceAdjustmentInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const serviceResult = await currentPriceFromService(ctx, hit.bundle, input.priceKindId)\n const currentPrice: ResolvedCurrentPrice =\n serviceResult === 'resolver_unavailable'\n ? currentPriceFromBundle(hit.bundle, input.priceKindId)\n : serviceResult\n const priceKindsResult = await listPriceKindsCore(ctx, { limit: 100 }, tenantId)\n const availablePriceKinds = priceKindsResult.items.map((row) => ({\n id: row.id,\n code: row.code,\n scope: row.organizationId ? ('organization' as const) : ('tenant' as const),\n }))\n return {\n found: true as const,\n proposal: {\n currentPrice,\n proposedPrice: {\n amount: currentPrice?.amount ?? 0,\n currency: currentPrice?.currency ?? (hit.bundle.product.primaryCurrencyCode ?? 'USD'),\n priceKindId: currentPrice?.priceKindId ?? (input.priceKindId ?? ''),\n },\n rationale: '',\n constraints: {\n respectedPriceKindScope: true,\n respectedCurrency: true,\n },\n },\n context: {\n product: hit.bundle,\n intent: input.intent,\n availablePriceKinds,\n },\n outputSchemaDescriptor: {\n schemaName: SUGGEST_PRICE_ADJUSTMENT_SCHEMA,\n jsonSchema: toJsonSchema(suggestPriceAdjustmentProposal, SUGGEST_PRICE_ADJUSTMENT_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* Export */\n/* -------------------------------------------------------------------------- */\n\nexport const authoringAiTools: CatalogAiToolDefinition[] = [\n draftDescriptionFromAttributesTool,\n extractAttributesFromDescriptionTool,\n draftDescriptionFromMediaTool,\n suggestTitleVariantsTool,\n suggestPriceAdjustmentTool,\n]\n\nexport default authoringAiTools\n"],
|
|
5
|
-
"mappings": "AA2CA,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAE3B,SAAS,yBAAgF;AACzF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;
|
|
4
|
+
"sourcesContent": ["/**\n * D18 catalog AI-authoring tools (Phase 1 WS-C, Step 3.12).\n *\n * Five structured-output helpers the `catalog.merchandising_assistant`\n * agent (Step 4.9) whitelists verbatim. These tools never write to the\n * database and never open a fresh model call from inside their handlers.\n * Instead, each handler assembles the tenant-scoped context the model will\n * need (product bundle, attribute schema, media references) and returns\n * an `{ proposal, context, outputSchemaDescriptor }` contract:\n *\n * - `context` \u2014 tenant-validated input for the model's\n * structured-output step.\n * - `outputSchemaDescriptor` \u2014 a `{ schemaName, jsonSchema }` descriptor\n * the agent runtime passes into\n * `runAiAgentObject` (Step 3.5) so the SAME\n * agent turn that invoked the tool fills the\n * `proposal` via structured output.\n * - `proposal` \u2014 a zod-typed placeholder with the fields the\n * model is expected to populate. Handlers\n * return empty defaults; the model fills them.\n *\n * This preserves the \"no database writes + no extra model round trip from\n * a tool\" contract from the brief. Any downstream mutation (e.g.\n * `catalog.update_product` in Step 5.14) re-validates the proposal\n * authoritatively on the server before writing.\n *\n * Five tools:\n * - `catalog.draft_description_from_attributes`\n * - `catalog.extract_attributes_from_description`\n * - `catalog.draft_description_from_media`\n * - `catalog.suggest_title_variants`\n * - `catalog.suggest_price_adjustment`\n *\n * Every tool sets `isMutation: false` explicitly (spec \u00A77 line 536 calls\n * this out specifically for `suggest_price_adjustment`; the whole authoring\n * pack mirrors the flag for consistency).\n *\n * Tenant scoping: all lookups route through the shared `_shared.ts`\n * helpers (`buildProductBundle`, `resolveAttributeSchema`,\n * `listPriceKindsCore`) and the shared attachment + product-price readers.\n * Never touches raw `em.find` / `em.findOne`.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport { assertTenantScope, type CatalogAiToolDefinition, type CatalogToolContext } from './types'\nimport {\n buildProductBundle,\n buildScope,\n listPriceKindsCore,\n resolveAttributeSchema,\n resolveEm,\n type AttributeSchemaResult,\n type ProductBundle,\n type ProductBundleResult,\n} from './_shared'\nimport type { CatalogPricingService } from '../services/catalogPricingService'\nimport type { PriceRow, PricingContext } from '../lib/pricing'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\n/* -------------------------------------------------------------------------- */\n/* Shared helpers */\n/* -------------------------------------------------------------------------- */\n\nfunction toJsonSchema<T>(schema: z.ZodType<T>, schemaName: string): Record<string, unknown> {\n const json = z.toJSONSchema(schema) as Record<string, unknown>\n return { ...json, title: schemaName }\n}\n\ntype ProductContextHit = {\n found: true\n productId: string\n bundle: ProductBundle\n}\n\ntype ProductContextMiss = {\n found: false\n productId: string\n}\n\nasync function loadProductContext(\n ctx: CatalogToolContext,\n tenantId: string,\n productId: string,\n): Promise<ProductContextHit | ProductContextMiss> {\n const em = resolveEm(ctx)\n const result: ProductBundleResult = await buildProductBundle(em, ctx, tenantId, productId)\n if (!result.found) {\n return { found: false as const, productId }\n }\n return { found: true as const, productId, bundle: result }\n}\n\nfunction resolvePricingService(ctx: CatalogToolContext): CatalogPricingService | null {\n try {\n return ctx.container.resolve<CatalogPricingService>('catalogPricingService')\n } catch {\n return null\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.draft_description_from_attributes */\n/* -------------------------------------------------------------------------- */\n\nconst draftDescriptionFromAttributesInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n tonePreference: z\n .enum(['neutral', 'marketing', 'technical', 'short'])\n .optional()\n .describe('Preferred tone for the drafted description (default `neutral`).'),\n})\n\nconst draftDescriptionFromAttributesProposal = z.object({\n description: z.string().describe('Drafted product description the model should author from the attribute bundle.'),\n rationale: z.string().describe('Why the model authored this description (which attributes drove it).'),\n attributesUsed: z.array(z.string()).describe('Attribute keys the model actually referenced.'),\n})\n\nconst DRAFT_DESCRIPTION_FROM_ATTRIBUTES_SCHEMA = 'DraftDescriptionFromAttributes'\n\nconst draftDescriptionFromAttributesTool: CatalogAiToolDefinition = {\n name: 'catalog.draft_description_from_attributes',\n displayName: 'Draft description from attributes',\n description:\n 'Structured-output helper for D18: returns the tenant-scoped product bundle (core fields + attributes + media metadata) alongside a JSON-Schema descriptor the surrounding agent turn uses via `runAiAgentObject` to draft a product description. The handler NEVER calls the model itself; it composes the context and the output shape.',\n inputSchema: draftDescriptionFromAttributesInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = draftDescriptionFromAttributesInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const tonePreference = input.tonePreference ?? 'neutral'\n return {\n found: true as const,\n proposal: {\n description: '',\n rationale: '',\n attributesUsed: [] as string[],\n },\n context: {\n product: hit.bundle,\n tonePreference,\n },\n outputSchemaDescriptor: {\n schemaName: DRAFT_DESCRIPTION_FROM_ATTRIBUTES_SCHEMA,\n jsonSchema: toJsonSchema(draftDescriptionFromAttributesProposal, DRAFT_DESCRIPTION_FROM_ATTRIBUTES_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.extract_attributes_from_description */\n/* -------------------------------------------------------------------------- */\n\nconst extractAttributesFromDescriptionInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n descriptionOverride: z\n .string()\n .min(1)\n .optional()\n .describe('Optional description text to analyze instead of the product\\'s stored description.'),\n})\n\n// `attributes` is modeled as a free-form `Record<string, unknown>` because\n// the attribute schema is tenant-defined and heterogeneous (enum / numeric /\n// boolean / string-with-unit). `z.record` emits JSON Schema\n// `additionalProperties: {}` which is the intended open-object surface for\n// the model; downstream validation (Step 5.14 `apply_attribute_extraction`)\n// re-checks each value against the resolved schema authoritatively.\nconst extractAttributesFromDescriptionProposal = z.object({\n attributes: z\n .record(z.string(), z.unknown())\n .describe('Attribute key \u2192 value pairs mapped from the description. Shape matches the resolved attribute schema; server re-validates before any write.'),\n confidence: z\n .number()\n .min(0)\n .max(1)\n .describe('Model confidence (0..1) in the extracted attribute set as a whole.'),\n unmapped: z\n .array(z.string())\n .describe('Description phrases that could not be mapped to any schema attribute.'),\n})\n\nconst EXTRACT_ATTRIBUTES_FROM_DESCRIPTION_SCHEMA = 'ExtractAttributesFromDescription'\n\nconst extractAttributesFromDescriptionTool: CatalogAiToolDefinition = {\n name: 'catalog.extract_attributes_from_description',\n displayName: 'Extract attributes from description',\n description:\n 'Structured-output helper for D18: returns the product bundle, the resolved attribute schema, and the description text the model should parse. The surrounding agent turn uses `runAiAgentObject` with the emitted JSON-Schema descriptor to produce the extracted attribute map. The `attributes` output shape intentionally uses `additionalProperties: true` because tenant attribute schemas are heterogeneous; any downstream write re-validates each value against the schema authoritatively.',\n inputSchema: extractAttributesFromDescriptionInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = extractAttributesFromDescriptionInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const attributeSchema: AttributeSchemaResult = await resolveAttributeSchema(\n ctx,\n tenantId,\n input.productId,\n undefined,\n )\n const description = input.descriptionOverride ?? hit.bundle.product.description ?? ''\n return {\n found: true as const,\n proposal: {\n attributes: {} as Record<string, unknown>,\n confidence: 0,\n unmapped: [] as string[],\n },\n context: {\n product: hit.bundle,\n attributeSchema,\n description,\n },\n outputSchemaDescriptor: {\n schemaName: EXTRACT_ATTRIBUTES_FROM_DESCRIPTION_SCHEMA,\n jsonSchema: toJsonSchema(\n extractAttributesFromDescriptionProposal,\n EXTRACT_ATTRIBUTES_FROM_DESCRIPTION_SCHEMA,\n ),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.draft_description_from_media */\n/* -------------------------------------------------------------------------- */\n\nconst draftDescriptionFromMediaInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n userUploadedAttachmentIds: z\n .array(z.string().uuid())\n .max(20)\n .optional()\n .describe('Additional attachment IDs the user just uploaded. Cross-tenant IDs are dropped with a warning (not surfaced).'),\n})\n\nconst draftDescriptionFromMediaProposal = z.object({\n description: z.string().describe('Drafted description based on the media references.'),\n features: z.array(z.string()).describe('Salient product features the model surfaced from the media.'),\n mediaReferences: z\n .array(\n z.object({\n attachmentId: z.string(),\n note: z.string().optional(),\n }),\n )\n .describe('Attachment IDs the model actually used, with an optional note per reference.'),\n})\n\nconst DRAFT_DESCRIPTION_FROM_MEDIA_SCHEMA = 'DraftDescriptionFromMedia'\n\ntype UserMediaEntry = {\n attachmentId: string\n fileName: string\n mediaType: string | null\n size: number | null\n}\n\nasync function loadUserMedia(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n attachmentIds: string[],\n): Promise<UserMediaEntry[]> {\n if (!attachmentIds.length) return []\n const unique = Array.from(new Set(attachmentIds))\n const where: Record<string, unknown> = {\n tenantId,\n id: { $in: unique },\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const rows = await findWithDecryption<Attachment>(\n em,\n Attachment,\n where as any,\n { limit: unique.length } as any,\n buildScope(ctx, tenantId),\n )\n const tenantScoped = rows.filter((row) => (row.tenantId ?? null) === tenantId)\n for (const dropped of unique) {\n if (!tenantScoped.find((row) => row.id === dropped)) {\n logger.warn('catalog.draft_description_from_media dropping attachment not in scope', { attachmentId: dropped })\n }\n }\n return tenantScoped.map((row) => ({\n attachmentId: row.id,\n fileName: row.fileName,\n mediaType: row.mimeType ?? null,\n size: row.fileSize ?? null,\n }))\n}\n\nconst draftDescriptionFromMediaTool: CatalogAiToolDefinition = {\n name: 'catalog.draft_description_from_media',\n displayName: 'Draft description from media',\n description:\n 'Structured-output helper for D18. Returns the product bundle, the product\\'s media metadata, and any tenant-scoped user-uploaded attachment IDs (cross-tenant IDs are dropped with a warning). The handler does NOT fetch attachment bytes \u2014 the Step 3.7 attachment bridge resolves bytes when the surrounding agent turn dispatches this tool. Requires a vision-capable provider to use raw image bytes; OCR-text fallback for non-vision providers already flows through the Step 3.7 bridge.',\n inputSchema: draftDescriptionFromMediaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising', 'media'],\n isMutation: false,\n supportsAttachments: true,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = draftDescriptionFromMediaInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const em = resolveEm(ctx)\n const userMedia = await loadUserMedia(em, ctx, tenantId, input.userUploadedAttachmentIds ?? [])\n const productMedia = hit.bundle.media.map((entry) => ({\n attachmentId: entry.attachmentId,\n fileName: entry.fileName,\n mediaType: entry.mediaType,\n altText: entry.altText,\n sortOrder: entry.sortOrder,\n }))\n return {\n found: true as const,\n proposal: {\n description: '',\n features: [] as string[],\n mediaReferences: [] as Array<{ attachmentId: string; note?: string }>,\n },\n context: {\n product: hit.bundle,\n productMedia,\n userMedia,\n },\n outputSchemaDescriptor: {\n schemaName: DRAFT_DESCRIPTION_FROM_MEDIA_SCHEMA,\n jsonSchema: toJsonSchema(draftDescriptionFromMediaProposal, DRAFT_DESCRIPTION_FROM_MEDIA_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.suggest_title_variants */\n/* -------------------------------------------------------------------------- */\n\nconst suggestTitleVariantsInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n targetStyle: z\n .enum(['short', 'seo', 'marketplace'])\n .describe('Voice/style for the variants (short, seo-optimized, or marketplace-friendly).'),\n maxVariants: z\n .number()\n .int()\n .min(1)\n .max(5)\n .optional()\n .describe('Max number of variants to return. Default 3, hard-capped at 5.'),\n})\n\nconst suggestTitleVariantsProposal = z.object({\n variants: z\n .array(\n z.object({\n title: z.string(),\n style: z.string(),\n rationale: z.string().optional(),\n }),\n )\n .describe('Proposed title variants (respect the `maxVariants` bound).'),\n})\n\nconst SUGGEST_TITLE_VARIANTS_SCHEMA = 'SuggestTitleVariants'\n\nconst suggestTitleVariantsTool: CatalogAiToolDefinition = {\n name: 'catalog.suggest_title_variants',\n displayName: 'Suggest title variants',\n description:\n 'Structured-output helper for D18: returns the product bundle and the target style. The surrounding agent turn uses `runAiAgentObject` to propose `maxVariants` (default 3, capped at 5) title variants matching the style.',\n inputSchema: suggestTitleVariantsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'authoring', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = suggestTitleVariantsInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const maxVariants = Math.min(input.maxVariants ?? 3, 5)\n return {\n found: true as const,\n proposal: {\n variants: [] as Array<{ title: string; style: string; rationale?: string }>,\n },\n context: {\n product: hit.bundle,\n targetStyle: input.targetStyle,\n maxVariants,\n },\n outputSchemaDescriptor: {\n schemaName: SUGGEST_TITLE_VARIANTS_SCHEMA,\n jsonSchema: toJsonSchema(suggestTitleVariantsProposal, SUGGEST_TITLE_VARIANTS_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.suggest_price_adjustment */\n/* -------------------------------------------------------------------------- */\n\nconst suggestPriceAdjustmentInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n intent: z\n .string()\n .trim()\n .min(1)\n .describe('Human-readable intent (e.g. \"raise price by 10%\", \"match competitor at 29.99\").'),\n priceKindId: z\n .string()\n .uuid()\n .optional()\n .describe('Optional price-kind id to target. When omitted, the model picks the most relevant kind from `context.availablePriceKinds`.'),\n})\n\nconst suggestPriceAdjustmentProposal = z.object({\n currentPrice: z\n .union([\n z.object({\n amount: z.number(),\n currency: z.string(),\n priceKindId: z.string(),\n }),\n z.null(),\n ])\n .describe('Current price on the targeted kind (null if none is defined).'),\n proposedPrice: z.object({\n amount: z.number(),\n currency: z.string(),\n priceKindId: z.string(),\n }),\n rationale: z.string(),\n constraints: z.object({\n respectedPriceKindScope: z.boolean(),\n respectedCurrency: z.boolean(),\n }),\n})\n\nconst SUGGEST_PRICE_ADJUSTMENT_SCHEMA = 'SuggestPriceAdjustment'\n\ntype ResolvedCurrentPrice = {\n amount: number\n currency: string\n priceKindId: string\n} | null\n\nfunction currentPriceFromBundle(\n bundle: ProductBundle,\n priceKindId?: string,\n): ResolvedCurrentPrice {\n const priceRows = bundle.prices.all as Array<Record<string, unknown>>\n if (!priceRows.length) return null\n const candidates = priceKindId\n ? priceRows.filter((row) => row.priceKindId === priceKindId)\n : priceRows\n const pick = candidates[0] ?? null\n if (!pick) return null\n const rawAmount = (pick.unitPriceGross ?? pick.unitPriceNet) as string | null | undefined\n const amount = rawAmount === null || rawAmount === undefined ? null : Number(rawAmount)\n if (amount === null || !Number.isFinite(amount)) return null\n const currency = (pick.currencyCode as string | null) ?? null\n const resolvedKind = (pick.priceKindId as string | null) ?? null\n if (!currency || !resolvedKind) return null\n return { amount, currency, priceKindId: resolvedKind }\n}\n\nasync function currentPriceFromService(\n ctx: CatalogToolContext,\n bundle: ProductBundle,\n priceKindId?: string,\n): Promise<ResolvedCurrentPrice | 'resolver_unavailable'> {\n const pricingService = resolvePricingService(ctx)\n if (!pricingService) return 'resolver_unavailable'\n const priceRows = bundle.prices.all as Array<Record<string, unknown>>\n if (!priceRows.length) return null\n const candidates = priceKindId\n ? priceRows.filter((row) => row.priceKindId === priceKindId)\n : priceRows\n if (!candidates.length) return null\n try {\n const pricingContext: PricingContext = { quantity: 1, date: new Date() }\n const resolved = await pricingService.resolvePrice(\n candidates as unknown as PriceRow[],\n pricingContext,\n )\n if (!resolved) return null\n const rawAmount = ((resolved as any).unitPriceGross ?? (resolved as any).unitPriceNet) as\n | string\n | number\n | null\n | undefined\n const amount = rawAmount === null || rawAmount === undefined ? null : Number(rawAmount)\n if (amount === null || !Number.isFinite(amount)) return null\n const currency = ((resolved as any).currencyCode as string | null) ?? null\n const resolvedKind = ((resolved as any).priceKindId\n ?? ((resolved as any).priceKind && typeof (resolved as any).priceKind === 'object'\n ? (resolved as any).priceKind.id\n : (resolved as any).priceKind)) as string | null | undefined\n if (!currency || !resolvedKind) return null\n return { amount, currency, priceKindId: resolvedKind }\n } catch {\n // The pricing service may throw for reasons beyond our control (config,\n // resolver chain error). We fall back to the bundle view rather than\n // bubbling the error out of a read-only tool.\n return 'resolver_unavailable'\n }\n}\n\nconst suggestPriceAdjustmentTool: CatalogAiToolDefinition = {\n name: 'catalog.suggest_price_adjustment',\n displayName: 'Suggest price adjustment',\n description:\n 'Structured-output helper for D18. Returns the product bundle, the current tenant-resolved price (via `catalogPricingService.selectBestPrice` when available, otherwise a fallback projection from the bundle prices), and the list of available price kinds. The surrounding agent turn uses `runAiAgentObject` to propose a price adjustment. `isMutation: false` is explicit \u2014 this tool NEVER writes; `catalog.update_product` (Step 5.14) recalculates authoritatively on the server before any write.',\n inputSchema: suggestPriceAdjustmentInput,\n requiredFeatures: ['catalog.pricing.manage'],\n tags: ['read', 'catalog', 'authoring', 'merchandising', 'pricing'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = suggestPriceAdjustmentInput.parse(rawInput)\n const hit = await loadProductContext(ctx, tenantId, input.productId)\n if (!hit.found) {\n return { found: false as const, productId: input.productId }\n }\n const serviceResult = await currentPriceFromService(ctx, hit.bundle, input.priceKindId)\n const currentPrice: ResolvedCurrentPrice =\n serviceResult === 'resolver_unavailable'\n ? currentPriceFromBundle(hit.bundle, input.priceKindId)\n : serviceResult\n const priceKindsResult = await listPriceKindsCore(ctx, { limit: 100 }, tenantId)\n const availablePriceKinds = priceKindsResult.items.map((row) => ({\n id: row.id,\n code: row.code,\n scope: row.organizationId ? ('organization' as const) : ('tenant' as const),\n }))\n return {\n found: true as const,\n proposal: {\n currentPrice,\n proposedPrice: {\n amount: currentPrice?.amount ?? 0,\n currency: currentPrice?.currency ?? (hit.bundle.product.primaryCurrencyCode ?? 'USD'),\n priceKindId: currentPrice?.priceKindId ?? (input.priceKindId ?? ''),\n },\n rationale: '',\n constraints: {\n respectedPriceKindScope: true,\n respectedCurrency: true,\n },\n },\n context: {\n product: hit.bundle,\n intent: input.intent,\n availablePriceKinds,\n },\n outputSchemaDescriptor: {\n schemaName: SUGGEST_PRICE_ADJUSTMENT_SCHEMA,\n jsonSchema: toJsonSchema(suggestPriceAdjustmentProposal, SUGGEST_PRICE_ADJUSTMENT_SCHEMA),\n },\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* Export */\n/* -------------------------------------------------------------------------- */\n\nexport const authoringAiTools: CatalogAiToolDefinition[] = [\n draftDescriptionFromAttributesTool,\n extractAttributesFromDescriptionTool,\n draftDescriptionFromMediaTool,\n suggestTitleVariantsTool,\n suggestPriceAdjustmentTool,\n]\n\nexport default authoringAiTools\n"],
|
|
5
|
+
"mappings": "AA2CA,SAAS,SAAS;AAClB,SAAS,0BAA0B;AACnC,SAAS,kBAAkB;AAE3B,SAAS,yBAAgF;AACzF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAGP,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,SAAS;AAMrC,SAAS,aAAgB,QAAsB,YAA6C;AAC1F,QAAM,OAAO,EAAE,aAAa,MAAM;AAClC,SAAO,EAAE,GAAG,MAAM,OAAO,WAAW;AACtC;AAaA,eAAe,mBACb,KACA,UACA,WACiD;AACjD,QAAM,KAAK,UAAU,GAAG;AACxB,QAAM,SAA8B,MAAM,mBAAmB,IAAI,KAAK,UAAU,SAAS;AACzF,MAAI,CAAC,OAAO,OAAO;AACjB,WAAO,EAAE,OAAO,OAAgB,UAAU;AAAA,EAC5C;AACA,SAAO,EAAE,OAAO,MAAe,WAAW,QAAQ,OAAO;AAC3D;AAEA,SAAS,sBAAsB,KAAuD;AACpF,MAAI;AACF,WAAO,IAAI,UAAU,QAA+B,uBAAuB;AAAA,EAC7E,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAMA,MAAM,sCAAsC,EAAE,OAAO;AAAA,EACnD,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AAAA,EAClE,gBAAgB,EACb,KAAK,CAAC,WAAW,aAAa,aAAa,OAAO,CAAC,EACnD,SAAS,EACT,SAAS,iEAAiE;AAC/E,CAAC;AAED,MAAM,yCAAyC,EAAE,OAAO;AAAA,EACtD,aAAa,EAAE,OAAO,EAAE,SAAS,gFAAgF;AAAA,EACjH,WAAW,EAAE,OAAO,EAAE,SAAS,sEAAsE;AAAA,EACrG,gBAAgB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,+CAA+C;AAC9F,CAAC;AAED,MAAM,2CAA2C;AAEjD,MAAM,qCAA8D;AAAA,EAClE,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,aAAa,eAAe;AAAA,EACtD,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,oCAAoC,MAAM,QAAQ;AAChE,UAAM,MAAM,MAAM,mBAAmB,KAAK,UAAU,MAAM,SAAS;AACnE,QAAI,CAAC,IAAI,OAAO;AACd,aAAO,EAAE,OAAO,OAAgB,WAAW,MAAM,UAAU;AAAA,IAC7D;AACA,UAAM,iBAAiB,MAAM,kBAAkB;AAC/C,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,QACR,aAAa;AAAA,QACb,WAAW;AAAA,QACX,gBAAgB,CAAC;AAAA,MACnB;AAAA,MACA,SAAS;AAAA,QACP,SAAS,IAAI;AAAA,QACb;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,YAAY;AAAA,QACZ,YAAY,aAAa,wCAAwC,wCAAwC;AAAA,MAC3G;AAAA,IACF;AAAA,EACF;AACF;AAMA,MAAM,wCAAwC,EAAE,OAAO;AAAA,EACrD,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AAAA,EAClE,qBAAqB,EAClB,OAAO,EACP,IAAI,CAAC,EACL,SAAS,EACT,SAAS,mFAAoF;AAClG,CAAC;AAQD,MAAM,2CAA2C,EAAE,OAAO;AAAA,EACxD,YAAY,EACT,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,EAC9B,SAAS,kJAA6I;AAAA,EACzJ,YAAY,EACT,OAAO,EACP,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,oEAAoE;AAAA,EAChF,UAAU,EACP,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,uEAAuE;AACrF,CAAC;AAED,MAAM,6CAA6C;AAEnD,MAAM,uCAAgE;AAAA,EACpE,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,aAAa,eAAe;AAAA,EACtD,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,sCAAsC,MAAM,QAAQ;AAClE,UAAM,MAAM,MAAM,mBAAmB,KAAK,UAAU,MAAM,SAAS;AACnE,QAAI,CAAC,IAAI,OAAO;AACd,aAAO,EAAE,OAAO,OAAgB,WAAW,MAAM,UAAU;AAAA,IAC7D;AACA,UAAM,kBAAyC,MAAM;AAAA,MACnD;AAAA,MACA;AAAA,MACA,MAAM;AAAA,MACN;AAAA,IACF;AACA,UAAM,cAAc,MAAM,uBAAuB,IAAI,OAAO,QAAQ,eAAe;AACnF,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,QACR,YAAY,CAAC;AAAA,QACb,YAAY;AAAA,QACZ,UAAU,CAAC;AAAA,MACb;AAAA,MACA,SAAS;AAAA,QACP,SAAS,IAAI;AAAA,QACb;AAAA,QACA;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,YAAY;AAAA,QACZ,YAAY;AAAA,UACV;AAAA,UACA;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAMA,MAAM,iCAAiC,EAAE,OAAO;AAAA,EAC9C,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AAAA,EAClE,2BAA2B,EACxB,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,EACvB,IAAI,EAAE,EACN,SAAS,EACT,SAAS,+GAA+G;AAC7H,CAAC;AAED,MAAM,oCAAoC,EAAE,OAAO;AAAA,EACjD,aAAa,EAAE,OAAO,EAAE,SAAS,oDAAoD;AAAA,EACrF,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,6DAA6D;AAAA,EACpG,iBAAiB,EACd;AAAA,IACC,EAAE,OAAO;AAAA,MACP,cAAc,EAAE,OAAO;AAAA,MACvB,MAAM,EAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,CAAC;AAAA,EACH,EACC,SAAS,8EAA8E;AAC5F,CAAC;AAED,MAAM,sCAAsC;AAS5C,eAAe,cACb,IACA,KACA,UACA,eAC2B;AAC3B,MAAI,CAAC,cAAc,OAAQ,QAAO,CAAC;AACnC,QAAM,SAAS,MAAM,KAAK,IAAI,IAAI,aAAa,CAAC;AAChD,QAAM,QAAiC;AAAA,IACrC;AAAA,IACA,IAAI,EAAE,KAAK,OAAO;AAAA,EACpB;AACA,MAAI,IAAI,eAAgB,OAAM,iBAAiB,IAAI;AACnD,QAAM,OAAO,MAAM;AAAA,IACjB;AAAA,IACA;AAAA,IACA;AAAA,IACA,EAAE,OAAO,OAAO,OAAO;AAAA,IACvB,WAAW,KAAK,QAAQ;AAAA,EAC1B;AACA,QAAM,eAAe,KAAK,OAAO,CAAC,SAAS,IAAI,YAAY,UAAU,QAAQ;AAC7E,aAAW,WAAW,QAAQ;AAC5B,QAAI,CAAC,aAAa,KAAK,CAAC,QAAQ,IAAI,OAAO,OAAO,GAAG;AACnD,aAAO,KAAK,yEAAyE,EAAE,cAAc,QAAQ,CAAC;AAAA,IAChH;AAAA,EACF;AACA,SAAO,aAAa,IAAI,CAAC,SAAS;AAAA,IAChC,cAAc,IAAI;AAAA,IAClB,UAAU,IAAI;AAAA,IACd,WAAW,IAAI,YAAY;AAAA,IAC3B,MAAM,IAAI,YAAY;AAAA,EACxB,EAAE;AACJ;AAEA,MAAM,gCAAyD;AAAA,EAC7D,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,aAAa,iBAAiB,OAAO;AAAA,EAC/D,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,+BAA+B,MAAM,QAAQ;AAC3D,UAAM,MAAM,MAAM,mBAAmB,KAAK,UAAU,MAAM,SAAS;AACnE,QAAI,CAAC,IAAI,OAAO;AACd,aAAO,EAAE,OAAO,OAAgB,WAAW,MAAM,UAAU;AAAA,IAC7D;AACA,UAAM,KAAK,UAAU,GAAG;AACxB,UAAM,YAAY,MAAM,cAAc,IAAI,KAAK,UAAU,MAAM,6BAA6B,CAAC,CAAC;AAC9F,UAAM,eAAe,IAAI,OAAO,MAAM,IAAI,CAAC,WAAW;AAAA,MACpD,cAAc,MAAM;AAAA,MACpB,UAAU,MAAM;AAAA,MAChB,WAAW,MAAM;AAAA,MACjB,SAAS,MAAM;AAAA,MACf,WAAW,MAAM;AAAA,IACnB,EAAE;AACF,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,QACR,aAAa;AAAA,QACb,UAAU,CAAC;AAAA,QACX,iBAAiB,CAAC;AAAA,MACpB;AAAA,MACA,SAAS;AAAA,QACP,SAAS,IAAI;AAAA,QACb;AAAA,QACA;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,YAAY;AAAA,QACZ,YAAY,aAAa,mCAAmC,mCAAmC;AAAA,MACjG;AAAA,IACF;AAAA,EACF;AACF;AAMA,MAAM,4BAA4B,EAAE,OAAO;AAAA,EACzC,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AAAA,EAClE,aAAa,EACV,KAAK,CAAC,SAAS,OAAO,aAAa,CAAC,EACpC,SAAS,+EAA+E;AAAA,EAC3F,aAAa,EACV,OAAO,EACP,IAAI,EACJ,IAAI,CAAC,EACL,IAAI,CAAC,EACL,SAAS,EACT,SAAS,gEAAgE;AAC9E,CAAC;AAED,MAAM,+BAA+B,EAAE,OAAO;AAAA,EAC5C,UAAU,EACP;AAAA,IACC,EAAE,OAAO;AAAA,MACP,OAAO,EAAE,OAAO;AAAA,MAChB,OAAO,EAAE,OAAO;AAAA,MAChB,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,CAAC;AAAA,EACH,EACC,SAAS,4DAA4D;AAC1E,CAAC;AAED,MAAM,gCAAgC;AAEtC,MAAM,2BAAoD;AAAA,EACxD,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,uBAAuB;AAAA,EAC1C,MAAM,CAAC,QAAQ,WAAW,aAAa,eAAe;AAAA,EACtD,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,0BAA0B,MAAM,QAAQ;AACtD,UAAM,MAAM,MAAM,mBAAmB,KAAK,UAAU,MAAM,SAAS;AACnE,QAAI,CAAC,IAAI,OAAO;AACd,aAAO,EAAE,OAAO,OAAgB,WAAW,MAAM,UAAU;AAAA,IAC7D;AACA,UAAM,cAAc,KAAK,IAAI,MAAM,eAAe,GAAG,CAAC;AACtD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,QACR,UAAU,CAAC;AAAA,MACb;AAAA,MACA,SAAS;AAAA,QACP,SAAS,IAAI;AAAA,QACb,aAAa,MAAM;AAAA,QACnB;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,YAAY;AAAA,QACZ,YAAY,aAAa,8BAA8B,6BAA6B;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AACF;AAMA,MAAM,8BAA8B,EAAE,OAAO;AAAA,EAC3C,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,4BAA4B;AAAA,EAClE,QAAQ,EACL,OAAO,EACP,KAAK,EACL,IAAI,CAAC,EACL,SAAS,iFAAiF;AAAA,EAC7F,aAAa,EACV,OAAO,EACP,KAAK,EACL,SAAS,EACT,SAAS,4HAA4H;AAC1I,CAAC;AAED,MAAM,iCAAiC,EAAE,OAAO;AAAA,EAC9C,cAAc,EACX,MAAM;AAAA,IACL,EAAE,OAAO;AAAA,MACP,QAAQ,EAAE,OAAO;AAAA,MACjB,UAAU,EAAE,OAAO;AAAA,MACnB,aAAa,EAAE,OAAO;AAAA,IACxB,CAAC;AAAA,IACD,EAAE,KAAK;AAAA,EACT,CAAC,EACA,SAAS,+DAA+D;AAAA,EAC3E,eAAe,EAAE,OAAO;AAAA,IACtB,QAAQ,EAAE,OAAO;AAAA,IACjB,UAAU,EAAE,OAAO;AAAA,IACnB,aAAa,EAAE,OAAO;AAAA,EACxB,CAAC;AAAA,EACD,WAAW,EAAE,OAAO;AAAA,EACpB,aAAa,EAAE,OAAO;AAAA,IACpB,yBAAyB,EAAE,QAAQ;AAAA,IACnC,mBAAmB,EAAE,QAAQ;AAAA,EAC/B,CAAC;AACH,CAAC;AAED,MAAM,kCAAkC;AAQxC,SAAS,uBACP,QACA,aACsB;AACtB,QAAM,YAAY,OAAO,OAAO;AAChC,MAAI,CAAC,UAAU,OAAQ,QAAO;AAC9B,QAAM,aAAa,cACf,UAAU,OAAO,CAAC,QAAQ,IAAI,gBAAgB,WAAW,IACzD;AACJ,QAAM,OAAO,WAAW,CAAC,KAAK;AAC9B,MAAI,CAAC,KAAM,QAAO;AAClB,QAAM,YAAa,KAAK,kBAAkB,KAAK;AAC/C,QAAM,SAAS,cAAc,QAAQ,cAAc,SAAY,OAAO,OAAO,SAAS;AACtF,MAAI,WAAW,QAAQ,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACxD,QAAM,WAAY,KAAK,gBAAkC;AACzD,QAAM,eAAgB,KAAK,eAAiC;AAC5D,MAAI,CAAC,YAAY,CAAC,aAAc,QAAO;AACvC,SAAO,EAAE,QAAQ,UAAU,aAAa,aAAa;AACvD;AAEA,eAAe,wBACb,KACA,QACA,aACwD;AACxD,QAAM,iBAAiB,sBAAsB,GAAG;AAChD,MAAI,CAAC,eAAgB,QAAO;AAC5B,QAAM,YAAY,OAAO,OAAO;AAChC,MAAI,CAAC,UAAU,OAAQ,QAAO;AAC9B,QAAM,aAAa,cACf,UAAU,OAAO,CAAC,QAAQ,IAAI,gBAAgB,WAAW,IACzD;AACJ,MAAI,CAAC,WAAW,OAAQ,QAAO;AAC/B,MAAI;AACF,UAAM,iBAAiC,EAAE,UAAU,GAAG,MAAM,oBAAI,KAAK,EAAE;AACvE,UAAM,WAAW,MAAM,eAAe;AAAA,MACpC;AAAA,MACA;AAAA,IACF;AACA,QAAI,CAAC,SAAU,QAAO;AACtB,UAAM,YAAc,SAAiB,kBAAmB,SAAiB;AAKzE,UAAM,SAAS,cAAc,QAAQ,cAAc,SAAY,OAAO,OAAO,SAAS;AACtF,QAAI,WAAW,QAAQ,CAAC,OAAO,SAAS,MAAM,EAAG,QAAO;AACxD,UAAM,WAAa,SAAiB,gBAAkC;AACtE,UAAM,eAAiB,SAAiB,gBACjC,SAAiB,aAAa,OAAQ,SAAiB,cAAc,WACrE,SAAiB,UAAU,KAC3B,SAAiB;AACxB,QAAI,CAAC,YAAY,CAAC,aAAc,QAAO;AACvC,WAAO,EAAE,QAAQ,UAAU,aAAa,aAAa;AAAA,EACvD,QAAQ;AAIN,WAAO;AAAA,EACT;AACF;AAEA,MAAM,6BAAsD;AAAA,EAC1D,MAAM;AAAA,EACN,aAAa;AAAA,EACb,aACE;AAAA,EACF,aAAa;AAAA,EACb,kBAAkB,CAAC,wBAAwB;AAAA,EAC3C,MAAM,CAAC,QAAQ,WAAW,aAAa,iBAAiB,SAAS;AAAA,EACjE,YAAY;AAAA,EACZ,SAAS,OAAO,UAAU,QAAQ;AAChC,UAAM,EAAE,SAAS,IAAI,kBAAkB,GAAG;AAC1C,UAAM,QAAQ,4BAA4B,MAAM,QAAQ;AACxD,UAAM,MAAM,MAAM,mBAAmB,KAAK,UAAU,MAAM,SAAS;AACnE,QAAI,CAAC,IAAI,OAAO;AACd,aAAO,EAAE,OAAO,OAAgB,WAAW,MAAM,UAAU;AAAA,IAC7D;AACA,UAAM,gBAAgB,MAAM,wBAAwB,KAAK,IAAI,QAAQ,MAAM,WAAW;AACtF,UAAM,eACJ,kBAAkB,yBACd,uBAAuB,IAAI,QAAQ,MAAM,WAAW,IACpD;AACN,UAAM,mBAAmB,MAAM,mBAAmB,KAAK,EAAE,OAAO,IAAI,GAAG,QAAQ;AAC/E,UAAM,sBAAsB,iBAAiB,MAAM,IAAI,CAAC,SAAS;AAAA,MAC/D,IAAI,IAAI;AAAA,MACR,MAAM,IAAI;AAAA,MACV,OAAO,IAAI,iBAAkB,iBAA4B;AAAA,IAC3D,EAAE;AACF,WAAO;AAAA,MACL,OAAO;AAAA,MACP,UAAU;AAAA,QACR;AAAA,QACA,eAAe;AAAA,UACb,QAAQ,cAAc,UAAU;AAAA,UAChC,UAAU,cAAc,aAAa,IAAI,OAAO,QAAQ,uBAAuB;AAAA,UAC/E,aAAa,cAAc,gBAAgB,MAAM,eAAe;AAAA,QAClE;AAAA,QACA,WAAW;AAAA,QACX,aAAa;AAAA,UACX,yBAAyB;AAAA,UACzB,mBAAmB;AAAA,QACrB;AAAA,MACF;AAAA,MACA,SAAS;AAAA,QACP,SAAS,IAAI;AAAA,QACb,QAAQ,MAAM;AAAA,QACd;AAAA,MACF;AAAA,MACA,wBAAwB;AAAA,QACtB,YAAY;AAAA,QACZ,YAAY,aAAa,gCAAgC,+BAA+B;AAAA,MAC1F;AAAA,IACF;AAAA,EACF;AACF;AAMO,MAAM,mBAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAEA,IAAO,yBAAQ;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
toPriceNumeric,
|
|
22
22
|
toProductSummary
|
|
23
23
|
} from "./_shared.js";
|
|
24
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
25
|
+
const logger = createLogger("catalog");
|
|
24
26
|
const CATALOG_PRODUCT_ENTITY = "catalog:catalog_product";
|
|
25
27
|
function resolveSearchService(ctx) {
|
|
26
28
|
try {
|
|
@@ -262,7 +264,7 @@ const listSelectedProductsTool = {
|
|
|
262
264
|
items.push(entry.result);
|
|
263
265
|
} else {
|
|
264
266
|
missingIds.push(entry.productId);
|
|
265
|
-
|
|
267
|
+
logger.warn("catalog.list_selected_products product not in scope", { productId: entry.productId });
|
|
266
268
|
}
|
|
267
269
|
}
|
|
268
270
|
return { items, missingIds };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/catalog/ai-tools/merchandising-pack.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * D18 catalog merchandising read tools (Phase 1 WS-C, Step 3.11).\n *\n * Ships the seven canonical tool names the `catalog.merchandising_assistant`\n * agent (Step 4.9) whitelists verbatim:\n *\n * - `catalog.search_products` \u2014 fulltext + filter search (hybrid path).\n * - `catalog.get_product_bundle` \u2014 aggregate bundle for a single product.\n * - `catalog.list_selected_products` \u2014 bundle aggregate for an ID array.\n * - `catalog.get_product_media` \u2014 media metadata (attachment IDs only \u2014\n * the Step 3.7 attachment bridge converts these to model file parts at\n * runtime invocation; this tool does NOT call the bridge directly).\n * - `catalog.get_attribute_schema` \u2014 merged module + category + product\n * custom-field schema.\n * - `catalog.get_category_brief` \u2014 category snapshot with inherited schema.\n * - `catalog.list_price_kinds` \u2014 D18 spec-named price-kind enumerator.\n *\n * Every tool is read-only (no `isMutation: true`). Mutation tooling for D18\n * lands in Step 5.14 under the pending-action contract.\n *\n * Tenant scoping: all DB access uses `findWithDecryption` /\n * `findOneWithDecryption`, plus a defensive post-filter against\n * `row.tenantId === ctx.tenantId`. Cross-tenant IDs surface through the\n * `missingIds` output (not an error), so a chat agent receives a uniform\n * not-found signal whether the product is missing, deleted, or out of scope.\n *\n * Shared helpers:\n * `list_price_kinds` + Step 3.10's `list_price_kinds_base` both route\n * through `listPriceKindsCore` in `./_shared.ts`; there is no duplicate\n * query path.\n *\n * Step 3.12 (authoring pack) promoted the product-bundle builder plus\n * `toProductSummary` / `resolveAttributeSchema` into `./_shared.ts` so\n * both packs consume the same loader. This file now just wires the\n * shared helpers into tool handlers.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { type QueryEngine, type QueryResult, SortDir } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n} from '../data/entities'\nimport { assertTenantScope, type CatalogAiToolDefinition, type CatalogToolContext } from './types'\nimport {\n buildProductBundle,\n buildScope,\n listPriceKindsCore,\n resolveAttributeSchema,\n resolveEm,\n toPriceNumeric,\n toProductSummary,\n type ProductBundle,\n type ProductBundleResult,\n} from './_shared'\n\ntype SearchServiceLike = {\n search: (query: string, options: {\n tenantId: string\n organizationId?: string | null\n limit?: number\n entityTypes?: string[]\n }) => Promise<Array<{\n entityId: string\n recordId: string\n score: number\n source: string\n presenter?: unknown\n url?: string\n }>>\n}\n\nconst CATALOG_PRODUCT_ENTITY = 'catalog:catalog_product'\n\nfunction resolveSearchService(ctx: CatalogToolContext): SearchServiceLike | null {\n try {\n return ctx.container.resolve<SearchServiceLike>('searchService')\n } catch {\n return null\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.search_products */\n/* -------------------------------------------------------------------------- */\n\nconst searchProductsInput = z\n .object({\n q: z.string().trim().optional().describe('Optional fulltext query (title / subtitle / sku / handle). Omit or leave empty (or do NOT pass it at all) to list all products. NEVER use \"*\", \"**\", or \"%\" \u2014 they are not wildcards and will be discarded.'),\n limit: z.number().int().min(1).max(100).optional().describe('Page size. Default 50, hard maximum 100. Results are paginated: when `total` exceeds `limit + offset`, call again with the next `offset` instead of asking for more than 100 rows.'),\n offset: z.number().int().min(0).optional().describe('Rows to skip for pagination (default 0). Combine with `limit` to fetch subsequent pages \u2014 for example offset=100, limit=100 fetches rows 101..200.'),\n categoryId: z.string().optional().describe('Restrict to products assigned to this catalog category UUID. Only use a category ID returned by a previous tool call \u2014 do NOT guess. Empty string is treated the same as omitting the field.'),\n priceMin: z.number().optional().describe('Lower-bound (inclusive) on the gross unit price. OMIT this field when you do not want a lower bound \u2014 do NOT pass 0 as \"no minimum\", because 0 is a real bound that combined with priceMax=0 will return only free products.'),\n priceMax: z.number().optional().describe('Upper-bound (inclusive) on the gross unit price. OMIT this field when you do not want an upper bound \u2014 do NOT pass 0 as \"no maximum\", because 0 is a real bound that will exclude every priced product.'),\n tags: z.array(z.string()).optional().describe('Tag labels or slugs (any-match) the product carries. Omit or pass an empty array to skip the tag filter.'),\n active: z.boolean().optional().describe('When true, only active products are returned. Omit to include inactive products as well.'),\n })\n .passthrough()\n\ntype SearchProductsInput = z.infer<typeof searchProductsInput>\n\nasync function queryProductsWithFilters(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n input: SearchProductsInput,\n restrictToIds: string[] | null,\n): Promise<{ items: ReturnType<typeof toProductSummary>[]; total: number }> {\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n let idRestriction: string[] | null = restrictToIds\n ? Array.from(new Set(restrictToIds))\n : null\n\n if (input.categoryId) {\n const assignments = await findWithDecryption<CatalogProductCategoryAssignment>(\n em,\n CatalogProductCategoryAssignment,\n { tenantId, category: input.categoryId } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const ids = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => ids.includes(id))\n : ids\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.tags && input.tags.length > 0) {\n const tagWhere: Record<string, unknown> = {\n tenantId,\n $or: [\n { slug: { $in: input.tags } },\n { label: { $in: input.tags } },\n ],\n }\n if (ctx.organizationId) tagWhere.organizationId = ctx.organizationId\n const tags = await findWithDecryption<CatalogProductTag>(\n em,\n CatalogProductTag,\n tagWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const tagIds = tags.map((tag) => tag.id)\n if (!tagIds.length) return { items: [], total: 0 }\n const assignments = await findWithDecryption<CatalogProductTagAssignment>(\n em,\n CatalogProductTagAssignment,\n { tenantId, tag: { $in: tagIds } } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const scopedIds = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : scopedIds\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.priceMin !== undefined || input.priceMax !== undefined) {\n const priceWhere: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) priceWhere.organizationId = ctx.organizationId\n const priceRows = await findWithDecryption<CatalogProductPrice>(\n em,\n CatalogProductPrice,\n priceWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const bounded = priceRows.filter((row) => {\n const net = toPriceNumeric(row.unitPriceNet ?? null)\n const gross = toPriceNumeric(row.unitPriceGross ?? null)\n const comparable = gross ?? net\n if (comparable === null) return false\n if (input.priceMin !== undefined && comparable < input.priceMin) return false\n if (input.priceMax !== undefined && comparable > input.priceMax) return false\n return true\n })\n const scopedIds = bounded\n .map((row) => {\n const product = (row as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : Array.from(new Set(scopedIds))\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n const filters: Record<string, unknown> = {}\n if (input.active === true) filters.is_active = true\n if (input.q) {\n const pattern = `%${input.q}%`\n filters.$or = [\n { title: { $ilike: pattern } },\n { subtitle: { $ilike: pattern } },\n { sku: { $ilike: pattern } },\n { handle: { $ilike: pattern } },\n ]\n }\n if (idRestriction) {\n filters.id = { $in: idRestriction }\n }\n\n const qe = ctx.container.resolve<QueryEngine>('queryEngine')\n const result: QueryResult = await qe.query('catalog:catalog_product', {\n filters,\n sort: [{ field: 'created_at', dir: SortDir.Desc }],\n page: { page: Math.floor(offset / limit) + 1, pageSize: limit },\n tenantId,\n organizationId: ctx.organizationId ?? undefined,\n })\n\n const productIds = result.items\n .map((row: Record<string, unknown>) => row.id as string)\n .filter((id): id is string => typeof id === 'string')\n if (!productIds.length) return { items: [], total: result.total }\n\n const products = await findWithDecryption<CatalogProduct>(\n em,\n CatalogProduct,\n { tenantId, id: { $in: productIds }, deletedAt: null } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const productById = new Map(products.map((p) => [p.id, p]))\n const ordered = productIds\n .map((id) => productById.get(id))\n .filter((p): p is CatalogProduct => !!p)\n\n return { items: ordered.map(toProductSummary), total: result.total }\n}\n\nconst searchProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.search_products',\n displayName: 'Search products',\n description:\n 'Hybrid search + filter across tenant products. When `q` is non-empty, routes through the search service (tenant + organization scoped) then hydrates tenant-scoped product summaries; when `q` is empty or omitted, runs the catalog query engine with the supplied filters and returns ALL products. ' +\n 'Pagination: response shape is `{ items, total, limit, offset, source }`. Hard cap is 100 rows per call (`limit` max=100, default=50); if `total` exceeds `limit + offset`, call again with the next `offset` rather than asking for >100. ' +\n 'Empty / sentinel inputs: pass an empty `q` (or omit it) to list all products. NEVER pass `0` for `priceMin`/`priceMax` to mean \"no bound\" \u2014 `0` is a real numeric bound (priceMin=0 + priceMax=0 returns only free products). To remove a bound, OMIT the field. Empty string `categoryId` and empty `tags` arrays are ignored. ' +\n '`source` in the output indicates which path executed (`search_service` vs `query_engine`).',\n inputSchema: searchProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = searchProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n // Treat wildcard-style and empty queries as \"list all\".\n if (!input.q || input.q === '*' || input.q === '**' || input.q === '%') {\n input.q = undefined\n }\n\n // Empty-string sentinel for `categoryId` is the agent's way of saying\n // \"no category filter\" \u2014 normalize before the UUID guard runs.\n if (typeof input.categoryId === 'string' && input.categoryId.trim() === '') {\n input.categoryId = undefined\n }\n\n // Empty `tags` array is meaningless as a filter; drop it so the price /\n // category narrowing path doesn't execute a no-op tag join.\n if (Array.isArray(input.tags) && input.tags.length === 0) {\n input.tags = undefined\n }\n\n // Agents commonly pass `priceMin: 0` / `priceMax: 0` thinking 0 means\n // \"no bound\". It does not \u2014 0 is a real inclusive bound and the two\n // together ask for free products only. Treat the (0, 0) pair as\n // \"unset\" so the empty-bound default carries through. A standalone 0\n // (e.g. priceMin=0 with priceMax=50) is left intact because it can be\n // a meaningful \"everything from free up to 50\" filter.\n if (\n input.priceMin === 0 &&\n input.priceMax === 0\n ) {\n input.priceMin = undefined\n input.priceMax = undefined\n }\n\n // Guard against hallucinated or invalid category IDs.\n if (input.categoryId) {\n const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n if (!uuidPattern.test(input.categoryId)) {\n input.categoryId = undefined\n } else {\n try {\n const catExists = await em.count(CatalogProductCategory, { id: input.categoryId, tenantId } as any)\n if (catExists === 0) input.categoryId = undefined\n } catch {\n input.categoryId = undefined\n }\n }\n }\n\n if (input.q && input.q.trim().length > 0) {\n const service = resolveSearchService(ctx)\n if (service) {\n const hits = await service.search(input.q.trim(), {\n tenantId,\n organizationId: ctx.organizationId,\n limit,\n entityTypes: [CATALOG_PRODUCT_ENTITY],\n })\n const hitIds = hits\n .filter((hit) => hit.entityId === CATALOG_PRODUCT_ENTITY)\n .map((hit) => hit.recordId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n if (!hitIds.length) {\n return { items: [], total: 0, limit, offset, source: 'search_service' as const }\n }\n // Hydrate tenant-scoped products from the hit IDs, then apply any\n // structured filters the search service can't express (category /\n // price / tags / active). Search services in this repo do not\n // currently accept structured filters, so we narrow in-process and\n // document that in the return report + description.\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, { ...input, q: undefined }, hitIds)\n return { items, total, limit, offset, source: 'search_service' as const }\n }\n // Fall through to the query-engine path if the search service is not\n // registered in the DI container \u2014 keeps the tool usable in test\n // harnesses and during early bring-up.\n }\n\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, input, null)\n return { items, total, limit, offset, source: 'query_engine' as const }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_bundle / catalog.list_selected_products */\n/* -------------------------------------------------------------------------- */\n\nconst getProductBundleInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n})\n\nconst getProductBundleTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_bundle',\n displayName: 'Get product bundle',\n description:\n 'Aggregate product snapshot for D18 merchandising: core fields + categories + tags + variants + prices (base + best via pricing service) + media metadata + custom-field values + merged attribute schema. Media bytes flow through the attachment bridge (use `catalog.get_product_media` then the bridge). Returns `{ found: false }` on miss or cross-tenant access.',\n inputSchema: getProductBundleInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductBundleInput.parse(rawInput)\n const em = resolveEm(ctx)\n return buildProductBundle(em, ctx, tenantId, input.productId)\n },\n}\n\nconst listSelectedProductsInput = z.object({\n productIds: z\n .array(z.string().uuid())\n .min(1)\n .max(50)\n .describe('1..50 catalog product ids (UUIDs). Duplicates are collapsed; cross-tenant ids drop into `missingIds`.'),\n})\n\nconst listSelectedProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_selected_products',\n displayName: 'List selected products (bundles)',\n description:\n 'Bulk variant of `catalog.get_product_bundle`: resolves 1..50 product ids into tenant-scoped bundle aggregates. Missing / cross-tenant ids are returned in `missingIds` (not as an error) so selection-aware agents can render partial results.',\n inputSchema: listSelectedProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listSelectedProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const unique = Array.from(new Set(input.productIds))\n const resolved = await Promise.all(\n unique.map(async (productId) => ({ productId, result: await buildProductBundle(em, ctx, tenantId, productId) })),\n )\n const items: ProductBundle[] = []\n const missingIds: string[] = []\n for (const entry of resolved) {\n if (entry.result.found) {\n items.push(entry.result)\n } else {\n missingIds.push(entry.productId)\n console.warn(`[catalog.list_selected_products] product not in scope: ${entry.productId}`)\n }\n }\n return { items, missingIds }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_media */\n/* -------------------------------------------------------------------------- */\n\nconst getProductMediaInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n})\n\nconst getProductMediaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_media',\n displayName: 'Get product media (with attachment IDs)',\n description:\n 'List media records attached to a product with metadata (filename, mime, size, sort order) and the `attachmentId` string for each row. Does NOT invoke the attachment bridge \u2014 the Step 3.7 runtime bridge converts attachment ids into model file parts when the chat/object helper invokes this tool in-context. Returns `{ items, total, limit, offset }`.',\n inputSchema: getProductMediaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductMediaInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = {\n tenantId,\n entityId: E.catalog.catalog_product,\n recordId: input.productId,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const [rows, total] = await Promise.all([\n findWithDecryption<Attachment>(\n em,\n Attachment,\n where as any,\n { limit, offset, orderBy: { createdAt: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(Attachment, where as any),\n ])\n const filtered = rows.filter((row) => (row.tenantId ?? null) === tenantId)\n return {\n items: filtered.map((row) => ({\n mediaId: row.id,\n productId: input.productId,\n attachmentId: row.id,\n fileName: row.fileName,\n mediaType: row.mimeType,\n size: row.fileSize,\n altText: null,\n sortOrder: 0,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_attribute_schema / catalog.get_category_brief */\n/* -------------------------------------------------------------------------- */\n\nconst getAttributeSchemaInput = z.object({\n productId: z.string().uuid().optional().describe('Narrow schema resolution to this product (scope: `product`).'),\n categoryId: z.string().uuid().optional().describe('Narrow schema resolution to this category (scope: `category`).'),\n})\n\nconst getAttributeSchemaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_attribute_schema',\n displayName: 'Get attribute schema',\n description:\n 'Resolve the merged custom-field attribute schema for catalog products. When both `productId` and `categoryId` are absent, returns module-level fields only. Reuses the shared `loadCustomFieldDefinitionIndex` resolver so tenant + organization scoping rules stay consistent with CrudForm / admin routes.',\n inputSchema: getAttributeSchemaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getAttributeSchemaInput.parse(rawInput)\n return resolveAttributeSchema(ctx, tenantId, input.productId, input.categoryId)\n },\n}\n\nconst getCategoryBriefInput = z.object({\n categoryId: z.string().uuid().describe('Category id (UUID).'),\n})\n\nconst getCategoryBriefTool: CatalogAiToolDefinition = {\n name: 'catalog.get_category_brief',\n displayName: 'Get category brief',\n description:\n 'Category name, full tree path, description, and merged attribute schema (same resolver as `catalog.get_attribute_schema` with `categoryId`). Returns `{ found: false }` on miss / cross-tenant.',\n inputSchema: getCategoryBriefInput,\n requiredFeatures: ['catalog.categories.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getCategoryBriefInput.parse(rawInput)\n const em = resolveEm(ctx)\n const where: Record<string, unknown> = {\n id: input.categoryId,\n tenantId,\n deletedAt: null,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const category = await findOneWithDecryption<CatalogProductCategory>(\n em,\n CatalogProductCategory,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!category || category.tenantId !== tenantId) {\n return { found: false as const, categoryId: input.categoryId }\n }\n const attributeSchema = await resolveAttributeSchema(ctx, tenantId, undefined, category.id)\n return {\n found: true as const,\n id: category.id,\n name: category.name,\n path: category.treePath ?? null,\n description: category.description ?? null,\n attributeSchema,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.list_price_kinds */\n/* -------------------------------------------------------------------------- */\n\nconst listPriceKindsInput = z\n .object({\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listPriceKindsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_price_kinds',\n displayName: 'List price kinds',\n description:\n 'Enumerate tenant price kinds for the D18 merchandising assistant. Shares the tenant-scoped query path with `catalog.list_price_kinds_base`; the two tools differ only in description/framing (the base tool is the low-level settings enumerator, this one is the spec-named D18 surface).',\n inputSchema: listPriceKindsInput,\n requiredFeatures: ['catalog.settings.manage'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listPriceKindsInput.parse(rawInput)\n const base = await listPriceKindsCore(ctx, input, tenantId)\n return {\n items: base.items.map((row) => ({\n id: row.id,\n code: row.code,\n name: row.title,\n scope: row.organizationId ? ('organization' as const) : ('tenant' as const),\n currency: row.currencyCode,\n appliesTo: row.isPromotion ? ('promotion' as const) : ('regular' as const),\n })),\n total: base.total,\n limit: base.limit,\n offset: base.offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* Export */\n/* -------------------------------------------------------------------------- */\n\nexport const merchandisingAiTools: CatalogAiToolDefinition[] = [\n searchProductsTool,\n getProductBundleTool,\n listSelectedProductsTool,\n getProductMediaTool,\n getAttributeSchemaTool,\n getCategoryBriefTool,\n listPriceKindsTool,\n]\n\nexport default merchandisingAiTools\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["/**\n * D18 catalog merchandising read tools (Phase 1 WS-C, Step 3.11).\n *\n * Ships the seven canonical tool names the `catalog.merchandising_assistant`\n * agent (Step 4.9) whitelists verbatim:\n *\n * - `catalog.search_products` \u2014 fulltext + filter search (hybrid path).\n * - `catalog.get_product_bundle` \u2014 aggregate bundle for a single product.\n * - `catalog.list_selected_products` \u2014 bundle aggregate for an ID array.\n * - `catalog.get_product_media` \u2014 media metadata (attachment IDs only \u2014\n * the Step 3.7 attachment bridge converts these to model file parts at\n * runtime invocation; this tool does NOT call the bridge directly).\n * - `catalog.get_attribute_schema` \u2014 merged module + category + product\n * custom-field schema.\n * - `catalog.get_category_brief` \u2014 category snapshot with inherited schema.\n * - `catalog.list_price_kinds` \u2014 D18 spec-named price-kind enumerator.\n *\n * Every tool is read-only (no `isMutation: true`). Mutation tooling for D18\n * lands in Step 5.14 under the pending-action contract.\n *\n * Tenant scoping: all DB access uses `findWithDecryption` /\n * `findOneWithDecryption`, plus a defensive post-filter against\n * `row.tenantId === ctx.tenantId`. Cross-tenant IDs surface through the\n * `missingIds` output (not an error), so a chat agent receives a uniform\n * not-found signal whether the product is missing, deleted, or out of scope.\n *\n * Shared helpers:\n * `list_price_kinds` + Step 3.10's `list_price_kinds_base` both route\n * through `listPriceKindsCore` in `./_shared.ts`; there is no duplicate\n * query path.\n *\n * Step 3.12 (authoring pack) promoted the product-bundle builder plus\n * `toProductSummary` / `resolveAttributeSchema` into `./_shared.ts` so\n * both packs consume the same loader. This file now just wires the\n * shared helpers into tool handlers.\n */\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { type QueryEngine, type QueryResult, SortDir } from '@open-mercato/shared/lib/query/types'\nimport { E } from '#generated/entities.ids.generated'\nimport { Attachment } from '@open-mercato/core/modules/attachments/data/entities'\nimport {\n CatalogProduct,\n CatalogProductCategory,\n CatalogProductCategoryAssignment,\n CatalogProductPrice,\n CatalogProductTag,\n CatalogProductTagAssignment,\n} from '../data/entities'\nimport { assertTenantScope, type CatalogAiToolDefinition, type CatalogToolContext } from './types'\nimport {\n buildProductBundle,\n buildScope,\n listPriceKindsCore,\n resolveAttributeSchema,\n resolveEm,\n toPriceNumeric,\n toProductSummary,\n type ProductBundle,\n type ProductBundleResult,\n} from './_shared'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\ntype SearchServiceLike = {\n search: (query: string, options: {\n tenantId: string\n organizationId?: string | null\n limit?: number\n entityTypes?: string[]\n }) => Promise<Array<{\n entityId: string\n recordId: string\n score: number\n source: string\n presenter?: unknown\n url?: string\n }>>\n}\n\nconst CATALOG_PRODUCT_ENTITY = 'catalog:catalog_product'\n\nfunction resolveSearchService(ctx: CatalogToolContext): SearchServiceLike | null {\n try {\n return ctx.container.resolve<SearchServiceLike>('searchService')\n } catch {\n return null\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.search_products */\n/* -------------------------------------------------------------------------- */\n\nconst searchProductsInput = z\n .object({\n q: z.string().trim().optional().describe('Optional fulltext query (title / subtitle / sku / handle). Omit or leave empty (or do NOT pass it at all) to list all products. NEVER use \"*\", \"**\", or \"%\" \u2014 they are not wildcards and will be discarded.'),\n limit: z.number().int().min(1).max(100).optional().describe('Page size. Default 50, hard maximum 100. Results are paginated: when `total` exceeds `limit + offset`, call again with the next `offset` instead of asking for more than 100 rows.'),\n offset: z.number().int().min(0).optional().describe('Rows to skip for pagination (default 0). Combine with `limit` to fetch subsequent pages \u2014 for example offset=100, limit=100 fetches rows 101..200.'),\n categoryId: z.string().optional().describe('Restrict to products assigned to this catalog category UUID. Only use a category ID returned by a previous tool call \u2014 do NOT guess. Empty string is treated the same as omitting the field.'),\n priceMin: z.number().optional().describe('Lower-bound (inclusive) on the gross unit price. OMIT this field when you do not want a lower bound \u2014 do NOT pass 0 as \"no minimum\", because 0 is a real bound that combined with priceMax=0 will return only free products.'),\n priceMax: z.number().optional().describe('Upper-bound (inclusive) on the gross unit price. OMIT this field when you do not want an upper bound \u2014 do NOT pass 0 as \"no maximum\", because 0 is a real bound that will exclude every priced product.'),\n tags: z.array(z.string()).optional().describe('Tag labels or slugs (any-match) the product carries. Omit or pass an empty array to skip the tag filter.'),\n active: z.boolean().optional().describe('When true, only active products are returned. Omit to include inactive products as well.'),\n })\n .passthrough()\n\ntype SearchProductsInput = z.infer<typeof searchProductsInput>\n\nasync function queryProductsWithFilters(\n em: EntityManager,\n ctx: CatalogToolContext,\n tenantId: string,\n input: SearchProductsInput,\n restrictToIds: string[] | null,\n): Promise<{ items: ReturnType<typeof toProductSummary>[]; total: number }> {\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n let idRestriction: string[] | null = restrictToIds\n ? Array.from(new Set(restrictToIds))\n : null\n\n if (input.categoryId) {\n const assignments = await findWithDecryption<CatalogProductCategoryAssignment>(\n em,\n CatalogProductCategoryAssignment,\n { tenantId, category: input.categoryId } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const ids = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!ids.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => ids.includes(id))\n : ids\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.tags && input.tags.length > 0) {\n const tagWhere: Record<string, unknown> = {\n tenantId,\n $or: [\n { slug: { $in: input.tags } },\n { label: { $in: input.tags } },\n ],\n }\n if (ctx.organizationId) tagWhere.organizationId = ctx.organizationId\n const tags = await findWithDecryption<CatalogProductTag>(\n em,\n CatalogProductTag,\n tagWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const tagIds = tags.map((tag) => tag.id)\n if (!tagIds.length) return { items: [], total: 0 }\n const assignments = await findWithDecryption<CatalogProductTagAssignment>(\n em,\n CatalogProductTagAssignment,\n { tenantId, tag: { $in: tagIds } } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const scopedIds = assignments\n .map((assignment) => {\n const product = (assignment as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : scopedIds\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n if (input.priceMin !== undefined || input.priceMax !== undefined) {\n const priceWhere: Record<string, unknown> = { tenantId }\n if (ctx.organizationId) priceWhere.organizationId = ctx.organizationId\n const priceRows = await findWithDecryption<CatalogProductPrice>(\n em,\n CatalogProductPrice,\n priceWhere as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const bounded = priceRows.filter((row) => {\n const net = toPriceNumeric(row.unitPriceNet ?? null)\n const gross = toPriceNumeric(row.unitPriceGross ?? null)\n const comparable = gross ?? net\n if (comparable === null) return false\n if (input.priceMin !== undefined && comparable < input.priceMin) return false\n if (input.priceMax !== undefined && comparable > input.priceMax) return false\n return true\n })\n const scopedIds = bounded\n .map((row) => {\n const product = (row as any).product\n if (!product) return null\n return typeof product === 'string' ? product : product.id ?? null\n })\n .filter((value: string | null): value is string => typeof value === 'string' && value.length > 0)\n if (!scopedIds.length) return { items: [], total: 0 }\n idRestriction = idRestriction\n ? idRestriction.filter((id) => scopedIds.includes(id))\n : Array.from(new Set(scopedIds))\n if (!idRestriction.length) return { items: [], total: 0 }\n }\n\n const filters: Record<string, unknown> = {}\n if (input.active === true) filters.is_active = true\n if (input.q) {\n const pattern = `%${input.q}%`\n filters.$or = [\n { title: { $ilike: pattern } },\n { subtitle: { $ilike: pattern } },\n { sku: { $ilike: pattern } },\n { handle: { $ilike: pattern } },\n ]\n }\n if (idRestriction) {\n filters.id = { $in: idRestriction }\n }\n\n const qe = ctx.container.resolve<QueryEngine>('queryEngine')\n const result: QueryResult = await qe.query('catalog:catalog_product', {\n filters,\n sort: [{ field: 'created_at', dir: SortDir.Desc }],\n page: { page: Math.floor(offset / limit) + 1, pageSize: limit },\n tenantId,\n organizationId: ctx.organizationId ?? undefined,\n })\n\n const productIds = result.items\n .map((row: Record<string, unknown>) => row.id as string)\n .filter((id): id is string => typeof id === 'string')\n if (!productIds.length) return { items: [], total: result.total }\n\n const products = await findWithDecryption<CatalogProduct>(\n em,\n CatalogProduct,\n { tenantId, id: { $in: productIds }, deletedAt: null } as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n const productById = new Map(products.map((p) => [p.id, p]))\n const ordered = productIds\n .map((id) => productById.get(id))\n .filter((p): p is CatalogProduct => !!p)\n\n return { items: ordered.map(toProductSummary), total: result.total }\n}\n\nconst searchProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.search_products',\n displayName: 'Search products',\n description:\n 'Hybrid search + filter across tenant products. When `q` is non-empty, routes through the search service (tenant + organization scoped) then hydrates tenant-scoped product summaries; when `q` is empty or omitted, runs the catalog query engine with the supplied filters and returns ALL products. ' +\n 'Pagination: response shape is `{ items, total, limit, offset, source }`. Hard cap is 100 rows per call (`limit` max=100, default=50); if `total` exceeds `limit + offset`, call again with the next `offset` rather than asking for >100. ' +\n 'Empty / sentinel inputs: pass an empty `q` (or omit it) to list all products. NEVER pass `0` for `priceMin`/`priceMax` to mean \"no bound\" \u2014 `0` is a real numeric bound (priceMin=0 + priceMax=0 returns only free products). To remove a bound, OMIT the field. Empty string `categoryId` and empty `tags` arrays are ignored. ' +\n '`source` in the output indicates which path executed (`search_service` vs `query_engine`).',\n inputSchema: searchProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = searchProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n\n // Treat wildcard-style and empty queries as \"list all\".\n if (!input.q || input.q === '*' || input.q === '**' || input.q === '%') {\n input.q = undefined\n }\n\n // Empty-string sentinel for `categoryId` is the agent's way of saying\n // \"no category filter\" \u2014 normalize before the UUID guard runs.\n if (typeof input.categoryId === 'string' && input.categoryId.trim() === '') {\n input.categoryId = undefined\n }\n\n // Empty `tags` array is meaningless as a filter; drop it so the price /\n // category narrowing path doesn't execute a no-op tag join.\n if (Array.isArray(input.tags) && input.tags.length === 0) {\n input.tags = undefined\n }\n\n // Agents commonly pass `priceMin: 0` / `priceMax: 0` thinking 0 means\n // \"no bound\". It does not \u2014 0 is a real inclusive bound and the two\n // together ask for free products only. Treat the (0, 0) pair as\n // \"unset\" so the empty-bound default carries through. A standalone 0\n // (e.g. priceMin=0 with priceMax=50) is left intact because it can be\n // a meaningful \"everything from free up to 50\" filter.\n if (\n input.priceMin === 0 &&\n input.priceMax === 0\n ) {\n input.priceMin = undefined\n input.priceMax = undefined\n }\n\n // Guard against hallucinated or invalid category IDs.\n if (input.categoryId) {\n const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n if (!uuidPattern.test(input.categoryId)) {\n input.categoryId = undefined\n } else {\n try {\n const catExists = await em.count(CatalogProductCategory, { id: input.categoryId, tenantId } as any)\n if (catExists === 0) input.categoryId = undefined\n } catch {\n input.categoryId = undefined\n }\n }\n }\n\n if (input.q && input.q.trim().length > 0) {\n const service = resolveSearchService(ctx)\n if (service) {\n const hits = await service.search(input.q.trim(), {\n tenantId,\n organizationId: ctx.organizationId,\n limit,\n entityTypes: [CATALOG_PRODUCT_ENTITY],\n })\n const hitIds = hits\n .filter((hit) => hit.entityId === CATALOG_PRODUCT_ENTITY)\n .map((hit) => hit.recordId)\n .filter((id): id is string => typeof id === 'string' && id.length > 0)\n if (!hitIds.length) {\n return { items: [], total: 0, limit, offset, source: 'search_service' as const }\n }\n // Hydrate tenant-scoped products from the hit IDs, then apply any\n // structured filters the search service can't express (category /\n // price / tags / active). Search services in this repo do not\n // currently accept structured filters, so we narrow in-process and\n // document that in the return report + description.\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, { ...input, q: undefined }, hitIds)\n return { items, total, limit, offset, source: 'search_service' as const }\n }\n // Fall through to the query-engine path if the search service is not\n // registered in the DI container \u2014 keeps the tool usable in test\n // harnesses and during early bring-up.\n }\n\n const { items, total } = await queryProductsWithFilters(em, ctx, tenantId, input, null)\n return { items, total, limit, offset, source: 'query_engine' as const }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_bundle / catalog.list_selected_products */\n/* -------------------------------------------------------------------------- */\n\nconst getProductBundleInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n})\n\nconst getProductBundleTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_bundle',\n displayName: 'Get product bundle',\n description:\n 'Aggregate product snapshot for D18 merchandising: core fields + categories + tags + variants + prices (base + best via pricing service) + media metadata + custom-field values + merged attribute schema. Media bytes flow through the attachment bridge (use `catalog.get_product_media` then the bridge). Returns `{ found: false }` on miss or cross-tenant access.',\n inputSchema: getProductBundleInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductBundleInput.parse(rawInput)\n const em = resolveEm(ctx)\n return buildProductBundle(em, ctx, tenantId, input.productId)\n },\n}\n\nconst listSelectedProductsInput = z.object({\n productIds: z\n .array(z.string().uuid())\n .min(1)\n .max(50)\n .describe('1..50 catalog product ids (UUIDs). Duplicates are collapsed; cross-tenant ids drop into `missingIds`.'),\n})\n\nconst listSelectedProductsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_selected_products',\n displayName: 'List selected products (bundles)',\n description:\n 'Bulk variant of `catalog.get_product_bundle`: resolves 1..50 product ids into tenant-scoped bundle aggregates. Missing / cross-tenant ids are returned in `missingIds` (not as an error) so selection-aware agents can render partial results.',\n inputSchema: listSelectedProductsInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listSelectedProductsInput.parse(rawInput)\n const em = resolveEm(ctx)\n const unique = Array.from(new Set(input.productIds))\n const resolved = await Promise.all(\n unique.map(async (productId) => ({ productId, result: await buildProductBundle(em, ctx, tenantId, productId) })),\n )\n const items: ProductBundle[] = []\n const missingIds: string[] = []\n for (const entry of resolved) {\n if (entry.result.found) {\n items.push(entry.result)\n } else {\n missingIds.push(entry.productId)\n logger.warn('catalog.list_selected_products product not in scope', { productId: entry.productId })\n }\n }\n return { items, missingIds }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_product_media */\n/* -------------------------------------------------------------------------- */\n\nconst getProductMediaInput = z.object({\n productId: z.string().uuid().describe('Catalog product id (UUID).'),\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n})\n\nconst getProductMediaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_product_media',\n displayName: 'Get product media (with attachment IDs)',\n description:\n 'List media records attached to a product with metadata (filename, mime, size, sort order) and the `attachmentId` string for each row. Does NOT invoke the attachment bridge \u2014 the Step 3.7 runtime bridge converts attachment ids into model file parts when the chat/object helper invokes this tool in-context. Returns `{ items, total, limit, offset }`.',\n inputSchema: getProductMediaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getProductMediaInput.parse(rawInput)\n const em = resolveEm(ctx)\n const limit = input.limit ?? 50\n const offset = input.offset ?? 0\n const where: Record<string, unknown> = {\n tenantId,\n entityId: E.catalog.catalog_product,\n recordId: input.productId,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const [rows, total] = await Promise.all([\n findWithDecryption<Attachment>(\n em,\n Attachment,\n where as any,\n { limit, offset, orderBy: { createdAt: 'asc' } as any } as any,\n buildScope(ctx, tenantId),\n ),\n em.count(Attachment, where as any),\n ])\n const filtered = rows.filter((row) => (row.tenantId ?? null) === tenantId)\n return {\n items: filtered.map((row) => ({\n mediaId: row.id,\n productId: input.productId,\n attachmentId: row.id,\n fileName: row.fileName,\n mediaType: row.mimeType,\n size: row.fileSize,\n altText: null,\n sortOrder: 0,\n })),\n total,\n limit,\n offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.get_attribute_schema / catalog.get_category_brief */\n/* -------------------------------------------------------------------------- */\n\nconst getAttributeSchemaInput = z.object({\n productId: z.string().uuid().optional().describe('Narrow schema resolution to this product (scope: `product`).'),\n categoryId: z.string().uuid().optional().describe('Narrow schema resolution to this category (scope: `category`).'),\n})\n\nconst getAttributeSchemaTool: CatalogAiToolDefinition = {\n name: 'catalog.get_attribute_schema',\n displayName: 'Get attribute schema',\n description:\n 'Resolve the merged custom-field attribute schema for catalog products. When both `productId` and `categoryId` are absent, returns module-level fields only. Reuses the shared `loadCustomFieldDefinitionIndex` resolver so tenant + organization scoping rules stay consistent with CrudForm / admin routes.',\n inputSchema: getAttributeSchemaInput,\n requiredFeatures: ['catalog.products.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getAttributeSchemaInput.parse(rawInput)\n return resolveAttributeSchema(ctx, tenantId, input.productId, input.categoryId)\n },\n}\n\nconst getCategoryBriefInput = z.object({\n categoryId: z.string().uuid().describe('Category id (UUID).'),\n})\n\nconst getCategoryBriefTool: CatalogAiToolDefinition = {\n name: 'catalog.get_category_brief',\n displayName: 'Get category brief',\n description:\n 'Category name, full tree path, description, and merged attribute schema (same resolver as `catalog.get_attribute_schema` with `categoryId`). Returns `{ found: false }` on miss / cross-tenant.',\n inputSchema: getCategoryBriefInput,\n requiredFeatures: ['catalog.categories.view'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = getCategoryBriefInput.parse(rawInput)\n const em = resolveEm(ctx)\n const where: Record<string, unknown> = {\n id: input.categoryId,\n tenantId,\n deletedAt: null,\n }\n if (ctx.organizationId) where.organizationId = ctx.organizationId\n const category = await findOneWithDecryption<CatalogProductCategory>(\n em,\n CatalogProductCategory,\n where as any,\n undefined,\n buildScope(ctx, tenantId),\n )\n if (!category || category.tenantId !== tenantId) {\n return { found: false as const, categoryId: input.categoryId }\n }\n const attributeSchema = await resolveAttributeSchema(ctx, tenantId, undefined, category.id)\n return {\n found: true as const,\n id: category.id,\n name: category.name,\n path: category.treePath ?? null,\n description: category.description ?? null,\n attributeSchema,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* catalog.list_price_kinds */\n/* -------------------------------------------------------------------------- */\n\nconst listPriceKindsInput = z\n .object({\n limit: z.number().int().min(1).max(100).optional().describe('Max rows (default 50, max 100).'),\n offset: z.number().int().min(0).optional().describe('Rows to skip (default 0).'),\n })\n .passthrough()\n\nconst listPriceKindsTool: CatalogAiToolDefinition = {\n name: 'catalog.list_price_kinds',\n displayName: 'List price kinds',\n description:\n 'Enumerate tenant price kinds for the D18 merchandising assistant. Shares the tenant-scoped query path with `catalog.list_price_kinds_base`; the two tools differ only in description/framing (the base tool is the low-level settings enumerator, this one is the spec-named D18 surface).',\n inputSchema: listPriceKindsInput,\n requiredFeatures: ['catalog.settings.manage'],\n tags: ['read', 'catalog', 'merchandising'],\n isMutation: false,\n handler: async (rawInput, ctx) => {\n const { tenantId } = assertTenantScope(ctx)\n const input = listPriceKindsInput.parse(rawInput)\n const base = await listPriceKindsCore(ctx, input, tenantId)\n return {\n items: base.items.map((row) => ({\n id: row.id,\n code: row.code,\n name: row.title,\n scope: row.organizationId ? ('organization' as const) : ('tenant' as const),\n currency: row.currencyCode,\n appliesTo: row.isPromotion ? ('promotion' as const) : ('regular' as const),\n })),\n total: base.total,\n limit: base.limit,\n offset: base.offset,\n }\n },\n}\n\n/* -------------------------------------------------------------------------- */\n/* Export */\n/* -------------------------------------------------------------------------- */\n\nexport const merchandisingAiTools: CatalogAiToolDefinition[] = [\n searchProductsTool,\n getProductBundleTool,\n listSelectedProductsTool,\n getProductMediaTool,\n getAttributeSchemaTool,\n getCategoryBriefTool,\n listPriceKindsTool,\n]\n\nexport default merchandisingAiTools\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["items", "total"]
|
|
7
7
|
}
|
|
@@ -3,6 +3,8 @@ import { z } from "zod";
|
|
|
3
3
|
import { Dictionary, DictionaryEntry } from "@open-mercato/core/modules/dictionaries/data/entities";
|
|
4
4
|
import { resolveDictionariesRouteContext } from "@open-mercato/core/modules/dictionaries/api/context";
|
|
5
5
|
import { CrudHttpError, isCrudHttpError } from "@open-mercato/shared/lib/crud/errors";
|
|
6
|
+
import { createLogger } from "@open-mercato/shared/lib/logger";
|
|
7
|
+
const logger = createLogger("catalog");
|
|
6
8
|
const KEY_ALIASES = {
|
|
7
9
|
currency: ["currency", "currencies"],
|
|
8
10
|
unit: ["unit", "units", "measurement_units"]
|
|
@@ -55,7 +57,7 @@ async function GET(req, ctx) {
|
|
|
55
57
|
if (isCrudHttpError(err)) {
|
|
56
58
|
return NextResponse.json(err.body, { status: err.status });
|
|
57
59
|
}
|
|
58
|
-
|
|
60
|
+
logger.error("catalog.dictionaries.GET Unexpected error", { err });
|
|
59
61
|
return NextResponse.json({ error: "Failed to load dictionary." }, { status: 500 });
|
|
60
62
|
}
|
|
61
63
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../../../src/modules/catalog/api/dictionaries/%5Bkey%5D/route.ts"],
|
|
4
|
-
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\n\nconst KEY_ALIASES: Record<string, string[]> = {\n currency: ['currency', 'currencies'],\n unit: ['unit', 'units', 'measurement_units'],\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['catalog.products.manage'] },\n}\n\nexport async function GET(\n req: Request,\n ctx: { params?: { key?: string } },\n): Promise<Response> {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const keyParam = (ctx.params?.key ?? '').toLowerCase().trim()\n if (!keyParam) {\n throw new CrudHttpError(400, { error: 'Dictionary key is required.' })\n }\n const keys = KEY_ALIASES[keyParam] ?? [keyParam]\n const dictionaries = await context.em.find(\n Dictionary,\n {\n tenantId: context.tenantId,\n key: { $in: keys },\n deletedAt: null,\n isActive: true,\n },\n { orderBy: { organizationId: 'asc', createdAt: 'asc' } },\n )\n const dictionary =\n dictionaries.find((entry) => entry.organizationId === context.organizationId) ??\n dictionaries[0] ??\n null\n if (!dictionary) {\n return NextResponse.json({ error: 'Dictionary not found.' }, { status: 404 })\n }\n const entries = await context.em.find(\n DictionaryEntry,\n {\n dictionary,\n organizationId: dictionary.organizationId,\n tenantId: dictionary.tenantId,\n },\n { orderBy: { label: 'asc' } },\n )\n return NextResponse.json({\n id: dictionary.id,\n entries: entries.map((entry) => ({\n id: entry.id,\n value: entry.value,\n label: entry.label,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n })),\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n
|
|
5
|
-
"mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,YAAY,uBAAuB;AAC5C,SAAS,uCAAuC;AAChD,SAAS,eAAe,uBAAuB;
|
|
4
|
+
"sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { Dictionary, DictionaryEntry } from '@open-mercato/core/modules/dictionaries/data/entities'\nimport { resolveDictionariesRouteContext } from '@open-mercato/core/modules/dictionaries/api/context'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('catalog')\n\nconst KEY_ALIASES: Record<string, string[]> = {\n currency: ['currency', 'currencies'],\n unit: ['unit', 'units', 'measurement_units'],\n}\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['catalog.products.manage'] },\n}\n\nexport async function GET(\n req: Request,\n ctx: { params?: { key?: string } },\n): Promise<Response> {\n try {\n const context = await resolveDictionariesRouteContext(req)\n const keyParam = (ctx.params?.key ?? '').toLowerCase().trim()\n if (!keyParam) {\n throw new CrudHttpError(400, { error: 'Dictionary key is required.' })\n }\n const keys = KEY_ALIASES[keyParam] ?? [keyParam]\n const dictionaries = await context.em.find(\n Dictionary,\n {\n tenantId: context.tenantId,\n key: { $in: keys },\n deletedAt: null,\n isActive: true,\n },\n { orderBy: { organizationId: 'asc', createdAt: 'asc' } },\n )\n const dictionary =\n dictionaries.find((entry) => entry.organizationId === context.organizationId) ??\n dictionaries[0] ??\n null\n if (!dictionary) {\n return NextResponse.json({ error: 'Dictionary not found.' }, { status: 404 })\n }\n const entries = await context.em.find(\n DictionaryEntry,\n {\n dictionary,\n organizationId: dictionary.organizationId,\n tenantId: dictionary.tenantId,\n },\n { orderBy: { label: 'asc' } },\n )\n return NextResponse.json({\n id: dictionary.id,\n entries: entries.map((entry) => ({\n id: entry.id,\n value: entry.value,\n label: entry.label,\n color: entry.color ?? null,\n icon: entry.icon ?? null,\n })),\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n logger.error('catalog.dictionaries.GET Unexpected error', { err })\n return NextResponse.json({ error: 'Failed to load dictionary.' }, { status: 500 })\n }\n}\n\nconst dictionaryEntrySchema = z.object({\n id: z.string().uuid(),\n value: z.string(),\n label: z.string(),\n color: z.string().nullable(),\n icon: z.string().nullable(),\n})\n\nconst dictionaryResponseSchema = z.object({\n id: z.string().uuid(),\n entries: z.array(dictionaryEntrySchema),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Catalog',\n summary: 'Catalog Dictionary lookup',\n methods: {\n GET: {\n summary: 'Get dictionary entries by key',\n description: 'Returns dictionary entries for a specific key (e.g., currency, unit).',\n responses: [\n {\n status: 200,\n description: 'Dictionary entries',\n schema: dictionaryResponseSchema,\n },\n ],\n },\n },\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|